Skip to content

Commit f4a05bb

Browse files
ckbo3hrkdluxemburg
authored andcommitted
AdkernelAdn analytics adapter (prebid#1868)
* AdkernelAdn analytics adapter * Markdown page fix
1 parent ecc5ce1 commit f4a05bb

File tree

3 files changed

+637
-0
lines changed

3 files changed

+637
-0
lines changed
Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
import adapter from 'src/AnalyticsAdapter';
2+
import CONSTANTS from 'src/constants.json';
3+
import adaptermanager from 'src/adaptermanager';
4+
import {parse} from 'src/url';
5+
import * as utils from 'src/utils';
6+
import {ajax} from 'src/ajax';
7+
8+
const ANALYTICS_VERSION = '1.0.0';
9+
const DEFAULT_QUEUE_TIMEOUT = 4000;
10+
const DEFAULT_HOST = 'tag.adkernel.com';
11+
12+
const ADK_HB_EVENTS = {
13+
AUCTION_INIT: 'auctionInit',
14+
BID_REQUEST: 'bidRequested',
15+
BID_RESPONSE: 'bidResponse',
16+
BID_WON: 'bidWon',
17+
AUCTION_END: 'auctionEnd',
18+
TIMEOUT: 'adapterTimedOut'
19+
};
20+
21+
function buildRequestTemplate(pubId) {
22+
const url = utils.getTopWindowUrl();
23+
const ref = utils.getTopWindowReferrer();
24+
const topLocation = utils.getTopWindowLocation();
25+
26+
return {
27+
ver: ANALYTICS_VERSION,
28+
domain: topLocation.hostname,
29+
path: topLocation.pathname,
30+
accId: pubId,
31+
env: {
32+
screen: {
33+
w: window.screen.width,
34+
h: window.screen.height
35+
},
36+
lang: navigator.language
37+
},
38+
src: getUmtSource(url, ref)
39+
}
40+
}
41+
42+
let analyticsAdapter = Object.assign(adapter({analyticsType: 'endpoint'}),
43+
{
44+
track({ eventType, args }) {
45+
if (!analyticsAdapter.context) {
46+
return;
47+
}
48+
let handler = null;
49+
switch (eventType) {
50+
case CONSTANTS.EVENTS.AUCTION_INIT:
51+
if (analyticsAdapter.context.queue) {
52+
analyticsAdapter.context.queue.init();
53+
}
54+
handler = trackAuctionInit;
55+
break;
56+
case CONSTANTS.EVENTS.BID_REQUESTED:
57+
handler = trackBidRequest;
58+
break;
59+
case CONSTANTS.EVENTS.BID_RESPONSE:
60+
handler = trackBidResponse;
61+
break;
62+
case CONSTANTS.EVENTS.BID_WON:
63+
handler = trackBidWon;
64+
break;
65+
case CONSTANTS.EVENTS.BID_TIMEOUT:
66+
handler = trackBidTimeout;
67+
break;
68+
case CONSTANTS.EVENTS.AUCTION_END:
69+
handler = trackAuctionEnd;
70+
break;
71+
}
72+
if (handler) {
73+
let events = handler(args);
74+
if (analyticsAdapter.context.queue) {
75+
analyticsAdapter.context.queue.push(events);
76+
}
77+
if (eventType === CONSTANTS.EVENTS.AUCTION_END) {
78+
sendAll();
79+
}
80+
}
81+
}
82+
});
83+
84+
analyticsAdapter.context = {};
85+
86+
analyticsAdapter.originEnableAnalytics = analyticsAdapter.enableAnalytics;
87+
88+
analyticsAdapter.enableAnalytics = (config) => {
89+
if (!config.options.pubId) {
90+
utils.logError('PubId is not defined. Analytics won\'t work');
91+
return;
92+
}
93+
analyticsAdapter.context = {
94+
host: config.options.host || DEFAULT_HOST,
95+
pubId: config.options.pubId,
96+
requestTemplate: buildRequestTemplate(config.options.pubId),
97+
queue: new ExpiringQueue(sendAll, config.options.queueTimeout || DEFAULT_QUEUE_TIMEOUT)
98+
};
99+
analyticsAdapter.originEnableAnalytics(config);
100+
};
101+
102+
adaptermanager.registerAnalyticsAdapter({
103+
adapter: analyticsAdapter,
104+
code: 'adkernelAdn'
105+
});
106+
107+
export default analyticsAdapter;
108+
109+
function sendAll() {
110+
let events = analyticsAdapter.context.queue.popAll();
111+
if (events.length !== 0) {
112+
let req = Object.assign({}, analyticsAdapter.context.requestTemplate, {hb_ev: events});
113+
ajax(`//${analyticsAdapter.context.host}/hb-analytics`, () => {
114+
}, JSON.stringify(req));
115+
}
116+
}
117+
118+
function trackAuctionInit() {
119+
analyticsAdapter.context.auctionTimeStart = Date.now();
120+
const event = createHbEvent(undefined, ADK_HB_EVENTS.AUCTION_INIT);
121+
return [event];
122+
}
123+
124+
function trackBidRequest(args) {
125+
return args.bids.map(bid =>
126+
createHbEvent(args.bidderCode, ADK_HB_EVENTS.BID_REQUEST, bid.placementCode));
127+
}
128+
129+
function trackBidResponse(args) {
130+
const event = createHbEvent(args.bidderCode, ADK_HB_EVENTS.BID_RESPONSE,
131+
args.adUnitCode, args.cpm, args.timeToRespond / 1000);
132+
return [event];
133+
}
134+
135+
function trackBidWon(args) {
136+
const event = createHbEvent(args.bidderCode, ADK_HB_EVENTS.BID_WON, args.adUnitCode, args.cpm);
137+
return [event];
138+
}
139+
140+
function trackAuctionEnd(args) {
141+
const event = createHbEvent(undefined, ADK_HB_EVENTS.AUCTION_END, undefined,
142+
undefined, (Date.now() - analyticsAdapter.context.auctionTimeStart) / 1000);
143+
return [event];
144+
}
145+
146+
function trackBidTimeout(args) {
147+
return args.map(bidderName => createHbEvent(bidderName, ADK_HB_EVENTS.TIMEOUT));
148+
}
149+
150+
function createHbEvent(adapter, event, tagid = undefined, value = 0, time = 0) {
151+
let ev = { event: event };
152+
if (adapter) {
153+
ev.adapter = adapter
154+
}
155+
if (tagid) {
156+
ev.tagid = tagid;
157+
}
158+
if (value) {
159+
ev.val = value;
160+
}
161+
if (time) {
162+
ev.time = time;
163+
}
164+
return ev;
165+
}
166+
167+
const UTM_TAGS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',
168+
'utm_c1', 'utm_c2', 'utm_c3', 'utm_c4', 'utm_c5'];
169+
const ADKERNEL_PREBID_KEY = 'adk_dpt_analytics';
170+
const DIRECT = '(direct)';
171+
const REFERRAL = '(referral)';
172+
const ORGANIC = '(organic)';
173+
174+
export function getUmtSource(pageUrl, referrer) {
175+
let prevUtm = getPreviousTrafficSource();
176+
let currUtm = getCurrentTrafficSource(pageUrl, referrer);
177+
let [updated, actual] = chooseActualUtm(prevUtm, currUtm);
178+
if (updated) {
179+
storeUtm(actual);
180+
}
181+
return actual;
182+
183+
function getPreviousTrafficSource() {
184+
let val = localStorage.getItem(ADKERNEL_PREBID_KEY);
185+
if (!val) {
186+
return getDirect();
187+
}
188+
return JSON.parse(val);
189+
}
190+
191+
function getCurrentTrafficSource(pageUrl, referrer) {
192+
var source = getUTM(pageUrl);
193+
if (source) {
194+
return source;
195+
}
196+
if (referrer) {
197+
let se = getSearchEngine(referrer);
198+
if (se) {
199+
return asUtm(se, ORGANIC, ORGANIC);
200+
}
201+
let parsedUrl = parse(pageUrl);
202+
let [refHost, refPath] = getReferrer(referrer);
203+
if (refHost && refHost !== parsedUrl.hostname) {
204+
return asUtm(refHost, REFERRAL, REFERRAL, '', refPath);
205+
}
206+
}
207+
return getDirect();
208+
}
209+
210+
function getSearchEngine(pageUrl) {
211+
let engines = {
212+
'google': /^https?\:\/\/(?:www\.)?(?:google\.(?:com?\.)?(?:com|cat|[a-z]{2})|g.cn)\//i,
213+
'yandex': /^https?\:\/\/(?:www\.)?ya(?:ndex\.(?:com|net)?\.?(?:asia|mobi|org|[a-z]{2})?|\.ru)\//i,
214+
'bing': /^https?\:\/\/(?:www\.)?bing\.com\//i,
215+
'duckduckgo': /^https?\:\/\/(?:www\.)?duckduckgo\.com\//i,
216+
'ask': /^https?\:\/\/(?:www\.)?ask\.com\//i,
217+
'yahoo': /^https?\:\/\/(?:[-a-z]+\.)?(?:search\.)?yahoo\.com\//i
218+
};
219+
220+
for (let engine in engines) {
221+
if (engines.hasOwnProperty(engine) && engines[engine].test(pageUrl)) {
222+
return engine;
223+
}
224+
}
225+
}
226+
227+
function getReferrer(referrer) {
228+
let ref = parse(referrer);
229+
return [ref.hostname, ref.pathname];
230+
}
231+
232+
function getUTM(pageUrl) {
233+
let urlParameters = parse(pageUrl).search;
234+
if (!urlParameters['utm_campaign'] || !urlParameters['utm_source']) {
235+
return;
236+
}
237+
let utmArgs = [];
238+
for (let utmTagName of UTM_TAGS) {
239+
let utmValue = urlParameters[utmTagName] || '';
240+
utmArgs.push(utmValue);
241+
}
242+
return asUtm.apply(this, utmArgs);
243+
}
244+
245+
function getDirect() {
246+
return asUtm(DIRECT, DIRECT, DIRECT);
247+
}
248+
249+
function storeUtm(utm) {
250+
let val = JSON.stringify(utm);
251+
localStorage.setItem(ADKERNEL_PREBID_KEY, val);
252+
}
253+
254+
function asUtm(source, medium, campaign, term = '', content = '', c1 = '', c2 = '', c3 = '', c4 = '', c5 = '') {
255+
let result = {
256+
source: source,
257+
medium: medium,
258+
campaign: campaign
259+
};
260+
if (term) {
261+
result.term = term;
262+
}
263+
if (content) {
264+
result.content = content;
265+
}
266+
if (c1) {
267+
result.c1 = c1;
268+
}
269+
if (c2) {
270+
result.c2 = c2;
271+
}
272+
if (c3) {
273+
result.c3 = c3;
274+
}
275+
if (c4) {
276+
result.c4 = c4;
277+
}
278+
if (c5) {
279+
result.c5 = c5;
280+
}
281+
return result;
282+
}
283+
284+
function chooseActualUtm(prev, curr) {
285+
if (ord(prev) < ord(curr)) {
286+
return [true, curr];
287+
} if (ord(prev) > ord(curr)) {
288+
return [false, prev];
289+
} else {
290+
if (prev.campaign === REFERRAL && prev.content !== curr.content) {
291+
return [true, curr];
292+
} else if (prev.campaign === ORGANIC && prev.source !== curr.source) {
293+
return [true, curr];
294+
} else if (isCampaignTraffic(prev) && (prev.campaign !== curr.campaign || prev.source !== curr.source)) {
295+
return [true, curr];
296+
}
297+
}
298+
return [false, prev];
299+
}
300+
301+
function ord(utm) {
302+
switch (utm.campaign) {
303+
case DIRECT:
304+
return 0;
305+
case ORGANIC:
306+
return 1;
307+
case REFERRAL:
308+
return 2;
309+
default:
310+
return 3;
311+
}
312+
}
313+
314+
function isCampaignTraffic(utm) {
315+
return [DIRECT, REFERRAL, ORGANIC].indexOf(utm.campaign) === -1;
316+
}
317+
}
318+
319+
/**
320+
* Expiring queue implementation. Fires callback on elapsed timeout since last last update or creation.
321+
* @param callback
322+
* @param ttl
323+
* @constructor
324+
*/
325+
export function ExpiringQueue(callback, ttl) {
326+
let queue = [];
327+
let timeoutId;
328+
329+
this.push = (event) => {
330+
if (event instanceof Array) {
331+
queue.push.apply(queue, event);
332+
} else {
333+
queue.push(event);
334+
}
335+
reset();
336+
};
337+
338+
this.popAll = () => {
339+
let result = queue;
340+
queue = [];
341+
reset();
342+
return result;
343+
};
344+
345+
/**
346+
* For test/debug purposes only
347+
* @return {Array}
348+
*/
349+
this.peekAll = () => {
350+
return queue;
351+
};
352+
353+
this.init = reset;
354+
355+
function reset() {
356+
if (timeoutId) {
357+
clearTimeout(timeoutId);
358+
}
359+
timeoutId = setTimeout(() => {
360+
if (queue.length) {
361+
callback();
362+
}
363+
}, ttl);
364+
}
365+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Overview
2+
Module Name: Adkernel ADN Analytics Adapter
3+
4+
Module Type: Analytics Adapter
5+
6+
Maintainer: [email protected]
7+
8+
# Description
9+
10+
Analytics adapter for Adkernel Ad Delivery Network. Contact [email protected] for information.
11+
12+
# Test Parameters
13+
14+
```
15+
{
16+
provider: 'adkernelAdn',
17+
options : {
18+
pubId : 50357, //id provided by adkernel
19+
host: 'dsp-staging.adkernel.com' //optional host for validation purposes
20+
}
21+
}
22+
```

0 commit comments

Comments
 (0)