Skip to content

Commit

Permalink
Add analytics adapter by Sigmoid (#2316)
Browse files Browse the repository at this point in the history
* Add analytics adapter and tests

* Linting code

* +Linting code

* Date change

* ++Linting code

* +++Fix Linting

* Add docs

* Remove dead code and alert

* Change let to const and typo fix

* use includes by importing from core-js

* +use includes by importing from core-js

* Increase test coverage to 75%

* Fix travis build error

* +Fix travis build error
  • Loading branch information
sigmoidanalytics authored and jaiminpanchal27 committed Apr 17, 2018
1 parent a76420c commit 1b29d1f
Show file tree
Hide file tree
Showing 3 changed files with 375 additions and 0 deletions.
285 changes: 285 additions & 0 deletions modules/sigmoidAnalyticsAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
/* Sigmoid Analytics Adapter for prebid.js v1.1.0-pre
Updated : 2018-03-28 */
import includes from 'core-js/library/fn/array/includes';
import adapter from 'src/AnalyticsAdapter';
import CONSTANTS from 'src/constants.json';
import adaptermanager from 'src/adaptermanager';

const utils = require('src/utils');

const url = 'https://kinesis.us-east-1.amazonaws.com/';
const analyticsType = 'endpoint';

const auctionInitConst = CONSTANTS.EVENTS.AUCTION_INIT;
const auctionEndConst = CONSTANTS.EVENTS.AUCTION_END;
const bidWonConst = CONSTANTS.EVENTS.BID_WON;
const bidRequestConst = CONSTANTS.EVENTS.BID_REQUESTED;
const bidAdjustmentConst = CONSTANTS.EVENTS.BID_ADJUSTMENT;
const bidResponseConst = CONSTANTS.EVENTS.BID_RESPONSE;

let initOptions = { publisherIds: [], utmTagData: [], adUnits: [] };
let bidWon = {options: {}, events: []};
let eventStack = {options: {}, events: []};

let auctionStatus = 'not_started';

let localStoragePrefix = 'sigmoid_analytics_';
let utmTags = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
let utmTimeoutKey = 'utm_timeout';
let utmTimeout = 60 * 60 * 1000;
let sessionTimeout = 60 * 60 * 1000;
let sessionIdStorageKey = 'session_id';
let sessionTimeoutKey = 'session_timeout';

function getParameterByName(param) {
let vars = {};
window.location.href.replace(location.hash, '').replace(
/[?&]+([^=&]+)=?([^&]*)?/gi,
function(m, key, value) {
vars[key] = value !== undefined ? value : '';
}
);

return vars[param] ? vars[param] : '';
}

function buildSessionIdLocalStorageKey() {
return localStoragePrefix.concat(sessionIdStorageKey);
}

function buildSessionIdTimeoutLocalStorageKey() {
return localStoragePrefix.concat(sessionTimeoutKey);
}

function updateSessionId() {
if (isSessionIdTimeoutExpired()) {
let newSessionId = utils.generateUUID();
localStorage.setItem(buildSessionIdLocalStorageKey(), newSessionId);
}
initOptions.sessionId = getSessionId();
updateSessionIdTimeout();
}

function updateSessionIdTimeout() {
localStorage.setItem(buildSessionIdTimeoutLocalStorageKey(), Date.now());
}

function isSessionIdTimeoutExpired() {
let cpmSessionTimestamp = localStorage.getItem(buildSessionIdTimeoutLocalStorageKey());
return Date.now() - cpmSessionTimestamp > sessionTimeout;
}

function getSessionId() {
return localStorage.getItem(buildSessionIdLocalStorageKey()) ? localStorage.getItem(buildSessionIdLocalStorageKey()) : '';
}

function updateUtmTimeout() {
localStorage.setItem(buildUtmLocalStorageTimeoutKey(), Date.now());
}

function isUtmTimeoutExpired() {
let utmTimestamp = localStorage.getItem(buildUtmLocalStorageTimeoutKey());
return (Date.now() - utmTimestamp) > utmTimeout;
}

function buildUtmLocalStorageTimeoutKey() {
return localStoragePrefix.concat(utmTimeoutKey);
}

function buildUtmLocalStorageKey(utmMarkKey) {
return localStoragePrefix.concat(utmMarkKey);
}

function checkOptions() {
if (typeof initOptions.publisherIds === 'undefined') {
return false;
}

return initOptions.publisherIds.length > 0;
}

function checkAdUnitConfig() {
if (typeof initOptions.adUnits === 'undefined') {
return false;
}

return initOptions.adUnits.length > 0;
}

function buildBidWon(eventType, args) {
bidWon.options = initOptions;
if (checkAdUnitConfig()) {
if (includes(initOptions.adUnits, args.adUnitCode)) {
bidWon.events = [{ args: args, eventType: eventType }];
}
} else {
bidWon.events = [{ args: args, eventType: eventType }];
}
}

function buildEventStack() {
eventStack.options = initOptions;
}

function filterBidsByAdUnit(bids) {
var filteredBids = [];
bids.forEach(function (bid) {
if (includes(initOptions.adUnits, bid.placementCode)) {
filteredBids.push(bid);
}
});
return filteredBids;
}

function isValidEvent(eventType, adUnitCode) {
if (checkAdUnitConfig()) {
let validationEvents = [bidAdjustmentConst, bidResponseConst, bidWonConst];
if (!includes(initOptions.adUnits, adUnitCode) && includes(validationEvents, eventType)) {
return false;
}
}
return true;
}

function isValidEventStack() {
if (eventStack.events.length > 0) {
return eventStack.events.some(function(event) {
return bidRequestConst === event.eventType || bidWonConst === event.eventType;
});
}
return false;
}

function isValidBidWon() {
return bidWon.events.length > 0;
}

function flushEventStack() {
eventStack.events = [];
}

let sigmoidAdapter = Object.assign(adapter({url, analyticsType}),
{
track({eventType, args}) {
if (!checkOptions()) {
return;
}

let info = Object.assign({}, args);

if (info && info.ad) {
info.ad = '';
}

if (eventType === auctionInitConst) {
auctionStatus = 'started';
}

if (eventType === bidWonConst && auctionStatus === 'not_started') {
updateSessionId();
buildBidWon(eventType, info);
if (isValidBidWon()) {
send(eventType, bidWon, 'bidWon');
}
return;
}

if (eventType === auctionEndConst) {
updateSessionId();
buildEventStack();
if (isValidEventStack()) {
send(eventType, eventStack, 'eventStack');
}
auctionStatus = 'not_started';
} else {
pushEvent(eventType, info);
}
},

});

sigmoidAdapter.originEnableAnalytics = sigmoidAdapter.enableAnalytics;

sigmoidAdapter.enableAnalytics = function (config) {
initOptions = config.options;
initOptions.utmTagData = this.buildUtmTagData();
utils.logInfo('Sigmoid Analytics enabled with config', initOptions);
sigmoidAdapter.originEnableAnalytics(config);
};

sigmoidAdapter.buildUtmTagData = function () {
let utmTagData = {};
let utmTagsDetected = false;
utmTags.forEach(function(utmTagKey) {
let utmTagValue = getParameterByName(utmTagKey);
if (utmTagValue !== '') {
utmTagsDetected = true;
}
utmTagData[utmTagKey] = utmTagValue;
});
utmTags.forEach(function(utmTagKey) {
if (utmTagsDetected) {
localStorage.setItem(buildUtmLocalStorageKey(utmTagKey), utmTagData[utmTagKey]);
updateUtmTimeout();
} else {
if (!isUtmTimeoutExpired()) {
utmTagData[utmTagKey] = localStorage.getItem(buildUtmLocalStorageKey(utmTagKey)) ? localStorage.getItem(buildUtmLocalStorageKey(utmTagKey)) : '';
updateUtmTimeout();
}
}
});
return utmTagData;
};

function send(eventType, data, sendDataType) {
AWS.config.credentials = new AWS.Credentials({
accessKeyId: 'accesskey', secretAccessKey: 'secretkey'
});

AWS.config.region = 'us-east-1';
AWS.config.credentials.get(function(err) {
// attach event listener
if (err) {
utils.logError(err);
return;
}
// create kinesis service object
var kinesis = new AWS.Kinesis({
apiVersion: '2013-12-02'
});
var dataList = [];
var jsonData = {};
jsonData['Data'] = JSON.stringify(data) + '\n';
jsonData['PartitionKey'] = 'partition-' + Math.random().toString(36).substring(7);
dataList.push(jsonData);
kinesis.putRecords({
Records: dataList,
StreamName: 'sample-stream'
});
if (sendDataType === 'eventStack') {
flushEventStack();
}
});
};

function pushEvent(eventType, args) {
if (eventType === bidRequestConst) {
if (checkAdUnitConfig()) {
args.bids = filterBidsByAdUnit(args.bids);
}
if (args.bids.length > 0) {
eventStack.events.push({ eventType: eventType, args: args });
}
} else {
if (isValidEvent(eventType, args.adUnitCode)) {
eventStack.events.push({ eventType: eventType, args: args });
}
}
}

adaptermanager.registerAnalyticsAdapter({
adapter: sigmoidAdapter,
code: 'sigmoid'
});

export default sigmoidAdapter;
23 changes: 23 additions & 0 deletions modules/sigmoidAnalyticsAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Overview
Module Name: Sigmoid Analytics Adapter

Module Type: Analytics Adapter

Maintainer: ramees@sigmoidanalytics.com

# Description

Analytics adapter for Sigmoid. We are an advanced analytical solutions company.
https://www.sigmoid.com/

# Test Parameters

```
{
provider: 'sigmoid',
options : {
publisherIds: ["3gxdf18d32"]
}
}
```
67 changes: 67 additions & 0 deletions test/spec/modules/sigmoidAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import sigmoidAnalytic from 'modules/sigmoidAnalyticsAdapter';
import { expect } from 'chai';
let events = require('src/events');
let adaptermanager = require('src/adaptermanager');
let constants = require('src/constants.json');

describe('sigmoid Prebid Analytic', function () {
let xhr;
before(() => {
xhr = sinon.useFakeXMLHttpRequest();
})
after(() => {
sigmoidAnalytic.disableAnalytics();
xhr.restore();
});

describe('enableAnalytics', function () {
beforeEach(() => {
sinon.spy(sigmoidAnalytic, 'track');
sinon.stub(events, 'getEvents').returns([]);
});

afterEach(() => {
sigmoidAnalytic.track.restore();
events.getEvents.restore();
});
it('should catch all events', function () {
adaptermanager.registerAnalyticsAdapter({
code: 'sigmoid',
adapter: sigmoidAnalytic
});

adaptermanager.enableAnalytics({
provider: 'sigmoid',
options: {
publisherIds: ['test_sigmoid_prebid_analytid_publisher_id']
}
});

events.emit(constants.EVENTS.AUCTION_INIT, {});
events.emit(constants.EVENTS.AUCTION_END, {});
events.emit(constants.EVENTS.BID_REQUESTED, {});
events.emit(constants.EVENTS.BID_RESPONSE, {});
events.emit(constants.EVENTS.BID_WON, {});

sinon.assert.callCount(sigmoidAnalytic.track, 5);
});
});
describe('build utm tag data', () => {
beforeEach(() => {
localStorage.setItem('sigmoid_analytics_utm_source', 'utm_source');
localStorage.setItem('sigmoid_analytics_utm_medium', 'utm_medium');
localStorage.setItem('sigmoid_analytics_utm_campaign', '');
localStorage.setItem('sigmoid_analytics_utm_term', '');
localStorage.setItem('sigmoid_analytics_utm_content', '');
localStorage.setItem('sigmoid_analytics_utm_timeout', Date.now());
});
it('should build utm data from local storage', () => {
let utmTagData = sigmoidAnalytic.buildUtmTagData();
expect(utmTagData.utm_source).to.equal('utm_source');
expect(utmTagData.utm_medium).to.equal('utm_medium');
expect(utmTagData.utm_campaign).to.equal('');
expect(utmTagData.utm_term).to.equal('');
expect(utmTagData.utm_content).to.equal('');
});
});
});

0 comments on commit 1b29d1f

Please sign in to comment.