Mediatool Integrations
Mediatool is a SaaS platform where our clients collaborate over planning of
their advertisements. As part of our service we provide the possibility for our
clients to connect to their different marketing platforms such as Facebook
Marketing, Google Ads etc in order to fetch the results of their marketing
activities so that they can compare those results to their initial planning.
The purpose of this project is to add connections to new services that we do not
currently have.
Overview
When a user wants to sync data from an API in Mediatool they create what we
call a connection
in our database. The process goes throught the steps below.
- The user connects to the remote service, usually via Oauth2. At a successful
authentication a connection is created.
- On a successful authentication the users profile is also fetched and stored
on the connection.
- The accounts for the user is then fetched. For Google Analytics this would
be the websites the user has access to, for Facebook Marketing its the
marketing accounts etc.
- The user selects which account(s) to fetch data from.
- The available metrics and dimensions for the provider is fetched.
- The user selects which measures and dimensions to fetch and how to map them
to their measures and dimensions in Mediatool.
- The user saves the connection and at this point and the actual data is
fetched based on the settings done in the previous steps.
How to add a connection to a new api
Setup oauth connection flow
First make sure an app is created at the provider and that you have the client
id and the client secret. Also make sure that the correct callback uri have
been configured for that app. These tasks should be taken care of by Mediatool.
- Open the file
./lib/api/api-public-conf.js
and add the configuration to it
for the new API. The configuration should contain clientId, scope, access
token URL and redirect url. If the provider for the api you are integrating
to is already in this file, you can re-use some properties like client id
and secret. You can see several examples of this in the file. - Create a "connection"-file in
./web/api/[apiKeyName].js
. These files
contain the same generic functions an provide you with the ability to
customize the behavior for making the Oauth request and parsing the
response. As in step 1, many APIs have the same provider and can therefore
share configurations with other APIs. In that case, create a configuration
by merging common props with the ones specific for the API you are working
with. - Add the new connection to the list in the file
web/available-apis.js
. Make
sure to set the properties { released: false, completed: false }
. - Test the flow by starting the demo server with
yarn demo
, and go to the
MTAvailableIntegrationsList component. If there is no icon for the API,
ignore this, Mediatool will fix that part.
Fetch and normalize the user
This step is only needed if there are no existing integrations for the provider.
Create a function in lib/api/fetch-[provider]-account-info
. The function
should fetch the user that authenticated the connection.
Create a function in lib/api/normalize-[provider]-account-info
. The function
should take the result of the fetch function and return an object with the format:
{
imageURL: [String],
displayName: [String],
}
Fetch and normalize "profiles" available from the API
Create a function in lib/api/fetch-[provider]-[profiles/addacounts/etc]
. The function
should fetch the accounts that the user has access to.
Create a function in lib/api/normalize-[provider]-[profiles/addacounts/etc]
.
The function should take the result of the fetch function and return an array
of objects with a property suitable for the api, in the case of Google
Analytics it is availableViews
and for Facebook Marketing it is
availableAdAccounts
. The property should map to a list of objects that has
the property name
, and other API specific properties needed to fetch the
data.
Fetch and normalize fields available from the API
Fields are what populate the dimension and metrics selectors. They represent
what can be queried from the API. How to get the available fields from an API
depends on how the API provides them but the implementation for getting them to
the user should be in the getFields
function of the APIs connection
object.
Fields should be returned as an object with two list properties, dimensions
and metrics
. Each list should contain objects with properties key
, name
and description. If description is not available, it can be left out.
Below are different methods of getting the fields:
-
Some providers have open routes where a user can request them (e.g. analytics). If this is the case, getFields
can be implemented as a request using the superagent
module. The response then needs to be proccesed so that the returned
fields are normalized into the format described above. See web/api/analytics.js
for an example.
-
Some providers only allow this information to be fetched with an authorized request. In that case, getFields
will receive
an object as its only parameter with a function called fetchFields
that can should be called. This function is provided through
our webapp and makes a request to our backend where the users refresh token is used to make the authorized request needed.
See examples in web/api/facebook-marketing.js
and web/api/dcm.js
.
-
Some providers do not give any API endpoint at all for fetching fields. In this case the data needs to be found and stored locally
in some way, probably a JSON or js file. The getFields
function can then return a Future of the stored data. See web/api/google-ads.js
for an example.
Create profile selector UI component used in the API form
Create a file in web/[apiName]-account-selector.js
. This will be the React component that will let users select which profiles / ad accounts etc. they want to fetch data from. For most cases, you can use the "createAccountSelector" function which wraps the behaviour and requires only some configuration parameters. For an example, see web/google-analytics-account-selector.js
. In special cases, you will need to write the component from scratch and include the custom behaviour you want to achieve, however like mentionned this is rarely the case.
put it all together in getAPIFunctions
Add a new registration in lib/api/get-api-functions.js
where the key is the
key of your API and the values are the functions you have created in the steps
above.