Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invisibly analytics adapter #4470

Merged
merged 7 commits into from
Dec 2, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 223 additions & 0 deletions modules/invisiblyAnalyticsAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/**
* invisiblyAdapterAdapter.js - analytics adapter for Invisibly
*/
import { ajaxBuilder } from '../src/ajax';
import adapter from '../src/AnalyticsAdapter';
import adapterManager from '../src/adapterManager';

const DEFAULT_EVENT_URL = 'https://api.pymx5.com/v1/' + 'sites/events';
const analyticsType = 'endpoint';
const analyticsName = 'Invisibly Analytics Adapter:';

const utils = require('../src/utils');
const CONSTANTS = require('../src/constants.json');
const ajax = ajaxBuilder(0);

// Events needed
const {
EVENTS: {
AUCTION_INIT,
AUCTION_END,
BID_ADJUSTMENT,
BID_TIMEOUT,
BID_REQUESTED,
BID_RESPONSE,
NO_BID,
BID_WON,
BIDDER_DONE,
SET_TARGETING,
REQUEST_BIDS,
ADD_AD_UNITS,
AD_RENDER_FAILED
}
} = CONSTANTS;

const _VERSION = 1;
const _pageViewId = utils.generateUUID();
let initOptions = null;
let _startAuction = 0;
let _bidRequestTimeout = 0;
let flushInterval;
let invisiblyAnalyticsEnabled = false;

const w = window;
const d = document;
let e = d.documentElement;
let g = d.getElementsByTagName('body')[0];
let x = w.innerWidth || e.clientWidth || g.clientWidth;
let y = w.innerHeight || e.clientHeight || g.clientHeight;

let _pageView = {
eventType: 'pageView',
userAgent: window.navigator.userAgent,
timestamp: Date.now(),
timezoneOffset: new Date().getTimezoneOffset(),
language: window.navigator.language,
vendor: window.navigator.vendor,
screenWidth: x,
screenHeight: y
};

let _eventQueue = [_pageView];

let invisiblyAdapter = Object.assign(
adapter({ url: DEFAULT_EVENT_URL, analyticsType }),
{
track({ eventType, args }) {
handleEvent(eventType, args);
},
sendEvent
}
);

invisiblyAdapter.originEnableAnalytics = invisiblyAdapter.enableAnalytics;
invisiblyAdapter.enableAnalytics = function(config) {
initOptions = config.options || {};
initOptions.url = initOptions.url || DEFAULT_EVENT_URL;
if (initOptions.url && initOptions.account) {
invisiblyAnalyticsEnabled = true;
invisiblyAdapter.originEnableAnalytics(config);
} else {
invisiblyAnalyticsEnabled = false;
invisiblyAdapter.originDisableAnalytics();
}
flushInterval = setInterval(flush, 1000);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given a normal auction workflow - this interval is still active after the auction finishes.

I see the variable assigned here flushInterval gets cleared when the disableAnalytics is called but not anywhere else. Would it make sense to call the clearInterval() again in the auctionEnd part of the sendEvent function (to clean-up after the last flush)?

If you need this interval running after the auction has finished, can you please clarify on the use-case?

Copy link
Contributor Author

@Hassija Hassija Nov 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed @jsnellbaker, have pushed changes to invoke clearInterval() again in the auctionEnd part of the sendEvent function to clean-up after the last flush.

Let me know if any additional changes are needed.
Thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsnellbaker,
Had added the clearInterval() as you suggested, but for some reason, the CircleCI job failed (could you provide some insights?) Have reverted the change & both the CI jobs passed. Is this a blocking change? The interval would be flushed anyways when adapter is disabled.
Will re-commit it if you can give a go-ahead with failing CircleCi job or suggest changes to prevent job from failing.
Regards,
Pankaj

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The circleCI tests haven't been entirely stable as of late (unfortunately), so it's possible that the reason it failed wasn't due to your change.

Can you please reintroduce the change to the file so I can take a look?

Copy link
Contributor Author

@Hassija Hassija Nov 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok @jsnellbaker. Have reintroduced the change to invoke clearInterval() from sendEvent() when AUCTION_END event is triggered.
The circleCI job failed when this change was pushed. Let me know if any additional changes are needed.

};

