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;