3.1.41 • Published 5 years ago

@fireflyxd/fm v3.1.41

Weekly downloads
-
License
ISC
Repository
github
Last release
5 years ago

FM2

Goals

fm provides a Promise-based module that can be easily integrated into browsers and React (or any npm controlled) apps. fm2 should behave identically to fm1 with events and callbacks.

Deployables

Fm maintains multiple deployables that are built independently of each other by CircleCI. It's important to note that the master branch is directly connected with building and deploying the client-facing version of fm-slim. See the webpack configuration for more details of the npm-published version versus the umd target.

Fm vs Fm-slim

The client-facing version of fm (fm-slim) does not need to export or use all of fm's modules. Clients would have no use for the lib module, for example. In order to keep fm-slim as small as possible, a slightly different start method is used.

Starting

By default, fm includes a base configuration that associates different modes with different endpoints and access tokens. This config can be seen in config/index.js. In general, this config is just the blueprint for how application configs should be handled. All configurations run through fm.start() should have the same basic structure, but the incoming configuration to fm.start() will override anything in the original configuration.

To get a good sense of how fm works, it's best to go through the start method in fm's main module to step through all of the init methods.

Mode

The mode configuration property is very important. It allows for switching between development (if it exists), staging and production behaviors.

Mode will default to production if certain criteria are not met:

  1. fm is being executed on localhost
  2. There is mode ?mode=production|test|development parameter set in the url.

Other parts of the stack may also depend on mode. This includes the main application as well as back end functionality.

Model

fm.model stores data and exposes set, get and save.

fm.set('test', 'test');
fm.set({
  some_property: 'things',
});
fm.get('test');
fm.save();

Data schemas

One of the bigger parts of moving data from "anywhere" to the back end is whitelisting specific properties as "important" versus "questions and answers."

There are a number of arrays of strings used throughout the application that are used to define schemas. These generally don't change, but the question and answers blacklist might. For example, the analytics utm tracking function uses a schema of:

export const UTM_SCHEMA = [
  'master_id',
  'engagement_id',
  'utm_source',
  'utm_medium',
  'utm_campaign',
  'utm_content',
  'mode',
];

The model module will filter the data out based on these keys in order to build the request. Other areas, such as the Pharma module's initializer method, will setup a blacklist that the model will perform a reverse filter against. Creating a large list of fields that are blacklisted from pharma data from multiple places in fm.

this.blacklist = [
  'engagement_id',
  ...SCHEMA_ENGAGMENT,
  ...BLACKLIST_QNA,
  ...PAGEVIEW_SCHEMA,
  ...UTM_SCHEMA,
  ...SchemaExtra,
  ...ALT_SCHEMA,
];

Filtering data

Fm's model module has two extremely useful methods for filtering data: filter and filterNot. Each of these takes an arbitrary number of strings and returns a matching object or its inverse respectively. These module methods are used extensively throughout fm.

Take for example the following data modal:

// fm.model.data...
{
  property_one: "one",
  property_two: "two",
  property_three: "three"
}

const filtered = fm.model.filter('property_one', 'proptery_three');
// filtered...
{
  property_one: "one",
  property_three: "three"
}

const notFiltered = fm.model.filterNot('property_three');
// notFiltered...
{
  property_one: "one",
  property_two: "two"
}

Events

Event handling is very straightforward. There are three methods of importance: on, off and emit. Events are handled only through callbacks.

fm.events.on('something', () => {
  // something event!
});

fm.events.emit('something');

Validation

Validation is built on top of validate.js. There are a series of default constraints for our most common fields that we store, but the validate function is now extensible with both constraints and transforms.

Basic validation:

fm.validate({
  email: 'test@test.com',
  phone: '(828) 222-2222',
  cc_number: '1234123412341234',
  cc_expiration: '2017',
  cc_cvv: '123',
  shipping_address: 'Test',
  billing_address: 'Test',
  shipping_city: 'Test',
  billing_city: 'Test',
  shipping_state: 'NC',
  billing_state: 'NC',
  first_name: 'Test',
  last_name: 'Test',
});

// or

fm.validate(fm.model.data);

Custom Constraints and Transforms