invisiblyAdapter.originDisableAnalytics = invisiblyAdapter.disableAnalytics;
invisiblyAdapter.disableAnalytics = function() {
if (!invisiblyAnalyticsEnabled) {
return;
}
flush();
clearInterval(flushInterval);
invisiblyAdapter.originDisableAnalytics();
};

function flush() {
if (!invisiblyAnalyticsEnabled) {
return;
}

if (_eventQueue.length > 0) {
while (_eventQueue.length) {
let eventFromQue = _eventQueue.shift();
let eventtype = 'PREBID_' + eventFromQue.eventType;
delete eventFromQue.eventType;

let data = {
pageViewId: _pageViewId,
ver: _VERSION,
bundleId: initOptions.bundleId,
...eventFromQue
};

let payload = {
event_type: eventtype,
event_data: { ...data }
};
ajax(
initOptions.url,
() => utils.logInfo(`${analyticsName} sent events batch`),
JSON.stringify(payload),
{
contentType: 'application/json',
method: 'POST',
withCredentials: true
}
);
}
}
}

function handleEvent(eventType, eventArgs) {
eventArgs = eventArgs ? JSON.parse(JSON.stringify(eventArgs)) : {};
let invisiblyEvent = {};

switch (eventType) {
case AUCTION_INIT: {
invisiblyEvent = eventArgs;
_startAuction = invisiblyEvent.timestamp;
_bidRequestTimeout = invisiblyEvent.timeout;
break;
}
case AUCTION_END: {
invisiblyEvent = eventArgs;
invisiblyEvent.start = _startAuction;
invisiblyEvent.end = Date.now();
break;
}
case BID_ADJUSTMENT: {
invisiblyEvent.bidders = eventArgs;
break;
}
case BID_TIMEOUT: {
invisiblyEvent.bidders = eventArgs;
invisiblyEvent.duration = _bidRequestTimeout;
break;
}
case BID_REQUESTED: {
invisiblyEvent = eventArgs;
break;
}
case BID_RESPONSE: {
invisiblyEvent = eventArgs;
break;
}
case NO_BID: {
invisiblyEvent.noBid = eventArgs;
break;
}
case BID_WON: {
invisiblyEvent = eventArgs;
break;
}
case BIDDER_DONE: {
invisiblyEvent = eventArgs;
break;
}
case SET_TARGETING: {
invisiblyEvent.targetings = eventArgs;
break;
}
case REQUEST_BIDS: {
invisiblyEvent = eventArgs;
break;
}
case ADD_AD_UNITS: {
invisiblyEvent = eventArgs;
break;
}
case AD_RENDER_FAILED: {
invisiblyEvent = eventArgs;
break;
}
default:
return;
}
invisiblyEvent.eventType = eventType;
invisiblyEvent.timestamp = invisiblyEvent.timestamp || Date.now();
sendEvent(invisiblyEvent);
}

function sendEvent(event) {
_eventQueue.push(event);
utils.logInfo(`${analyticsName}Event ${event.eventType}:`, event);

if (event.eventType === AUCTION_END) {
flush();
}
}

adapterManager.registerAnalyticsAdapter({
adapter: invisiblyAdapter,
code: 'invisiblyAnalytics'
});

invisiblyAdapter.getOptions = function() {
return initOptions;
};

invisiblyAdapter.flush = flush;

export default invisiblyAdapter;
24 changes: 24 additions & 0 deletions modules/invisiblyAnalyticsAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Overview

```
Module Name: Invisibly Analytics

Module Type: Analytics Adapter

Maintainer: sanjay.rawlani@invisibly.com
```

# Description

Analytics adapter for Invisibly. Please contact: sanjay.rawlani@invisibly.com for any additional information. Official website link to the vendor: https://invisibly.com/

# Test Parameters

```
{
provider: 'invisiblyAnalytics',
options : {
account: 'invisibly' //account is a mandatory input to adapter configuration
}
}
```
Loading