UNPKG

built.io-browserify

Version:

SDK for Built.io Backend which is compatible with Browserify

482 lines (453 loc) 15.7 kB
var R = require('ramda'); var utility = require('./utilities/utility'); var instanceMethodBuilder = require('./utilities/instanceMethodBuilder')(); var Built = require('./built'); var Event = require('./analyticsEvent') var userMetrics = require('./utilities/user-metrics'); var UUID_TOKEN_POSTFIX = "blt_uuid"; var multipleEvent = '/events/trigger_multiple'; /** * @class Analytics * @classdesc * With analytics, you can track various activity patterns of the user, and modify your application features accordingly. * @instance * @description To see how your application is faring in the wild use Analytics. * @param {object} trackAnonymously Enables annonymous tracking. (Logged-in users details is not traced) (Optional) * @param {object} batchSize Batch size specifies the number of triggers to be buffered before making a network call. (Optional) * @param {object} superProperties Set of custom properties that are to be sent along with each event. To send static super properties using the static key which should be a JavaScript object, for dynamic properties attach a function which should be executed before trigger. This function should return a JavaScript Object. Refer Example for better understanding (Optional) * @example * //'blt5d4sample2633b' is a dummy Application API key * var Analytics = Built.App('blt5d4sample2633b').Analytics; //Returns Analytics constructor * var analytics = app.Analytics(); * var analytics1 = app.Analytics(true); * var analytics2 = app.Analytics(false, 10); * // Static properties to be sent along with each event * var staticProps = { * name:'John', * gender: 'Male' * } * var dynamicProps = function(){ * return { * time: new Date(), * random: Math.Random() * } * } * var analytics3 = app.Analytics(true, 20, { * static: staticProps, * dynamic: dynamicProps * }); * @throw new Error() * @return {Analytics} */ var analyticCons = module.exports = R.curry(function(app, headers, queue, trackAnonymously, batchSize, superProperties) { if(!superProperties.static && !superProperties.dynamic) { throw new Error("Super properties is expected to have atleast a static or dynamic property") } superProperties.static = superProperties.static || {} superProperties.dynamic = superProperties.dynamic || function(){ return {} } var returnObj = { app : app, headers : headers, queue : queue, trackAnonymously: trackAnonymously, batchSize : batchSize, superProperties : superProperties, toJSON : function(){ return superProperties } } var curriedEvent = Event(app, returnObj); /** * Use {@link Event} to trigger an event in Built.io Backend. * @name Event * @memberof Analytics * @example * //'blt5d4sample2633b' is a dummy Application API key * var event = Built.App('blt5d4sample2633b').Analytics().Event('login'). */ returnObj.Event = function(uid, superProperties){ if(!uid) throw new Error("uid not found"); if(superProperties){ return curriedEvent(uid,superProperties); }else{ return curriedEvent(uid,{}); } } utility.copyProperties(returnObj.Event , Event); return instanceMethodBuilder.build(module.exports, returnObj); }); var set = module.exports.set = R.curry(function(key, value, analytics) { var queue = analytics.queue; var trackAnonymously = analytics.trackAnonymously; var batchSize = analytics.batchSize; var superProperties = analytics.superProperties; var headers = analytics.headers; switch(key){ case "headers": headers = value; break; case "queue": queue = value; break; case "trackAnonymously": trackAnonymously = value; break; case "batchSize": batchSize = value; break; case "superProperties": superProperties = value; break; } return analyticCons(analytics.app, headers, queue, trackAnonymously, batchSize, superProperties); }) instanceMethodBuilder.define('set', 3); var get = module.exports.get = R.curry(function(key, analytics){ return analytics[key]; }); instanceMethodBuilder.define('get', 2); /** * Batch size specifies the number of triggers to be buffered before making a network call.The idea is to reduce the number of api calls. The default batch size is set to 1. * @memberof Analytics * @param {Number} size The batch size. * @function setBatchSize * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(); * analytics = analytics.setBatchSize(3); * //This event trigger will be buffered * analytics.Event('test').trigger(); * * //Event this event trigger will be buffered * analytics.Event('test1').trigger(); * * // All three events are triggered in a single network call. * analytics.Event('test3').trigger(); * @return {Analytics} */ module.exports.setBatchSize = set('batchSize'); instanceMethodBuilder.define('setBatchSize',2); /** * Returns the batchSize. * @memberof Analytics * @function getBatchSize * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(true, 10); * //Will return 10 * console.log(analytics.getBatchSize()); * @return {Number} */ module.exports.getBatchSize = get('batchSize'); instanceMethodBuilder.define('getBatchSize',1); /** * By default, the logged-in user's uid is sent to the server for analysis. To track anaymously, set it to false. * @memberof Analytics * @function setTrackAnonymously * @param {Boolean} trackAnonymously Whether to track the logged-in user's uid * @instance * @example * // By default will track user's uid * var analytics = Built.App('blt5d4sample2633b').Analytics(); * // Would track anonymously * analytics = analytics.setTrackAnonymously(true); * @return {Analytics} */ module.exports.setTrackAnonymously = set('trackAnonymously') instanceMethodBuilder.define('setTrackAnonymously',2); /** * Determines whether users are tracked anonymously or not * @memberof Analytics * @function getTrackAnonymously * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(true, 10); * console.log(analytics.getTrackAnonymously()); * @return {Boolean} */ module.exports.getTrackAnonymously = get('trackAnonymously'); instanceMethodBuilder.define('getTrackAnonymously',1); /** * Super properties are a set of properties that need to be sent along with each trigger. * @memberof Analytics * @function setSuperProperties * @param {Object} properties Set of static and dynamic properties to be sent along with each trigger. For static properties use 'static' key with a JavaScript object as its value. For dyanmic properties use 'dynamic' key with a function to be executed before each trigger. (Function should return a JavaScript Object) * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(); * * var staticProps = { * name:'John', * gender: 'Male' * } * * var dynamicProps: function(){ * return { * time: new Date(), * random: Math.Random() * } * } * * analytics = analytics.setSuperProperties({ * static: staticProps, * dynamic: dynamicProps * }); * * analytics.Event('test',{ * abc: "pqr" * }).trigger(); * @return {Analytics} */ module.exports.setSuperProperties = set('superProperties'); instanceMethodBuilder.define('setSuperProperties',2); /** * Static properties that should be sent along with each event * @memberof Analytics * @function setStaticSuperProperties * @param {Object} properties A set of static properties to be sent with each trigger * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(); * analytics = analytics.setStaticSuperProperties({ * name:'John', * gender: 'Male' * }); * analytics.Event('test',{ * abc: "pqr" * }).trigger(); * @return {Analytics} */ module.exports.setStaticSuperProperties = R.curry(function(properties, analytics){ var newSuperProperties = R.mixin({}, analytics.getSuperProperties()) newSuperProperties.static = properties; return set('superProperties', newSuperProperties, analytics) }) instanceMethodBuilder.define('setStaticSuperProperties',2); /** * Super properties are a set of properties that need to be sent along with each trigger. * @memberof Analytics * @function setDynamicSuperProperties * @param {Function} propertiesGenerator A function which will generate event properties dynamically * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(); * * var fn = function(){ * return { * time: new Date(), * randome: Math.random() * } * } * * analytics = analytics.setDynamicSuperProperties(fn); * * analytics.Event('test',{ * abc: "pqr" * }).trigger(); * @return {Analytics} */ module.exports.setDynamicSuperProperties = R.curry(function(fn, analytics){ var newSuperProperties = R.mixin({}, analytics.getSuperProperties()) newSuperProperties.dynamic = fn; return set('superProperties', newSuperProperties, analytics) }) instanceMethodBuilder.define('setDynamicSuperProperties',2); /** * Returns the super properties set on this instance * @memberof Analytics * @function getSuperProperties * @instance * @example * var staticProps = { * name:'John', * gender: 'Male' * } * var analytics = Built.App('blt5d4sample2633b').Analytics(true, 10, { * static: staticProps * }); * console.log(analytics.getSuperProperties()); * @return {object} */ module.exports.getSuperProperties = get('superProperties'); instanceMethodBuilder.define('getSuperProperties',1); /** * Sets a header on analytics level * @memberof Analytics * @function setHeader * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(); * analytics = analytics.setHeader('abc','pqr') * console.log(analytics.getHeaders()); * @return {Analytics} */ module.exports.setHeader = R.curry(function(key, value, analytics){ var newHeader = R.mixin({}, analytics.headers); newHeader[key] = value; return set('headers', newHeader, analytics); }) instanceMethodBuilder.define('setHeader',3); /** * Returns headers set on analytics level * @memberof Analytics * @function getHeaders * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(); * analytics = analytics.setHeader('abc','pqr') * console.log(analytics.getHeaders()); * @return {object} */ module.exports.getHeaders = get('headers'); instanceMethodBuilder.define('getHeaders',1); /** * Triggers or enqueues a set of events based on the batchSize * @memberof Analytics * @function trigger * @param {Array|Object} event(s) Accepts an array of 'Event' objects or a single 'Event' object * @param {Boolean} forceTrigger Triggers the events regardless of the batchSize (Optional) * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(true, 10); * var eventObj = analytics.Event('test'); * var eventObj1 = analytics.Event('test1'); * * //Case 1: * //Enqueues the two events in buffer * analytics.trigger([eventObj, eventObj1]); * //Enqueues the eventObj in buffer * analytics.trigger(eventObj); * * //Case 2: * // Triggers immediately * analytics.trigger([eventObj, eventObj1], true) * //Triggers eventObj immediately * analytics.trigger(eventObj, true); */ module.exports.trigger = function(events, forceTrigger, analytics){ if(!utility.isArray(events)){ events = [events] } var queuedEvents = analytics.get('queue'); if(forceTrigger) { return triggerNReset(events, analytics); } if(analytics.get('batchSize') === 1){ return triggerNReset(events, analytics); }else{ var concatinatedEvents = queuedEvents.concat(events); if(concatinatedEvents.length >= analytics.getBatchSize()){ return triggerNReset(queuedEvents, analytics); }else{ analytics.queue = concatinatedEvents; } } } instanceMethodBuilder.define('trigger', 3, [false]); /** * Flushes all the events that are buffered * @memberof Analytics * @function flush * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(true, 10); * var eventObj = analytics.Event('test'); * var eventObj1 = analytics.Event('test1'); * * //This event trigger will be buffered * eventObj.trigger(); * * //Even this event trigger will be buffered * eventObj1.trigger(); * * console.log(analytics.getQueue().length) * * // Triggers the two events and empties the queue * analytics.flush(); */ module.exports.flush = function(analytics){ module.exports.trigger(analytics.queue, true, analytics); analytics.resetBatchQueue(); } instanceMethodBuilder.define('flush', 1); /** * Returns the queue * @memberof Analytics * @function getQueue * @instance * @example * var analytics = Built.App('blt5d4sample2633b').Analytics(true, 10); * var eventObj = analytics.Event('test'); * var eventObj1 = analytics.Event('test1'); * * //This event trigger will be buffered * eventObj.trigger(); * * //Even this event trigger will be buffered * eventObj1.trigger(); //buffered * * console.log(analytics.getQueue()) * @return {array} */ module.exports.getQueue = get('queue'); instanceMethodBuilder.define('getQueue', 1); var resetBatchQueue = module.exports.resetBatchQueue = function(analytics){ analytics.queue = []; return analytics; } instanceMethodBuilder.define('resetBatchQueue',1); module.exports.pushEventInQueue = R.curry(function(eventObj, analytics){ analytics.queue = analytics.get('queue').concat(eventObj); if(analytics.queue.length === analytics.getBatchSize()){ module.exports.trigger(analytics.get('queue'), true, analytics); analytics.resetBatchQueue(); } }); instanceMethodBuilder.define('pushEventInQueue', 2); function triggerNReset(eventArray, analytics){ triggerMultiple(eventArray); analytics.resetBatchQueue(); } function getCombinedHeaders(analytics) { var Built = require('./built') return R.mixin(analytics.headers, Built.App.getHeaders(analytics.app)) } function triggerMultiple(eventArray){ var analytics; //for getting the app instance required to generate url var keyValue = utility.keyValue; if(utility.isArray(eventArray)){ if(eventArray.length > 0){ analytics = eventArray[0].analytics; }else{ throw new Error('Events array is empty'); } } //Fetch uuid that should be used for this application var blt_uuid = userMetrics.getUUIDForAPI(analytics.app.getApiKey()) var entity = R.mixin(keyValue('events', constructEventArray(eventArray)), keyValue('super_properties', R.mixin(analytics.getSuperProperties().static,{blt_uuid:blt_uuid}))); var adaptor = analytics.app.options.adaptor; var requestObject = utility.getAdaptorObj('POST', getTriggerMultipleURL(analytics.app), getCombinedHeaders(analytics), entity, null); return adaptor.makeCall(requestObject) .then(function(response){ return eventArray; }) } function constructEventArray(eventArray) { var multiEventObj = {}; eventArray.forEach(function(event){ var eventArray = multiEventObj[event.getUid()] || []; eventArray.push(utility.keyValue('properties',constructProperties(event))); multiEventObj[event.getUid()] = eventArray; }); return multiEventObj; } function constructProperties(event){ // If trackAnonymously is false send currently logged-in user's uid if avaliable, if true send blank value var app_user_object_uid = !event.analytics.getTrackAnonymously()? (event.app.getUserJSON()? event.app.getUserJSON().uid : "anonymous"): "anonymous"; event = event.setProperties({app_user_object_uid:app_user_object_uid}) return R.mixin(event.analytics.getSuperProperties().dynamic(), event.getProperties()) } function getTriggerMultipleURL(app){ return app.getURL() + multipleEvent; // GLOBAL variable }