Manual Reference Source Test

src/modules/identity-eventbus.js

import Observable from './observable';

/**
 * Identity event bus
 *
 * Allows consumers to subscribe to generic events that may be interpreted to trigger specific
 * calls. Events are dispatched when the {@link IdentityEventbus#track} method is called by implementors
 * of the SDK.
 *
 * @example
 * const analytics = new IdentityEventbus({name: 'analytics'});
 * const unsubscribe = analytics.subscribe((event) => {
 *   { eventType = 'track', eventName, timestamp, properties = {} } = event;
 *   // interpret and send to analytics platform
 * });
 *
 * // to stop observing at a later time:
 * unsubscribe();
 *
 * // to dispatch an analytics event (by SDK implementors):
 * analytics.track('page_view', { foo: 'bar' });
 *
 */
class IdentityEventbus {
  /**
   * IdentityEventbus constructor
   *
   * @param {Object} [options={}] - Options
   * @param {string} [options.maxQueuedEvents=50] - Maximum queue length
   * @param {string} [options.name] - Observable name
   */
  constructor({
    maxQueuedEvents = 50,
    name,
  } = {}) {
    if (!name) {
      // eslint-disable-next-line no-console
      console.error('The option attribute "name" is required');
    }

    Object.assign(this, {
      maxQueuedEvents,
      observableName: name,
      _observable: new Observable(),
      _eventQueue: [],
    });
  }

  /**
   * Subscribes a callback to receive all events. If the deliverQueue
   * option is set to true, the callback will immediately be invoked for all passed events in the event queue, limited
   * to (by default) the last 50 events.
   *
   * @param {Function} callback
   * @param {Object} [options]
   * @param {boolean} [options.deliverQueue = false]
   * @return {Function|Error} unsubscribe | error
   */
  subscribe(callback, options = {}) {
    // Make sure observableName is set
    if (!this.observableName) {
      // eslint-disable-next-line no-console
      return console.error('Unable to subscribe, the eventbus observable name has not been set.');
    }

    const unsubscribe = this._observable.on(this.observableName, callback);

    // Deliver queue to new callback as individual events
    if (options.deliverQueue) {
      this._eventQueue.forEach((event) => {
        try {
          callback(event);
        } catch (error) {
          // errors should not break execution
          // eslint-disable-next-line no-console
          console.log(error);
        }
      });
    }

    return unsubscribe;
  }

  /**
   * Adds a generic event to the event queue and publishes it to all subscribers
   *
   * @param {String} eventName
   * @param {Object} properties
   * @return {Error} conditional error
   */
  track(eventName, properties = {}) {
    if (!this.observableName) {
      // eslint-disable-next-line no-console
      return console.error('Unable to track, the eventbus observable name has not been set.');
    }

    const {_eventQueue, maxQueuedEvents} = this;
    const event = {
      eventName,
      eventType: 'track',
      properties,
      timestamp: Date.now(),
    };

    _eventQueue.push(event);
    if (_eventQueue.length > maxQueuedEvents) {
      _eventQueue.splice(0, _eventQueue.length - maxQueuedEvents);
    }

    this._observable.notify(this.observableName, event);
  }
}

export default IdentityEventbus;