forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
adlooxRtdProvider.js
209 lines (182 loc) · 7.42 KB
/
adlooxRtdProvider.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/**
* This module adds the Adloox provider to the real time data module
* This module adds the [Adloox]{@link https://www.adloox.com/} provider to the real time data module
* The {@link module:modules/realTimeData} module is required
* The module will fetch segments from Adloox's server
* @module modules/adlooxRtdProvider
* @requires module:modules/realTimeData
* @requires module:modules/adlooxAnalyticsAdapter
* @optional module:modules/intersectionRtdProvider
*/
/* eslint standard/no-callback-literal: "off" */
/* eslint prebid/validate-imports: "off" */
import {auctionManager} from '../src/auctionManager.js';
import {command as analyticsCommand, COMMAND} from './adlooxAnalyticsAdapter.js';
import {submodule} from '../src/hook.js';
import {ajax} from '../src/ajax.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {getRefererInfo} from '../src/refererDetection.js';
import {
_each,
_map,
buildUrl,
deepAccess,
deepClone,
deepSetValue,
isArray,
isBoolean,
isInteger,
isPlainObject,
logError,
logInfo,
logWarn,
mergeDeep,
parseUrl,
safeJSONParse
} from '../src/utils.js';
import {getGptSlotInfoForAdUnitCode} from '../libraries/gptUtils/gptUtils.js';
const MODULE_NAME = 'adloox';
const MODULE = `${MODULE_NAME}RtdProvider`;
const API_ORIGIN = 'https://p.adlooxtracking.com';
const SEGMENT_HISTORIC = { 'a': 'aud', 'd': 'dis', 'v': 'vid' };
const ADSERVER_TARGETING_PREFIX = 'adl';
function init(config, userConsent) {
logInfo(MODULE, 'init', config, userConsent);
if (!isPlainObject(config)) {
logError(MODULE, 'missing config');
return false;
}
if (config.params === undefined) config.params = {};
if (!(isPlainObject(config.params))) {
logError(MODULE, 'invalid params');
return false;
}
if (!(config.params.imps === undefined || (isInteger(config.params.imps) && config.params.imps > 0))) {
logError(MODULE, 'invalid imps params value');
return false;
}
if (!(config.params.freqcap_ip === undefined || (isInteger(config.params.freqcap_ip) && config.params.freqcap_ip >= 0))) {
logError(MODULE, 'invalid freqcap_ip params value');
return false;
}
if (!(config.params.freqcap_ipua === undefined || (isInteger(config.params.freqcap_ipua) && config.params.freqcap_ipua >= 0))) {
logError(MODULE, 'invalid freqcap_ipua params value');
return false;
}
if (!(config.params.thresholds === undefined || (isArray(config.params.thresholds) && config.params.thresholds.every(x => isInteger(x) && x > 0 && x <= 100)))) {
logError(MODULE, 'invalid thresholds params value');
return false;
}
if (!(config.params.slotinpath === undefined || isBoolean(config.params.slotinpath))) {
logError(MODULE, 'invalid slotinpath params value');
return false;
}
// legacy/deprecated configuration code path
if (!(config.params.params === undefined || (isPlainObject(config.params.params) && isInteger(config.params.params.clientid) && isInteger(config.params.params.tagid) && isInteger(config.params.params.platformid)))) {
logError(MODULE, 'invalid subsection params block');
return false;
}
config.params.thresholds = config.params.thresholds || [ 50, 60, 70, 80, 90 ];
function analyticsConfigCallback(data) {
config = mergeDeep(config.params, data);
}
if (config.params.params) {
logWarn(MODULE, `legacy/deprecated configuration (please migrate to ${MODULE_NAME}AnalyticsAdapter)`);
analyticsConfigCallback(config.params.params);
} else {
analyticsCommand(COMMAND.CONFIG, null, analyticsConfigCallback);
}
return true;
}
function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) {
const adUnits0 = reqBidsConfigObj.adUnits || getGlobal().adUnits;
// adUnits must be ordered according to adUnitCodes for stable 's' param usage and handling the response below
const adUnits = reqBidsConfigObj.adUnitCodes.map(code => adUnits0.find(unit => unit.code == code));
// buildUrl creates PHP style multi-parameters and includes undefined... (╯°□°)╯ ┻━┻
const url = buildUrl(mergeDeep(parseUrl(`${API_ORIGIN}/q`), { search: {
'v': `pbjs-${getGlobal().version}`,
'c': config.params.clientid,
'p': config.params.platformid,
't': config.params.tagid,
'imp': config.params.imps,
'fc_ip': config.params.freqcap_ip,
'fc_ipua': config.params.freqcap_ipua,
'pn': (getRefererInfo().page || '').substr(0, 300).split(/[?#]/)[0],
's': _map(adUnits, function(unit) {
// gptPreAuction runs *after* RTD so pbadslot may not be populated... (╯°□°)╯ ┻━┻
const gpid = deepAccess(unit, 'ortb2Imp.ext.gpid') ||
deepAccess(unit, 'ortb2Imp.ext.data.pbadslot') ||
getGptSlotInfoForAdUnitCode(unit.code).gptSlot ||
unit.code;
const ref = [ gpid ];
if (!config.params.slotinpath) ref.push(unit.code);
return ref.join('\t');
})
} })).replace(/\[\]|[^?&]+=undefined/g, '').replace(/([?&])&+/g, '$1');
ajax(url,
function(responseText, q) {
function val(v, k) {
if (!(SEGMENT_HISTORIC[k] && v >= 0)) return v;
return config.params.thresholds.filter(t => t <= v);
}
const response = safeJSONParse(responseText);
if (!response) {
logError(MODULE, 'unexpected response');
return callback();
}
const { site: ortb2site, user: ortb2user } = reqBidsConfigObj.ortb2Fragments.global;
_each(response, function(v0, k0) {
if (k0 == '_') return;
const k = SEGMENT_HISTORIC[k0] || k0;
const v = val(v0, k0);
deepSetValue(k == k0 ? ortb2user : ortb2site, `ext.data.${MODULE_NAME}_rtd.${k}`, v);
});
_each(response._, function(segments, i) {
_each(segments, function(v0, k0) {
const k = SEGMENT_HISTORIC[k0] || k0;
const v = val(v0, k0);
deepSetValue(adUnits[i], `ortb2Imp.ext.data.${MODULE_NAME}_rtd.${k}`, v);
});
});
deepSetValue(ortb2site, `ext.data.${MODULE_NAME}_rtd.ok`, true);
callback();
}
);
}
function getTargetingData(adUnitArray, config, userConsent, auction) {
function val(v) {
if (isArray(v) && v.length == 0) return undefined;
if (isBoolean(v)) v = ~~v;
if (!v) return undefined; // empty string and zero
return v;
}
const { site: ortb2site, user: ortb2user } = auctionManager.index.getAuction(auction).getFPD().global;
const ortb2base = {};
_each(deepAccess(mergeDeep(ortb2site, ortb2user), `ext.data.${MODULE_NAME}_rtd`), function(v0, k) {
const v = val(v0);
if (v) ortb2base[`${ADSERVER_TARGETING_PREFIX}_${k}`] = v;
});
const targeting = {};
_each(auction.adUnits.filter(unit => adUnitArray.includes(unit.code)), function(unit) {
targeting[unit.code] = deepClone(ortb2base);
const ortb2imp = deepAccess(unit, `ortb2Imp.ext.data.${MODULE_NAME}_rtd`);
_each(ortb2imp, function(v0, k) {
const v = val(v0);
if (v) targeting[unit.code][`${ADSERVER_TARGETING_PREFIX}_${k}`] = v;
});
// ATF results shamelessly exfiltrated from intersectionRtdProvider
const bid = unit.bids.find(bid => !!bid.intersection);
if (bid) {
const v = val(config.params.thresholds.filter(t => t <= (bid.intersection.intersectionRatio * 100)));
if (v) targeting[unit.code][`${ADSERVER_TARGETING_PREFIX}_atf`] = v;
}
});
return targeting;
}
export const subModuleObj = {
name: MODULE_NAME,
init,
getBidRequestData,
getTargetingData
};
submodule('realTimeData', subModuleObj);