const constraints = {
  ski_boat: {
    length: {
      is: 8,
    },
    numericality: true,
  },
};

const transforms = {
  ski_boat: function(value) {
    return value.slice(0, 8);
  },
};

fm.set('ski_boat', '1234567890');
fm.validate(fm.model.data, constraints, transforms);

Analytics

The analytics module is composed of functions to identify a user's "system" and move information about page views, events, utm attributions and alt attributions to the back end. The majority of fm's schemas are defined and exported in this module.

Pharma

The pharma module functions similarly to analytics but it is intended to manage the engagement session as well as build out the question and answers object on update. The most useful function in this module (specifically for debugging) is fm.pharma.engagement(). Executing this function will return an object identical to the engagement passed to the backend. Additionally, it will execute all filters necessary based on fm's many analytics and pharma schemas.

Pixels

Pixels are collected through a GraphQL API and stored by their placements relative to events being dispatched. Because the current iteration of the frontend application does not rely on fm's event system, the pixels are created, pulled, and fired manually (outside of fm).

Router

The router module allows for custom routes to be defined and created based on the model's data. Because a client's media links generally take the user to a "brand site" we need a mechanism to pass engagement_id, master_id, and other pieces of identifying or attributing informatino off to the frontend application. The router config can be modified in fm.start by adjusting the router.linkTo property.

Automatic link updates

By default, when fm is started, the router.linkTo method will be executed. This will attempt to select all instances of <a ... class="fm-link"> on the page and replace their hrefs with a parameter injected version of the same link.

For example, pre fm.start:

<a href="http://www.fireflyxd.com/rtd" class="fm-link">Link</a>

Post fm.start:

<a
  href="http://www.fireflyxd.com/rtd?master_id=1234&engagement_id=1234&ga_id=1234.1234"
  class="fm-link"
  >Link</a
>

When fm is started on the frontend application, it will immediately read the url parameters present and "restore" the session.

Log

Logging is now a thing we handle through sentry, which means fm will Log things as specified, but user land logging can also be achieved through this.

The Log module is literally a wrapper around Raven.

Utilities

The utilities module provides access to tertiary services that may need to be accessible outside of the front end application. As of right now, this is only zip code querying. The states and rx functions are deprecated.

Debugging

Because any front end application using fm to manage its outgoing data layer needs to be able to debug certain pieces of that data, it's important to expose fm's namespace to the window. You may open the console and perform tasks like fm.model.data.engagement_id or fm.model.data.master_id to retreive information that can be useful in identifying a session on the backend.

fm-slim implementation

fm-slim is generally used by Firefly XD (FXD) clients to populate links in call-to-action buttons that link to an FXD path.

The fm-slim package picks out links from a page by looking for fm-link in the class of any <a> tags. It will replace the href inside any <a> tags with a link as configured in the fm.start() function (more on fm.start() below).

This is my <a class="fm-link" href="">call-to-action link</a>.

In order to populate a link inside a call-to-action button, the fm-slim package must be included in a page. The script is generally placed inside the body of a page. It must be placed after any links that the client expects to be populated.

<script
  type="text/javascript"
  src="https://fm.fireflyxd.io/3.1/fm-slim.js"
></script>

After the fm-slim package script, the fm.start() function must be called. For example, if the client were "nextgenmedicine," the fm.start() call would incorporate the client name by passing it into the program_id parameter.

alt_source

The alt_source parameter is used to specify the source of the link. For example, a client may use fm on multiple websites, and traffic coming into FXD would be tracked based on source by providing a different alt_source value on each website.

<script type="text/javascript">
  fm.start({
    current_path: 'brand',
    program_id: 'nextgenmedicine',
    program_version: 'client',
    page_name: window.location.pathname,
    alt_source: 'mainsite',
    router: {
      linkTo: 'https://nextgenmedicine.fireflyxd.com/',
    },
  });
</script>

With the configuration set in the fm.start() script above, any links in a page with a fm-link class will look something like the following:

This is my
<a
  class="fm-link"
  href="https://nextgenmedicine.fireflyxd.com/?alt_source=mainsite&engagement_id=GENERATED_FIREFLY_ENGAGEMENT_ID&master_id=GENERATED_FIREFLY_MASTER_ID"
  >call-to-action link</a
