built.io-browserify
Version:
SDK for Built.io Backend which is compatible with Browserify
482 lines (453 loc) • 15.7 kB
JavaScript
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
}