# How to create an Analytics Provider

WARNING

  • Every Analytics Provider must be defined in its own repository, including the custom ones.
  • Before starting the development, ask the BERG team to create the repository.
  • The new Analytics providers are not compatible with legacy tenants (non-XPlib). When possible, migrate these tenants to XPLib before creating a legacy custom analytics provider for them.

# Preliminary step

Make sure the Analytics Provider hasn't been created yet by looking for analytics-providers-[providerName] in the MarfeelReleases repository (opens new window).

# Scaffolding

  1. Ask BERG team to create a new repository for the provider. You can do it by posting a new message to the BERG discussion board (opens new window) on GitHub. Give them as many details as you can:

    • Name of the provider (If it's compatible with AMP it should have the same name as AMP. Check the list here (opens new window))
    • List of tenants that are going to use it
    • Is the provider compatible with AMP?
    • If it's a custom provider, what makes it necessary? Is there already a core version?
  2. Once created, clone the repository to your local environment.

git clone git@github.com:Marfeel/analytics-providers-NEW_ANALYTICS_PROVIDER.git
cd analytics-providers-NEW_ANALYTICS_PROVIDER/

It already contains a CODEOWNERS file and a .github folder with the files needed to build and deploy the Analytics Provider package. All the implementation files are also already scaffolded.

  1. Install the required dependencies by executing the following command from the provider's folder:
npm install

TIP

Make sure dependencies are up to date by runnning npx npm-check-updates -u before npm install. This command will automatically update each dependency to its latest version.

# Implementation

Analytics Providers are implemented as a Typescript (opens new window) class that exposes some predefined methods.

The entry point of the analytics implementation can be found at src/index.ts and it must export a Typescript class. Each class has a constructor, and any number of action methods, depending on its schema.

# constructor

Each Analytics Provider is initialized once per column: every navigation event (swipe, tap...) creates a different class instance.

The constructor receives two parameters:

  • configuration is a JSON object with the configuration specified for the provider in analytics.json. For example:
{
  "vars": {
    "account": "XXXXX"
  },
  "touchVars": {
    "someVar": 1234
  },
  "triggers": {
  }
}
  • marfeelEnvironment contains several utilities that can be used to install scripts and retrieve context information from Marfeel. They are injected into the provider to avoid exposing Marfeel source code and to keep the provider's implementation decoupled from Marfeel core.

The constructor must create a Promise field $initialized which resolves once the provider is initialized.

export default class Provider {
  accountId: string;
  $initialized: Promise<void>;

  constructor({ vars: { accountId } }: ProviderConfig) {
    this.accountId = accountId;
    this.$initialized = Promise.resolve();
  }
}

# Action methods

Action methods are defined in the triggers configuration of a provider's schema.

For example, with the following schema:










 
 
 
 
 
 
 







{
  "trigger": {
    "$id": "#trigger",
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
      "on": {
        "type": "string"
      },
      "action": {
        "enum": [
          "pageview",
          "event",
          "social"
        ]
      },
      "vars": {
        "$ref": "#/definitions/triggerVars"
      }
    }
  }
}

Which contains the actions:

  • pageview
  • event
  • social

The Typescript class must implement those:



 
 
 


export default class AnalyticsProvider {
  constructor() {}
  pageview() {}
  event() {}
  social() {}
}

Acid tests guarantee that no provider can be published without all its action methods.

All action methods receive the same parameters as the constructor, but with all variables updated.

  • configuration is a JSON object with the configuration specified for the provider in analytics.json. All run-time variables are replaced when the method is called.

For example with the following variables:






 




{
  "vars": {
    "account": "XXXXX"
  },
  "touchVars": {
    "location": "#{env.location.href}",
    "someOtherVariable": "Some Value here"
  }
}

If the current location.href is "https://someLocationHere.io", the action receives:






 




{
  "vars": {
    "account": "XXXXX"
  },
  "touchVars": {
    "location": "https://someLocationHere.io",
    "someOtherVariable": "Some Value here"
  }
}
  • marfeelEnvironment contains several utilities that can be used to install scripts and retrieve context information from Marfeel.

Finally, all action methods must return a Promise resolution:

async pageview(configuration: ConfigurationType, marfeelEnvironment: Marfeel): Promise<void> {
    await this.$initialized;

    // do something here...

    return Promise.resolve();
}