>.

Google Analytics

Optionally, the client may pass a Google Analytics ID into the fm-link as well. Before instantiating the fm-slim script and running the fm.start() function, instantiate Google Analytics. Replace UA-SOME-UA with your Google Anayltics UA.

<script type="text/javascript">
  (function(i, s, o, g, r, a, m) {
    i['GoogleAnalyticsObject'] = r;
    (i[r] =
      i[r] ||
      function() {
        (i[r].q = i[r].q || []).push(arguments);
      }),
      (i[r].l = 1 * new Date());
    (a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
    a.async = 1;
    a.src = g;
    m.parentNode.insertBefore(a, m);
  })(
    window,
    document,
    'script',
    'https://www.google-analytics.com/analytics.js',
    'ga'
  );

  ga('create', 'UA-SOME-UA', 'auto');
</script>

Then, before the fm.start() function, append the Google Analytics client ID to the fm router:

<script type="text/javascript">
  ga(function(tracker) {
    fm.router.append({ ga_id: tracker.get('clientId') });
  });

  fm.start({
    ...
  });
</script>

If the optional Google Analytics ID (ga_id) is set, the final link would look similar to the link below:

This is my
<a
  class="fm-link"
  href="https://nextgenmedicine.fireflyxd.com/?ga_id=SOME_GA_ID&alt_source=mainsite&engagement_id=GENERATED_FIREFLY_ENGAGEMENT_ID&master_id=GENERATED_FIREFLY_MASTER_ID&"
  >call-to-action link</a
>.

UTM

Optionally, the client may add Urchin Tracking Module (UTM) parameters to the fm.start() function.

If included in the fm.start() function, the UTM parameters will follow a user through the path. This functionality is useful for advertising campaigns, such as those from Facebook or Google.

fm tracks the following UTM parameters:

  utm_source
  utm_medium
  utm_campaign
  utm_content

Any number of the available UTM parameters may be included in the function. For example, the fm.start() function below includes a utm_content parameter.

<script type="text/javascript">
  fm.start({
    current_path: 'brand',
    program_id: 'nextgenmedicine',
    program_version: 'client',
    page_name: window.location.pathname,
    alt_source: 'mainsite',
    router: {
      linkTo: 'https://nextgenmedicine.fireflyxd.com/',
    },
    utm_content: 'some UTM content',
  });
</script>

Putting it all together

A basic page that includes fm-slim, Google Analytics and a UTM parameter would be similar to the following:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>NextGenMedicine</title>
  </head>
  <body>
    <a class="fm-link" href="">Path link</a>

    <script type="text/javascript">
      (function(i, s, o, g, r, a, m) {
        i['GoogleAnalyticsObject'] = r;
        (i[r] =
          i[r] ||
          function() {
            (i[r].q = i[r].q || []).push(arguments);
          }),
          (i[r].l = 1 * new Date());
        (a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
        a.async = 1;
        a.src = g;
        m.parentNode.insertBefore(a, m);
      })(
        window,
        document,
        'script',
        'https://www.google-analytics.com/analytics.js',
        'ga'
      );

      ga('create', 'UA-SOME-UA', 'auto');
    </script>

    <script
      type="text/javascript"
      src="https://fm.fireflyxd.io/3.1/fm-slim.js"
    ></script>

    <script type="text/javascript">
      ga(function(tracker) {
        fm.router.append({ ga_id: tracker.get('clientId') });
      });

      fm.start({
        current_path: 'brand',
        program_id: 'nextgenmedicine',
        program_version: 'client',
        page_name: window.location.pathname,
        alt_source: 'mainsite',
        router: {
          linkTo: 'https://nextgenmedicine.fireflyxd.com/',
        },
        utm_content: 'some UTM content',
      });
    </script>
  </body>
</html>
3.1.41

5 years ago

3.1.40

5 years ago

3.1.39

5 years ago

3.1.38

5 years ago

3.1.37

5 years ago

3.1.36

5 years ago

3.1.35

5 years ago

3.1.34

5 years ago

3.1.33

5 years ago

3.1.32

5 years ago

3.1.31

5 years ago

3.1.30

5 years ago