Building a Sell app using OAuth 2.0 - Part 5: Getting data from the Mailchimp API
At this stage of the tutorial series, you should have registered your app with Mailchimp and have OAuth set up in your app. In Mailchimp, you created at least one audience with a subscriber. The subscriber's email address should match the email of your person contact in Zendesk Sell.
In this tutorial, you'll add extra features to your app. The app will make authorized requests to the Mailchimp API to get audiences and then display them in a list.
This tutorial covers the following steps:
- Getting a person email address from Sell
- Working with authorized requests
- Retrieving a server prefix from Mailchimp
- Fetching audiences from Mailchimp
This tutorial is part of a series on building a Zendesk app:
- Part 1: Laying the groundwork
- Part 2: Installing the app in Zendesk Sell
- Part 3: OAuth 2.0 setup )
- Part 4: Creating audiences in Mailchimp
- Part 5: Getting data from the Mailchimp API - YOU ARE HERE
Getting a person email address from Sell
You'll be retrieving audiences that a person contact subscribes to using Mailchimp lists endpoint.
GET https://server.api.mailchimp.com/3.0/lists?email=<EMAIL>
First, you'll get a person's email address from Sell and pass it as a query parameter. You could use the useSellContactEmail hook, but since the app supports one location (person_card
), a simplified version can be created.
The useClientGet hook provided by the Sell ZAF app toolbox package will be used. It uses the Apps framework client.get() method to get data asynchronously for a given path.
The useClientGet
hook returns an object with data
, error
, and feedback
properties which have different values depending on the state of a request. The structure works well with the <ResponseHandler/>
component mentioned in Reviewing the template files in part 2 of the tutorial series.
If a request isn’t completed yet, the hook returns:
{data: null, error: null, feedback: {status: "loading"}}
If the request is completed successfully, it returns response data and updates a feedback status:
{
data: {
firstName: "Ewen",
lastName: "Marin",
email: "[email protected]",
name: "Ewen Marin",
....
}
error: null
feedback: {status: "success"}
}
Get the email address from Sell
-
Restart the app and the ZCLI server to be able to test it locally. For more information, refer back to Developing the app locally in the Part 2: Installing the app in Zendesk Sell.
Note: In Part 3: OAuth 2.0 setup , you added an access token as an OAuth parameter to the manifest.json file. If you are requested to type it while running the ZCLI server, ignore it and click Enter. It won’t affect your app.
In the Apps Sell API for person_card location, you can access the contact object. One of the contact object properties is an email address you retrieve with
client.get('contact.email')
. Try this out using the hook. -
In the EntryView.tsx file (src/EntryView.tsx), replace the
<EntryView/>
component with the following:export const EntryView = () => {
useClientHeight(230)
const contactEmailResponse = useClientGet('contact.email')
return (
<Grid gutters={false} className={css.App}>
<Row>
<ResponseHandler
response={contactEmailResponse}
loadingView={<Loader />}
errorView={<div>Something went wrong!</div>}
emptyView={<div>There's nothing to see yet.</div>}
>
{([email]: [string]) => (
<Col textAlign="center">
<Header contactEmail={email} />
<ContentView />
</Col>
)}
</ResponseHandler>
</Row>
</Grid>
)
}
-
Save the file and refresh the app in your web browser. The email should be still there.
Working with authorized requests
Since you’ll be calling the Mailchimp API a few times, this logic will be extracted to a new helper method. We can leverage the existing helper useClientRequest and pass extra options to make an authorized request.
useClientRequest
makes an HTTP request using the Apps framework. It uses the client.request() method which is the recommended method to make requests which are proxied to interpolate OAuth access tokens. It takes the URL of the resource as the first argument in the request. If you need to send additional parameters with the request, you can define them as a second argument.
The structure of the response is the same as for the useClientGet hook that you're using in the Getting a person email address from Sell.
Create an authorized request
-
Create a hooks folder in the src directory.
-
Create a useClientAuthorizedRequest.tsx file inside it. Add the following code to the file and save it:
import {useClientRequest, Response} from '@zendesk/sell-zaf-app-toolbox'
/* parameter of type 'oauth' should exist in the manifest.json file
{
"name" : "access_token",
"type": "oauth"
}
*/
export default function useClientAuthorizedRequest<T extends {}>(
url: string,
options: object = {},
dependencies?: any[],
): Response<T> {
const response = useClientRequest<T>(
url,
{
secure: true,
headers: {authorization: 'OAuth {{setting.access_token}}'},
dataType: 'json',
contentType: 'application/json',
...options,
},
dependencies,
)
return response
}
As discussed in Part 3: OAuth 2.0 setup in the "Configuring an app in the manifest" section, you have to include the following properties to a call request to a third-party service - secure: true
and authorization header set to OAuth {{setting.access_token}}
.
With this setup, you can make your first request to the Mailchimp API.
Retrieving a server prefix from Mailchimp
The created useClientAuthorizedRequest
hook will be used to get information about the user who granted OAuth access. Specifically, you get the user's server prefix which is needed to make calls to the Mailchimp API on the user’s behalf. This prefix will change from user to user. To retrieve the server prefix, a GET request is sent to Mailchimp's metadata endpoint:
https://login.mailchimp.com/oauth2/metadata
Retrieve server prefix from Mailchimp
-
Create another hook in the useMailchimpMetadata.tsx file /src/hooks/ directory with the following content:
import {Response} from '@zendesk/sell-zaf-app-toolbox'
import useClientAuthorizedRequest from './useClientAuthorizedRequest'
import {LoginMetadata} from '../types'
export default function useMailchimpMetadata(): Response<LoginMetadata> {
return useClientAuthorizedRequest<LoginMetadata>(
'https://login.mailchimp.com/oauth2/metadata',
{},
[],
)
}
-
Since you're using TypeScript in the project, create a types.ts file in the src/ directory and add the following types so the hook is statically typed.
export interface Login {
email: string
avatar?: any
login_id: number
login_name: string
login_email: string
}
export interface MailchimpMeta {
dc: string
role: string
accountname: string
user_id: number
login: Login
login_url: string
api_endpoint: string
}
export interface LoginMetadata {
data?: MailchimpMeta
}
-
Call the
useMailchimpMetadata
hook from the<EntryView/>
component.// other imports
import useMailchimpMetadata from './hooks/useMailchimpMetadata'
export const EntryView = () => {
... // other hooks
const mailchimpMetadataResponse = useMailchimpMetadata() // server prefix request
return ...
}
-
Save the files.
-
Go to Sell and open the Developer Tools by using the one of the shortcuts depending on your browser.
- Google Chrome: Option + ⌘ + J (on macOS), or Shift + CTRL + J (on Windows/Linux)
- Firefox: Option + ⌘ + I (on macOS), or Shift + CTRL + I or F12 (on Windows/Linux)
-
Click the Network tab to observe requests.
-
Refresh the app. When the app is loaded, look for the Mailchimp metadata request in the Network tab. The request should succeed with a status 200 response.
-
Switch to the Preview tab to check the response. Look for the
api_endpoint
.
The server prefix will be used in the next section to fetch audiences.
Fetching audiences from Mailchimp
We have all the information required to fetch audiences for a given person.
Fetch audiences from Mailchimp
-
Create useAudiences.tsx file in the /src/hooks/ directory and add the following code:
import {Response} from '@zendesk/sell-zaf-app-toolbox'
import useClientAuthorizedRequest from './useClientAuthorizedRequest'
import {MailchimpLists} from '../types'
const useAudiences = ({
email,
apiEndpoint,
}: {
email: string
apiEndpoint: string
}): Response<MailchimpLists> => {
const url = `${apiEndpoint}/3.0/lists?email=${email}&sort_field=date_created`
return useClientAuthorizedRequest<MailchimpLists>(url, {}, [])
}
export default useAudiences
The
useAudiences
hook takesemail
andapiEndpoint
(server prefix) which are required to compose the correct lists url. -
Add the following types to the types.ts file:
export interface Constraints {
may_create: boolean
max_instances: number
current_total_instances: number
}
export interface ListStats {
member_count: number
unsubscribe_count: number
cleaned_count: number
member_count_since_send: number
unsubscribe_count_since_send: number
cleaned_count_since_send: number
campaign_count: number
campaign_last_sent: string
merge_field_count: number
avg_sub_rate: number
avg_unsub_rate: number
target_sub_rate: number
open_rate: number
click_rate: number
last_sub_date: any
last_unsub_date: string
}
export interface Contact {
company: string
address1: string
address2: string
city: string
state: string
zip: string
country: string
phone: string
}
export interface CampaignDefaults {
from_name: string
from_email: string
subject: string
language: string
}
export interface Link {
rel: string
href: string
method: string
targetSchema?: string
schema?: string
}
export interface List {
id: string
web_id: number
name: string
contact: Contact
permission_reminder: string
use_archive_bar: boolean
campaign_defaults: CampaignDefaults
notify_on_subscribe: string
notify_on_unsubscribe: string
date_created: Date
list_rating: number
email_type_option: boolean
subscribe_url_short: string
subscribe_url_long: string
beamer_address: string
visibility: string
double_optin: boolean
has_welcome: boolean
marketing_permissions: boolean
modules: any[]
stats: ListStats
_links: Link[]
}
export interface MailchimpLists {
lists: List[]
total_items: number
constraints: Constraints
_links: Link[]
}
Next, you'll to call the
useAudiences
hook in a new<AudiencesList/>
component which is responsible for displaying audiences from the lists request. Take a look at JSON success example response to get familiar with the data structure.We'll be iterating audiences, showing an audience name (
audience.name
) and a the number of subscribers (audience.stats.member_count
) for a single item. -
Create
AudiencesList.tsx
file in src/components/ folder with the following content:import * as React from 'react'
import {Row} from '@zendeskgarden/react-grid'
import {ResponseHandler} from '@zendesk/sell-zaf-app-toolbox'
import ListStroke from '@zendeskgarden/svg-icons/src/16/clipboard-list-stroke.svg'
import useAudiences from '../hooks/useAudiences'
import {List} from '../types'
import css from '../App.css'
interface AudiencesListProps {
email: string
apiEndpoint: string
}
const Audience = ({audience}: any) => {
return (
<div className={css.audience}>
<div className={css.title}>{audience.name}</div>
<p>Member count: {audience.stats.member_count}</p>
</div>
)
}
const AudiencesList = ({email, apiEndpoint}: AudiencesListProps) => {
const audiencesResponse = useAudiences({email, apiEndpoint})
return (
<>
<Row justifyContent="center" className={css.header}>
<div className={css.icon}>
<ListStroke />
</div>
<div className={css.title}>Audiences</div>
</Row>
<Row>
<ResponseHandler
response={audiencesResponse}
errorView={<div>Something went wrong!</div>}
emptyView={<div>There's nothing to see yet.</div>}
>
{([{lists}]) => {
return lists.map((list: List, index: number) => (
<Audience key={index} audience={list} />
))
}}
</ResponseHandler>
</Row>
</>
)
}
export default AudiencesList
-
Add the following rules to the App.css file to style the audiences list:
.audience {
border-bottom: solid 1px var(--zd-color-grey-300);
overflow: hidden;
display: block;
width: 100%;
padding: var(--zd-spacing-sm);
}
.icon {
margin-right: var(--zd-spacing-xs);
}
-
Update the
<EntryView/>
component in src/EntryView.tsx to display the audiences list as well as deliver the subscriber email and your server prefix:// other imports
import {MailchimpMeta} from './types'
import AudiencesList from './components/AudiencesList'
export const EntryView = () => {
... // other hooks
const mailchimpMetadataResponse = useMailchimpMetadata()
return (
<Grid gutters={false} className={css.App}>
<Row>
<ResponseHandler
responses={[contactEmailResponse, mailchimpMetadataResponse]}
loadingView={<Loader />}
errorView={<div>Something went wrong!</div>}
emptyView={<div>There's nothing to see yet.</div>}
>
{([email, mailchimpMetadata]: [string, MailchimpMeta]) => (
<Col>
<AudiencesList
email={email}
apiEndpoint={mailchimpMetadata.api_endpoint}
/>
</Col>
)}
</ResponseHandler>
</Row>
</Grid>
)
}
The
<ResponseHandler/>
component takes an array of responses and display its children when all requests are completed. -
Go to your web browser and refresh the app in your Sell instance. You should see a list of audiences to which your person contact in Sell subscribes.
Congratulations! You’ve created the app with a working OAuth flow.
Next steps
The best way to learn is to plan, design, and build your own apps. You can use any of the following resources:
- Getting started - A quick overview of the Apps framework
- Setting up new apps - How to create starter files for your apps with ZCLI or use the ZAF React Skeleton for Sell Apps
- Using the Apps framework - A concise developer guide
- Deploying apps - Information on branding, uploading, and localizing your apps
- ZAF Client API - A reference of the ZAF JavaScript client
- ZAF Core API - A reference of core events, actions, and properties available in all locations in all products that support the Zendesk Apps framework
- ZAF Sell API - A reference of the app locations, properties, and events you can use in apps running in Zendesk Sell
- ZAF App Toolbox - A package which delivers a bunch of useful methods, hooks, and components to help you build React apps integrated with Zendesk Sell quicker and with less effort. It uses the Zendesk Apps framework (ZAF) Client .
You can also look for answers or ask questions in the Zendesk Apps framework (ZAF) community.