# External dependencies

Analytics Providers can have external dependencies imported as Node packages in the provider's package.json:

"dependencies": {
  "@marfeel/touch-utils": "^1.0.7-snapshot.21.0"
}

Marfeel offers the @marfeel/touch-utils (opens new window) package with common utilities that can be used from any provider.

It contains helpers for arrays, scripts, and URLs among others, and should cover the needs of most of the providers. Check the source code (opens new window) for more information.

Bundle size

Dependencies are not shared among providers: every Analytics Provider configured in a tenant comes with its dependencies compiled in its production bundle.

Be mindful of dependencies, to keep the production bundle as light as possible.

# Unit tests

An Analytics Provider must contain unit tests to be deployed. Providers' unit tests must be written using JEST (opens new window) and should cover the public API of the analytics provider.

The test for the provider's implementation entry point is mandatory and can be found at src/index.test.ts, but you can create as many test files as necessary:

  • src/helpers.test.ts
  • src/utils.test.ts
  • ...

See googleanalytics unit tests (opens new window) for an example.

Run the test suite by executing the following command at the provider's root directory:

npm run test

# JSON Schema

In addition to its implementation and unit tests, every provider must contain a JSON schema (opens new window) The schema must be at schema/index.json and describes which properties and values can be used to configure the analytics provider in analytics.json files.

See googleanalytics schema (opens new window) for an example.

Validate the analytics provider's schema with at least one valid and one wrong test:

  • schema/tests/valid/index.test.json
{
    "vars": {
        "account": "UA-32332-5"
    },
    "touchVars": {
        "environment": "marfeel_browser"
    }
}
  • schema/tests/wrong/index.test.json
{
    "vars": {
        "account": "UA5"
    }
}

Each folder, valid and wrong, can contain as many *.test.json files as necessary.

# Extractor

With a new analytics provider, an extractor implementation has to be created as well to provide the support of this newly analytics provider to MarfeelAlfred (opens new window)

  • extractor/index.ts is the entry point of the extractor.
  • extractor/index.test.ts contains the unit tests of the extractor

TIP

For more information about extractors click here

# Debugging on playground

Analytics Providers have a local playground that can be used to debug a provider without having to integrate it into a tenant.

Run the following command from the Analytics Provider's root directory to open the playground in your local environment:

npm run start

# Debug on XPlib tenants

These are the steps to follow to integrate a local version of an Analytics Provider into an XPlib tenant:

  1. Generate a development bundle of the Analytics Provider by running the following command from its root directory:
npm run build:dev
  1. Clone the Media Group repository and install its dependencies by executing the following:
glue clone MEDIAGROUP
cd MEDIAGROUP
npm install
  1. Add the analytics provider to the dependencies list of the Media Group's package.json:
"dependencies": {
  "@marfeel/analytics-providers-customProviderIWant": "^1.0.0"
}
  1. Mark the analytics provider as a linkable dependency by running the npm link (opens new window) command from its root directory:
npm link
  1. Link the local version of the analytics provider with the tenant by executing the following command from the Media Group root directory:
npm link @marfeel/analytics-providers-customProviderIWant
  1. Compile the tenant by running the following command from the tenant's root:
npm run build

# Acid tests

All Analytics Providers come with an acid-test command that can be used to check that everything is in order before deploying the provider. Since this command uses the production bundle to perform some tests, execute the build command beforehand:

npm run build
npm run acid-tests

In addition to the providers' standard acid tests, it checks the following:

  • The Provider defines the $initalized promise.

# Deployment

  1. If the Analytics Provider has already been deployed before, make sure you've increased its version in the package.json file, following the SemVer spec (opens new window). Otherwise, the build fails, blocking deployment.
  2. Open a Pull Request and merge your commit to the master branch.
  3. The build process will start immediately after the merge. You can follow the deployment status at the Actions tab of the Analytics Provider repository.
  4. If everything goes well, the Analytics Provider package will be published in the Marfeel releases repository (opens new window) within 4 minutes approx.

TIP

If the build is broken, review the logs from GitHub and fix all the failures before trying again.

  1. The Analytics Provider's playground is published to GitHub Pages, under the following URL: https://marfeel.github.io/analytics-providers/[analytic-provider-name]. For example googleanalytics playground at GitHub pages (opens new window).