-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add e-planning analytics adapter (#2211)
* Add e-planning analytics adapter * fix irregular spacing in braces * Added new ci config option * Fix linting
- Loading branch information
Showing
3 changed files
with
316 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import {ajax} from 'src/ajax'; | ||
import adapter from 'src/AnalyticsAdapter'; | ||
import adaptermanager from 'src/adaptermanager'; | ||
import * as utils from 'src/utils'; | ||
|
||
const CONSTANTS = require('src/constants.json'); | ||
|
||
const analyticsType = 'endpoint'; | ||
const EPL_HOST = 'https://ads.us.e-planning.net/hba/1/'; | ||
|
||
function auctionEndHandler(args) { | ||
return {auctionId: args.auctionId}; | ||
} | ||
|
||
function auctionInitHandler(args) { | ||
return { | ||
auctionId: args.auctionId, | ||
time: args.timestamp | ||
}; | ||
} | ||
|
||
function bidRequestedHandler(args) { | ||
return { | ||
auctionId: args.auctionId, | ||
time: args.start, | ||
bidder: args.bidderCode, | ||
bids: args.bids.map(function(bid) { | ||
return { | ||
time: bid.startTime, | ||
bidder: bid.bidder, | ||
placementCode: bid.placementCode, | ||
auctionId: bid.auctionId, | ||
sizes: bid.sizes | ||
}; | ||
}), | ||
}; | ||
} | ||
|
||
function bidResponseHandler(args) { | ||
return { | ||
bidder: args.bidder, | ||
size: args.size, | ||
auctionId: args.auctionId, | ||
cpm: args.cpm, | ||
time: args.responseTimestamp, | ||
}; | ||
} | ||
|
||
function bidWonHandler(args) { | ||
return { | ||
auctionId: args.auctionId, | ||
size: args.width + 'x' + args.height, | ||
}; | ||
} | ||
|
||
function bidTimeoutHandler(args) { | ||
return args.map(function(bid) { | ||
return { | ||
bidder: bid.bidder, | ||
auctionId: bid.auctionId | ||
}; | ||
}) | ||
} | ||
|
||
function callHandler(evtype, args) { | ||
let handler = null; | ||
|
||
if (evtype === CONSTANTS.EVENTS.AUCTION_INIT) { | ||
handler = auctionInitHandler; | ||
eplAnalyticsAdapter.context.events = []; | ||
} else if (evtype === CONSTANTS.EVENTS.AUCTION_END) { | ||
handler = auctionEndHandler; | ||
} else if (evtype === CONSTANTS.EVENTS.BID_REQUESTED) { | ||
handler = bidRequestedHandler; | ||
} else if (evtype === CONSTANTS.EVENTS.BID_RESPONSE) { | ||
handler = bidResponseHandler | ||
} else if (evtype === CONSTANTS.EVENTS.BID_TIMEOUT) { | ||
handler = bidTimeoutHandler; | ||
} else if (evtype === CONSTANTS.EVENTS.BID_WON) { | ||
handler = bidWonHandler; | ||
} | ||
|
||
if (handler) { | ||
eplAnalyticsAdapter.context.events.push({ec: evtype, p: handler(args)}); | ||
} | ||
} | ||
|
||
var eplAnalyticsAdapter = Object.assign(adapter( | ||
{ | ||
EPL_HOST, | ||
analyticsType | ||
}), | ||
{ | ||
track({eventType, args}) { | ||
if (typeof args !== 'undefined') { | ||
callHandler(eventType, args); | ||
} | ||
|
||
if (eventType === CONSTANTS.EVENTS.AUCTION_END) { | ||
try { | ||
let strjson = JSON.stringify(eplAnalyticsAdapter.context.events); | ||
ajax(eplAnalyticsAdapter.context.host + eplAnalyticsAdapter.context.ci + '?d=' + encodeURIComponent(strjson)); | ||
} catch (err) {} | ||
} | ||
} | ||
} | ||
); | ||
|
||
eplAnalyticsAdapter.originEnableAnalytics = eplAnalyticsAdapter.enableAnalytics; | ||
|
||
eplAnalyticsAdapter.enableAnalytics = function (config) { | ||
if (!config.options.ci) { | ||
utils.logError('Client ID (ci) option is not defined. Analytics won\'t work'); | ||
return; | ||
} | ||
|
||
eplAnalyticsAdapter.context = { | ||
events: [], | ||
host: config.options.host || EPL_HOST, | ||
ci: config.options.ci | ||
}; | ||
|
||
eplAnalyticsAdapter.originEnableAnalytics(config); | ||
}; | ||
|
||
adaptermanager.registerAnalyticsAdapter({ | ||
adapter: eplAnalyticsAdapter, | ||
code: 'eplanning' | ||
}); | ||
|
||
export default eplAnalyticsAdapter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Overview | ||
|
||
``` | ||
Module Name: E-Planning Analytics Adapter | ||
Module Type: Analytics Adapter | ||
Maintainer: mmartinho@e-planning.net | ||
``` | ||
|
||
# Description | ||
|
||
Analytics adapter for E-Planning. | ||
|
||
# Test Parameters | ||
|
||
``` | ||
{ | ||
provider: 'eplanning', | ||
options : { | ||
host: 'https://ads.us.e-planning.net/hba/1/', // Host (optional) | ||
ci: "123456" // Client ID (required) | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import eplAnalyticsAdapter from 'modules/eplanningAnalyticsAdapter'; | ||
import { expect } from 'chai'; | ||
let adaptermanager = require('src/adaptermanager'); | ||
let events = require('src/events'); | ||
let constants = require('src/constants.json'); | ||
|
||
describe('eplanning analytics adapter', () => { | ||
let xhr; | ||
let requests; | ||
|
||
beforeEach(() => { | ||
xhr = sinon.useFakeXMLHttpRequest(); | ||
requests = []; | ||
xhr.onCreate = request => { requests.push(request) }; | ||
sinon.stub(events, 'getEvents').returns([]); | ||
}); | ||
|
||
afterEach(() => { | ||
xhr.restore(); | ||
events.getEvents.restore(); | ||
}); | ||
|
||
describe('track', () => { | ||
it('builds and sends auction data', () => { | ||
sinon.spy(eplAnalyticsAdapter, 'track'); | ||
|
||
let auctionTimestamp = 1496510254313; | ||
let pauctionId = '5018eb39-f900-4370-b71e-3bb5b48d324f'; | ||
let initOptions = { | ||
host: 'https://ads.ar.e-planning.net/hba/1/', | ||
ci: '12345' | ||
}; | ||
let pbidderCode = 'adapter'; | ||
|
||
const bidRequest = { | ||
bidderCode: pbidderCode, | ||
auctionId: pauctionId, | ||
bidderRequestId: '1a6fc81528d0f6', | ||
bids: [{ | ||
bidder: pbidderCode, | ||
placementCode: 'container-1', | ||
bidId: '208750227436c1', | ||
bidderRequestId: '1a6fc81528d0f6', | ||
auctionId: pauctionId, | ||
startTime: 1509369418389, | ||
sizes: [[300, 250]], | ||
}], | ||
auctionStart: 1509369418387, | ||
timeout: 3000, | ||
start: 1509369418389 | ||
}; | ||
|
||
const bidResponse = { | ||
bidderCode: pbidderCode, | ||
adId: '208750227436c1', | ||
cpm: 0.015, | ||
auctionId: pauctionId, | ||
responseTimestamp: 1509369418832, | ||
requestTimestamp: 1509369418389, | ||
bidder: pbidderCode, | ||
timeToRespond: 443, | ||
size: '300x250', | ||
width: 300, | ||
height: 250, | ||
}; | ||
|
||
let bidTimeout = [ | ||
{ | ||
bidId: '208750227436c1', | ||
bidder: pbidderCode, | ||
auctionId: pauctionId | ||
} | ||
]; | ||
|
||
adaptermanager.registerAnalyticsAdapter({ | ||
code: 'eplanning', | ||
adapter: eplAnalyticsAdapter | ||
}); | ||
|
||
adaptermanager.enableAnalytics({ | ||
provider: 'eplanning', | ||
options: initOptions | ||
}); | ||
|
||
// Emit the events with the "real" arguments | ||
|
||
// Step 1: Send auction init event | ||
events.emit(constants.EVENTS.AUCTION_INIT, { | ||
auctionId: pauctionId, | ||
timestamp: auctionTimestamp | ||
}); | ||
|
||
// Step 2: Send bid requested event | ||
events.emit(constants.EVENTS.BID_REQUESTED, bidRequest); | ||
|
||
// Step 3: Send bid response event | ||
events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); | ||
|
||
// Step 4: Send bid time out event | ||
events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); | ||
|
||
// Step 5: Send auction bid won event | ||
events.emit(constants.EVENTS.BID_WON, { | ||
adId: 'adIdData', | ||
ad: 'adContent', | ||
auctionId: pauctionId, | ||
width: 300, | ||
height: 250 | ||
}); | ||
|
||
// Step 6: Send auction end event | ||
events.emit(constants.EVENTS.AUCTION_END, {auctionId: pauctionId}); | ||
|
||
// Step 7: Find the request data sent (filtering other hosts) | ||
requests = requests.filter(req => req.url.includes(initOptions.host)); | ||
|
||
expect(requests.length).to.equal(1); | ||
|
||
expect(requests[0].url.includes(initOptions.host + initOptions.ci)); | ||
expect(requests[0].url.includes('https://ads.ar.e-planning.net/hba/1/12345?d=')); | ||
|
||
let info = requests[0].url; | ||
let purl = new URL(info); | ||
let eplData = JSON.parse(decodeURIComponent(purl.searchParams.get('d'))); | ||
|
||
// Step 8 check that 6 events were sent | ||
expect(eplData.length).to.equal(6); | ||
|
||
// Step 9 verify that we only receive the parameters we need | ||
let expectedEventValues = [ | ||
// AUCTION INIT | ||
{ec: constants.EVENTS.AUCTION_INIT, | ||
p: {auctionId: pauctionId, time: auctionTimestamp}}, | ||
// BID REQ | ||
{ec: constants.EVENTS.BID_REQUESTED, | ||
p: {auctionId: pauctionId, time: 1509369418389, bidder: pbidderCode, bids: [{time: 1509369418389, sizes: [[300, 250]], bidder: pbidderCode, placementCode: 'container-1', auctionId: pauctionId}]}}, | ||
// BID RESP | ||
{ec: constants.EVENTS.BID_RESPONSE, | ||
p: {auctionId: pauctionId, bidder: pbidderCode, cpm: 0.015, size: '300x250', time: 1509369418832}}, | ||
// BID T.O. | ||
{ec: constants.EVENTS.BID_TIMEOUT, | ||
p: [{auctionId: pauctionId, bidder: pbidderCode}]}, | ||
// BID WON | ||
{ec: constants.EVENTS.BID_WON, | ||
p: {auctionId: pauctionId, size: '300x250'}}, | ||
// AUCTION END | ||
{ec: constants.EVENTS.AUCTION_END, | ||
p: {auctionId: pauctionId}} | ||
]; | ||
|
||
for (let evid = 0; evid < eplData.length; evid++) { | ||
expect(eplData[evid]).to.deep.equal(expectedEventValues[evid]); | ||
} | ||
|
||
// Step 10 check that the host to send the ajax request is configurable via options | ||
expect(eplAnalyticsAdapter.context.host).to.equal(initOptions.host); | ||
|
||
// Step 11 verify that we received 6 events | ||
sinon.assert.callCount(eplAnalyticsAdapter.track, 6); | ||
}); | ||
}); | ||
}); |