Skip to content

Commit cac0902

Browse files
wi101leonelcuevasleonelcuevas
authored
LiveIntent Analytics Adapter: initial release (prebid#8960)
* CM-552 Liveintent Analytics Adapter (#4) * start work * send analytics event * Add first test and get winning bids from auctionManager * Add event test data and fix bugs * Remove duplicate userIds * add bidWonTimeout in configOptions * add sampling and adjust test * Add server test * Compare expected request body in the test * refactoring * update description * remove comment * comments * make sure we map defined data * refactoring * some refactoring * comments Co-authored-by: wiem <[email protected]> * fix typo * Use getRefererInfo to get url and ?? operator for default values Co-authored-by: Leonel Cuevas Valeriano <[email protected]> Co-authored-by: leonelcuevas <[email protected]>
1 parent ceb96ab commit cac0902

File tree

3 files changed

+467
-0
lines changed

3 files changed

+467
-0
lines changed

modules/liveIntentAnalyticsAdapter.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import {ajax} from '../src/ajax.js';
2+
import { generateUUID, logInfo, logWarn } from '../src/utils.js';
3+
import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js';
4+
import CONSTANTS from '../src/constants.json';
5+
import adapterManager from '../src/adapterManager.js';
6+
import { auctionManager } from '../src/auctionManager.js';
7+
import { getRefererInfo } from '../src/refererDetection.js';
8+
9+
const ANALYTICS_TYPE = 'endpoint';
10+
const URL = 'https://wba.liadm.com/analytic-events';
11+
const GVL_ID = 148;
12+
const ADAPTER_CODE = 'liveintent';
13+
const DEFAULT_SAMPLING = 0.1;
14+
const DEFAULT_BID_WON_TIMEOUT = 2000;
15+
const { EVENTS: { AUCTION_END } } = CONSTANTS;
16+
let initOptions = {};
17+
let isSampled;
18+
let bidWonTimeout;
19+
20+
function handleAuctionEnd(args) {
21+
setTimeout(() => {
22+
const auction = auctionManager.index.getAuction(args.auctionId);
23+
const winningBids = (auction) ? auction.getWinningBids() : [];
24+
const data = createAnalyticsEvent(args, winningBids);
25+
sendAnalyticsEvent(data);
26+
}, bidWonTimeout);
27+
}
28+
29+
function getAnalyticsEventBids(bidsReceived) {
30+
return bidsReceived.map(bid => {
31+
return {
32+
adUnitCode: bid.adUnitCode,
33+
timeToRespond: bid.timeToRespond,
34+
cpm: bid.cpm,
35+
currency: bid.currency,
36+
ttl: bid.ttl,
37+
bidder: bid.bidder
38+
};
39+
});
40+
}
41+
42+
function getBannerSizes(banner) {
43+
if (banner && banner.sizes) {
44+
return banner.sizes.map(size => {
45+
const [width, height] = size;
46+
return {w: width, h: height};
47+
});
48+
} else return [];
49+
}
50+
51+
function getUniqueBy(arr, key) {
52+
return [...new Map(arr.map(item => [item[key], item])).values()]
53+
}
54+
55+
function createAnalyticsEvent(args, winningBids) {
56+
let payload = {
57+
instanceId: generateUUID(),
58+
url: getRefererInfo().page,
59+
bidsReceived: getAnalyticsEventBids(args.bidsReceived),
60+
auctionStart: args.timestamp,
61+
auctionEnd: args.auctionEnd,
62+
adUnits: [],
63+
userIds: [],
64+
bidders: []
65+
}
66+
let allUserIds = [];
67+
68+
if (args.adUnits) {
69+
args.adUnits.forEach(unit => {
70+
if (unit.mediaTypes && unit.mediaTypes.banner) {
71+
payload['adUnits'].push({
72+
code: unit.code,
73+
mediaType: 'banner',
74+
sizes: getBannerSizes(unit.mediaTypes.banner),
75+
ortb2Imp: unit.ortb2Imp
76+
});
77+
}
78+
if (unit.bids) {
79+
let userIds = unit.bids.flatMap(getAnalyticsEventUserIds);
80+
allUserIds.push(...userIds);
81+
let bidders = unit.bids.map(({bidder, params}) => {
82+
return { bidder, params }
83+
});
84+
85+
payload['bidders'].push(...bidders);
86+
}
87+
})
88+
let uniqueUserIds = getUniqueBy(allUserIds, 'source');
89+
payload['userIds'] = uniqueUserIds;
90+
}
91+
payload['winningBids'] = getAnalyticsEventBids(winningBids);
92+
payload['auctionId'] = args.auctionId;
93+
return payload;
94+
}
95+
96+
function getAnalyticsEventUserIds(bid) {
97+
if (bid && bid.userIdAsEids) {
98+
return bid.userIdAsEids.map(({source, uids, ext}) => {
99+
let analyticsEventUserId = {source, uids, ext};
100+
return ignoreUndefined(analyticsEventUserId)
101+
});
102+
} else { return []; }
103+
}
104+
105+
function sendAnalyticsEvent(data) {
106+
ajax(URL, {
107+
success: function () {
108+
logInfo('LiveIntent Prebid Analytics: send data success');
109+
},
110+
error: function (e) {
111+
logWarn('LiveIntent Prebid Analytics: send data error' + e);
112+
}
113+
}, JSON.stringify(data), {
114+
contentType: 'application/json',
115+
method: 'POST'
116+
})
117+
}
118+
119+
function ignoreUndefined(data) {
120+
const filteredData = Object.entries(data).filter(([key, value]) => value)
121+
return Object.fromEntries(filteredData)
122+
}
123+
124+
let liAnalytics = Object.assign(adapter({URL, ANALYTICS_TYPE}), {
125+
track({ eventType, args }) {
126+
if (eventType == AUCTION_END && args && isSampled) { handleAuctionEnd(args); }
127+
}
128+
});
129+
130+
// save the base class function
131+
liAnalytics.originEnableAnalytics = liAnalytics.enableAnalytics;
132+
133+
// override enableAnalytics so we can get access to the config passed in from the page
134+
liAnalytics.enableAnalytics = function (config) {
135+
initOptions = config.options;
136+
const sampling = (initOptions && initOptions.sampling) ?? DEFAULT_SAMPLING;
137+
isSampled = Math.random() < parseFloat(sampling);
138+
bidWonTimeout = (initOptions && initOptions.bidWonTimeout) ?? DEFAULT_BID_WON_TIMEOUT;
139+
liAnalytics.originEnableAnalytics(config); // call the base class function
140+
};
141+
142+
adapterManager.registerAnalyticsAdapter({
143+
adapter: liAnalytics,
144+
code: ADAPTER_CODE,
145+
gvlid: GVL_ID
146+
});
147+
148+
export default liAnalytics;

modules/liveIntentAnalyticsAdapter.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Overview
2+
Module Name: LiveIntent Analytics Adapter
3+
4+
Module Type: Analytics Adapter
5+
6+
Maintainer: [email protected]
7+
8+
# Description
9+
10+
Analytics adapter for [LiveIntent](https://www.liveintent.com/). Contact [email protected] for information.
11+
12+
# Test Parameters
13+
14+
```
15+
{
16+
provider: 'liveintent',
17+
options: {
18+
bidWonTimeout: 2000,
19+
sampling: 0.5 // the tracked event percentage, a number between 0 to 1
20+
}
21+
}
22+
```

0 commit comments

Comments
 (0)