prebid.js 5.18.0 → 5.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/browsers.json +1 -0
  2. package/integrationExamples/gpt/akamaidap_segments_example.html +132 -0
  3. package/modules/.submodules.json +1 -0
  4. package/modules/adfBidAdapter.js +21 -16
  5. package/modules/adtelligentBidAdapter.js +2 -1
  6. package/modules/adxcgBidAdapter.js +311 -359
  7. package/modules/airgridRtdProvider.js +1 -1
  8. package/modules/akamaiDapRtdProvider.js +474 -0
  9. package/modules/akamaiDapRtdProvider.md +47 -0
  10. package/modules/appnexusBidAdapter.js +9 -3
  11. package/modules/atsAnalyticsAdapter.js +67 -46
  12. package/modules/atsAnalyticsAdapter.md +1 -0
  13. package/modules/betweenBidAdapter.js +20 -3
  14. package/modules/browsiRtdProvider.js +106 -18
  15. package/modules/cleanioRtdProvider.js +192 -0
  16. package/modules/cleanioRtdProvider.md +59 -0
  17. package/modules/codefuelBidAdapter.js +183 -0
  18. package/modules/codefuelBidAdapter.md +111 -0
  19. package/modules/connectIdSystem.js +104 -0
  20. package/modules/connectIdSystem.md +33 -0
  21. package/modules/deepintentBidAdapter.js +106 -9
  22. package/modules/deepintentBidAdapter.md +36 -1
  23. package/modules/deltaprojectsBidAdapter.js +252 -0
  24. package/modules/deltaprojectsBidAdapter.md +32 -0
  25. package/modules/engageyaBidAdapter.js +157 -0
  26. package/modules/gridBidAdapter.js +1 -0
  27. package/modules/gumgumBidAdapter.js +8 -0
  28. package/modules/inskinBidAdapter.js +7 -3
  29. package/modules/ipromBidAdapter.js +79 -0
  30. package/modules/ixBidAdapter.js +8 -1
  31. package/modules/jixieBidAdapter.js +8 -2
  32. package/modules/justpremiumBidAdapter.js +6 -1
  33. package/modules/kinessoIdSystem.js +1 -1
  34. package/modules/limelightDigitalBidAdapter.js +22 -2
  35. package/modules/livewrappedAnalyticsAdapter.js +49 -1
  36. package/modules/multibid/index.js +3 -3
  37. package/modules/nativoBidAdapter.js +5 -1
  38. package/modules/nextMillenniumBidAdapter.js +12 -3
  39. package/modules/oguryBidAdapter.js +14 -1
  40. package/modules/openxBidAdapter.js +34 -22
  41. package/modules/operaadsBidAdapter.js +21 -1
  42. package/modules/otmBidAdapter.js +146 -0
  43. package/modules/otmBidAdapter.md +27 -26
  44. package/modules/outbrainBidAdapter.js +5 -0
  45. package/modules/playwireBidAdapter.md +61 -0
  46. package/modules/publinkIdSystem.js +11 -6
  47. package/modules/rtdModule/index.js +2 -2
  48. package/modules/sonobiBidAdapter.js +7 -0
  49. package/modules/sortableBidAdapter.js +1 -0
  50. package/modules/teadsBidAdapter.js +3 -0
  51. package/modules/tripleliftBidAdapter.js +22 -5
  52. package/modules/trustxBidAdapter.js +8 -6
  53. package/modules/undertoneBidAdapter.js +9 -5
  54. package/modules/undertoneBidAdapter.md +5 -1
  55. package/modules/userId/eids.js +18 -0
  56. package/modules/userId/eids.md +7 -0
  57. package/modules/userId/userId.md +12 -0
  58. package/modules/ventesBidAdapter.js +370 -0
  59. package/modules/ventesBidAdapter.md +94 -0
  60. package/modules/videobyteBidAdapter.js +13 -6
  61. package/modules/videobyteBidAdapter.md +49 -0
  62. package/modules/yahoosspBidAdapter.js +6 -6
  63. package/modules/yieldmoSyntheticInventoryModule.js +46 -0
  64. package/modules/yieldmoSyntheticInventoryModule.md +68 -0
  65. package/package.json +1 -1
  66. package/src/adapterManager.js +5 -0
  67. package/src/adapters/bidderFactory.js +4 -3
  68. package/src/auction.js +11 -11
  69. package/src/constants.json +1 -0
  70. package/src/secureCreatives.js +6 -7
  71. package/src/targeting.js +11 -9
  72. package/test/spec/modules/adfBidAdapter_spec.js +83 -29
  73. package/test/spec/modules/adtelligentBidAdapter_spec.js +1 -0
  74. package/test/spec/modules/adxcgBidAdapter_spec.js +827 -571
  75. package/test/spec/modules/akamaiDapRtdProvider_spec.js +246 -0
  76. package/test/spec/modules/appnexusBidAdapter_spec.js +16 -1
  77. package/test/spec/modules/atsAnalyticsAdapter_spec.js +42 -9
  78. package/test/spec/modules/betweenBidAdapter_spec.js +41 -0
  79. package/test/spec/modules/browsiRtdProvider_spec.js +62 -7
  80. package/test/spec/modules/cleanioRtdProvider_spec.js +188 -0
  81. package/test/spec/modules/codefuelBidAdapter_spec.js +316 -0
  82. package/test/spec/modules/connectIdSystem_spec.js +189 -0
  83. package/test/spec/modules/deepintentBidAdapter_spec.js +153 -3
  84. package/test/spec/modules/deltaprojectsBidAdapter_spec.js +399 -0
  85. package/test/spec/modules/engageyaBidAdapter_spec.js +286 -0
  86. package/test/spec/modules/eplanningBidAdapter_spec.js +8 -8
  87. package/test/spec/modules/gumgumBidAdapter_spec.js +5 -1
  88. package/test/spec/modules/ipromBidAdapter_spec.js +195 -0
  89. package/test/spec/modules/ixBidAdapter_spec.js +13 -3
  90. package/test/spec/modules/jixieBidAdapter_spec.js +13 -11
  91. package/test/spec/modules/justpremiumBidAdapter_spec.js +9 -2
  92. package/test/spec/modules/limelightDigitalBidAdapter_spec.js +155 -1
  93. package/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +52 -7
  94. package/test/spec/modules/multibid_spec.js +31 -31
  95. package/test/spec/modules/nextMillenniumBidAdapter_spec.js +13 -1
  96. package/test/spec/modules/oguryBidAdapter_spec.js +53 -12
  97. package/test/spec/modules/openxBidAdapter_spec.js +85 -13
  98. package/test/spec/modules/operaadsBidAdapter_spec.js +38 -6
  99. package/test/spec/modules/otmBidAdapter_spec.js +67 -0
  100. package/test/spec/modules/outbrainBidAdapter_spec.js +18 -0
  101. package/test/spec/modules/publinkIdSystem_spec.js +6 -6
  102. package/test/spec/modules/sonobiBidAdapter_spec.js +34 -1
  103. package/test/spec/modules/sortableBidAdapter_spec.js +11 -0
  104. package/test/spec/modules/teadsBidAdapter_spec.js +132 -0
  105. package/test/spec/modules/tripleliftBidAdapter_spec.js +128 -0
  106. package/test/spec/modules/trustxBidAdapter_spec.js +3 -3
  107. package/test/spec/modules/undertoneBidAdapter_spec.js +52 -0
  108. package/test/spec/modules/ventesBidAdapter_spec.js +845 -0
  109. package/test/spec/modules/videobyteBidAdapter_spec.js +2 -2
  110. package/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js +89 -0
  111. package/test/spec/unit/core/adapterManager_spec.js +37 -2
  112. package/test/spec/unit/core/bidderFactory_spec.js +61 -1
  113. package/test/spec/unit/pbjs_api_spec.js +37 -2
  114. package/test/spec/unit/secureCreatives_spec.js +54 -25
@@ -33,7 +33,7 @@ export function attachScriptTagToDOM(rtdConfig) {
33
33
  edktInitializor.load = function(e) {
34
34
  var p = e || 'sdk';
35
35
  var n = document.createElement('script');
36
- n.type = 'text/javascript';
36
+ n.type = 'module';
37
37
  n.async = true;
38
38
  n.src = 'https://cdn.edkt.io/' + p + '/edgekit.min.js';
39
39
  document.getElementsByTagName('head')[0].appendChild(n);
@@ -0,0 +1,474 @@
1
+ /**
2
+ * This module adds the Akamai DAP RTD provider to the real time data module
3
+ * The {@link module:modules/realTimeData} module is required
4
+ * The module will fetch real-time data from DAP
5
+ * @module modules/akamaiDapRtdProvider
6
+ * @requires module:modules/realTimeData
7
+ */
8
+ import {ajax} from '../src/ajax.js';
9
+ import {config} from '../src/config.js';
10
+ import {getStorageManager} from '../src/storageManager.js';
11
+ import {submodule} from '../src/hook.js';
12
+ import {isPlainObject, mergeDeep, logMessage, logInfo, logError} from '../src/utils.js';
13
+
14
+ const MODULE_NAME = 'realTimeData';
15
+ const SUBMODULE_NAME = 'dap';
16
+
17
+ export const SEGMENTS_STORAGE_KEY = 'akamaiDapSegments';
18
+ export const storage = getStorageManager(null, SUBMODULE_NAME);
19
+
20
+ /**
21
+ * Lazy merge objects.
22
+ * @param {String} target
23
+ * @param {String} source
24
+ */
25
+ function mergeLazy(target, source) {
26
+ if (!isPlainObject(target)) {
27
+ target = {};
28
+ }
29
+ if (!isPlainObject(source)) {
30
+ source = {};
31
+ }
32
+ return mergeDeep(target, source);
33
+ }
34
+
35
+ /**
36
+ * Add real-time data & merge segments.
37
+ * @param {Object} bidConfig
38
+ * @param {Object} rtd
39
+ * @param {Object} rtdConfig
40
+ */
41
+ export function addRealTimeData(rtd) {
42
+ logInfo('DEBUG(addRealTimeData) - ENTER');
43
+ if (isPlainObject(rtd.ortb2)) {
44
+ let ortb2 = config.getConfig('ortb2') || {};
45
+ logMessage('DEBUG(addRealTimeData): merging original: ', ortb2);
46
+ logMessage('DEBUG(addRealTimeData): merging in: ', rtd.ortb2);
47
+ config.setConfig({ortb2: mergeLazy(ortb2, rtd.ortb2)});
48
+ }
49
+ logInfo('DEBUG(addRealTimeData) - EXIT');
50
+ }
51
+
52
+ /**
53
+ * Real-time data retrieval from Audigent
54
+ * @param {Object} reqBidsConfigObj
55
+ * @param {function} onDone
56
+ * @param {Object} rtdConfi
57
+ * @param {Object} userConsent
58
+ */
59
+ export function getRealTimeData(bidConfig, onDone, rtdConfig, userConsent) {
60
+ logInfo('DEBUG(getRealTimeData) - ENTER');
61
+ logMessage(' - apiHostname: ' + rtdConfig.params.apiHostname);
62
+ logMessage(' - apiVersion: ' + rtdConfig.params.apiVersion);
63
+ let jsonData = storage.getDataFromLocalStorage(SEGMENTS_STORAGE_KEY);
64
+ if (jsonData) {
65
+ let data = JSON.parse(jsonData);
66
+ if (data.rtd) {
67
+ addRealTimeData(data.rtd);
68
+ onDone();
69
+ logInfo('DEBUG(getRealTimeData) - 1');
70
+ // Don't return - ensure the data is always fresh.
71
+ }
72
+ }
73
+
74
+ if (rtdConfig && isPlainObject(rtdConfig.params)) {
75
+ let config = {
76
+ api_hostname: rtdConfig.params.apiHostname,
77
+ api_version: rtdConfig.params.apiVersion,
78
+ domain: rtdConfig.params.domain,
79
+ segtax: rtdConfig.params.segtax
80
+ };
81
+ let identity = {
82
+ type: rtdConfig.params.identityType
83
+ };
84
+ let token = dapUtils.dapGetToken(config, identity, rtdConfig.params.tokenTtl);
85
+ if (token !== null) {
86
+ let membership = dapUtils.dapGetMembership(config, token);
87
+ let udSegment = dapUtils.dapMembershipToRtbSegment(membership, config);
88
+ logMessage('DEBUG(getRealTimeData) - token: ' + token + ', user.data.segment: ', udSegment);
89
+ let data = {
90
+ rtd: {
91
+ ortb2: {
92
+ user: {
93
+ data: [
94
+ udSegment
95
+ ]
96
+ },
97
+ site: {
98
+ ext: {
99
+ data: {
100
+ dapSAID: membership.said
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ };
107
+ storage.setDataInLocalStorage(SEGMENTS_STORAGE_KEY, JSON.stringify(data));
108
+ onDone();
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Module init
115
+ * @param {Object} provider
116
+ * @param {Object} userConsent
117
+ * @return {boolean}
118
+ */
119
+ function init(provider, userConsent) {
120
+ return true;
121
+ }
122
+
123
+ /** @type {RtdSubmodule} */
124
+ export const akamaiDapRtdSubmodule = {
125
+ name: SUBMODULE_NAME,
126
+ getBidRequestData: getRealTimeData,
127
+ init: init
128
+ };
129
+
130
+ submodule(MODULE_NAME, akamaiDapRtdSubmodule);
131
+
132
+ export const dapUtils = {
133
+
134
+ dapGetToken: function(config, identity, ttl) {
135
+ let now = Math.round(Date.now() / 1000.0); // in seconds
136
+ let storageName = 'async_dap_token';
137
+ let token = null;
138
+
139
+ if (ttl == 0) {
140
+ localStorage.removeItem(storageName);
141
+ }
142
+
143
+ let item = JSON.parse(localStorage.getItem(storageName));
144
+ if (item == null) {
145
+ item = {
146
+ expires_at: now - 1,
147
+ token: null
148
+ };
149
+ } else {
150
+ token = item.token;
151
+ }
152
+
153
+ if (now > item.expires_at) {
154
+ dapUtils.dapLog('Token missing or expired, fetching a new one...');
155
+ // Trigger a refresh
156
+ let configAsync = {...config};
157
+ dapUtils.dapTokenize(configAsync, identity,
158
+ function(token, status, xhr) {
159
+ item.expires_at = now + ttl;
160
+ item.token = token;
161
+ localStorage.setItem(storageName, JSON.stringify(item));
162
+ dapUtils.dapLog('Successfully updated and stored token; expires in ' + ttl + ' seconds');
163
+ let deviceId100 = xhr.getResponseHeader('Akamai-DAP-100');
164
+ if (deviceId100 != null) {
165
+ localStorage.setItem('dap_deviceId100', deviceId100);
166
+ dapUtils.dapLog('Successfully stored DAP 100 Device ID: ' + deviceId100);
167
+ }
168
+ },
169
+ function(xhr, status, error) {
170
+ logError('ERROR(' + error + '): failed to retrieve token! ' + status);
171
+ }
172
+ );
173
+ }
174
+
175
+ return token;
176
+ },
177
+
178
+ dapGetMembership: function(config, token) {
179
+ let now = Math.round(Date.now() / 1000.0); // in seconds
180
+ let storageName = 'async_dap_membership';
181
+ let maxTtl = 3600; // if the cached membership is older than this, return null
182
+ let membership = null;
183
+ let item = JSON.parse(localStorage.getItem(storageName));
184
+ if (item == null || (now - item.expires_at) > maxTtl) {
185
+ item = {
186
+ expires_at: now - 1,
187
+ said: null,
188
+ cohorts: null,
189
+ attributes: null
190
+ };
191
+ } else {
192
+ membership = {
193
+ said: item.said,
194
+ cohorts: item.cohorts,
195
+ attributes: null
196
+ };
197
+ }
198
+
199
+ // Always refresh the cached membership.
200
+ let configAsync = {...config};
201
+ dapUtils.dapMembership(configAsync, token,
202
+ function(membership, status, xhr) {
203
+ item.expires_at = now + maxTtl;
204
+ item.said = membership.said;
205
+ item.cohorts = membership.cohorts;
206
+ localStorage.setItem(storageName, JSON.stringify(item));
207
+ dapUtils.dapLog('Successfully updated and stored membership:');
208
+ dapUtils.dapLog(item);
209
+ },
210
+ function(xhr, status, error) {
211
+ logError('ERROR(' + error + '): failed to retrieve membership! ' + status);
212
+ }
213
+ );
214
+
215
+ return membership;
216
+ },
217
+
218
+ /**
219
+ * DESCRIPTION
220
+ *
221
+ * Convert a DAP membership response to an OpenRTB2 segment object suitable
222
+ * for insertion into user.data.segment or site.data.segment.
223
+ */
224
+ dapMembershipToRtbSegment: function(membership, config) {
225
+ let segment = {
226
+ name: 'dap.akamai.com',
227
+ ext: {
228
+ 'segtax': config.segtax
229
+ },
230
+ segment: []
231
+ };
232
+ if (membership != null) {
233
+ for (const i of membership.cohorts) {
234
+ segment.segment.push({ id: i });
235
+ }
236
+ }
237
+ return segment;
238
+ },
239
+
240
+ dapLog: function(args) {
241
+ let css = '';
242
+ css += 'display: inline-block;';
243
+ css += 'color: #fff;';
244
+ css += 'background: #F28B20;';
245
+ css += 'padding: 1px 4px;';
246
+ css += 'border-radius: 3px';
247
+
248
+ logInfo('%cDAP Client', css, args);
249
+ },
250
+
251
+ /*******************************************************************************
252
+ *
253
+ * V2 (And Beyond) API
254
+ *
255
+ ******************************************************************************/
256
+
257
+ /**
258
+ * SYNOPSIS
259
+ *
260
+ * dapTokenize( config, identity );
261
+ *
262
+ * DESCRIPTION
263
+ *
264
+ * Tokenize an identity into a secure, privacy safe pseudonymiziation.
265
+ *
266
+ * PARAMETERS
267
+ *
268
+ * config: an array of system configuration parameters
269
+ *
270
+ * identity: an array of identity parameters passed to the tokenizing system
271
+ *
272
+ * EXAMPLE
273
+ *
274
+ * config = {
275
+ * api_hostname: "prebid.dap.akadns.net", // required
276
+ * domain: "prebid.org", // required
277
+ * api_version: "x1", // optional, default "x1"
278
+ * };
279
+ *
280
+ * token = null;
281
+ * identity_email = {
282
+ * type: "email",
283
+ * identity: "obiwan@jedi.com"
284
+ * attributes: { cohorts: [ "100:1641013200", "101:1641013200", "102":3:1641013200" ] },
285
+ * };
286
+ * dap_x1_tokenize( config, identity_email,
287
+ * function( response, status, xhr ) { token = response; },
288
+ * function( xhr, status, error ) { ; } // handle error
289
+ *
290
+ * token = null;
291
+ * identity_signature = { type: "signature:1.0.0" };
292
+ * dap_x1_tokenize( config, identity_signature,
293
+ * function( response, status, xhr } { token = response; },
294
+ * function( xhr, status, error ) { ; } // handle error
295
+ */
296
+ dapTokenize: function(config, identity, onSuccess = null, onError = null) {
297
+ if (onError == null) {
298
+ onError = function(xhr, status, error) {};
299
+ }
300
+
301
+ if (config == null || typeof (config) == typeof (undefined)) {
302
+ onError(null, 'Invalid config object', 'ClientError');
303
+ return;
304
+ }
305
+
306
+ if (typeof (config.domain) != 'string') {
307
+ onError(null, 'Invalid config.domain: must be a string', 'ClientError');
308
+ return;
309
+ }
310
+
311
+ if (config.domain.length <= 0) {
312
+ onError(null, 'Invalid config.domain: must have non-zero length', 'ClientError');
313
+ return;
314
+ }
315
+
316
+ if (!('api_version' in config) || (typeof (config.api_version) == 'string' && config.api_version.length == 0)) {
317
+ config.api_version = 'x1';
318
+ }
319
+
320
+ if (typeof (config.api_version) != 'string') {
321
+ onError(null, "Invalid api_version: must be a string like 'x1', etc.", 'ClientError');
322
+ return;
323
+ }
324
+
325
+ if (!(('api_hostname') in config) || typeof (config.api_hostname) != 'string' || config.api_hostname.length == 0) {
326
+ onError(null, 'Invalid api_hostname: must be a non-empty string', 'ClientError');
327
+ return;
328
+ }
329
+
330
+ if (identity == null || typeof (identity) == typeof (undefined)) {
331
+ onError(null, 'Invalid identity object', 'ClientError');
332
+ return;
333
+ }
334
+
335
+ if (!('type' in identity) || typeof (identity.type) != 'string' || identity.type.length <= 0) {
336
+ onError(null, "Identity must contain a valid 'type' field", 'ClientError');
337
+ return;
338
+ }
339
+
340
+ let apiParams = {
341
+ 'type': identity.type,
342
+ };
343
+
344
+ if (typeof (identity.identity) != typeof (undefined)) {
345
+ apiParams.identity = identity.identity;
346
+ }
347
+ if (typeof (identity.attributes) != typeof (undefined)) {
348
+ apiParams.attributes = identity.attributes;
349
+ }
350
+
351
+ let method;
352
+ let body;
353
+ let path;
354
+ switch (config.api_version) {
355
+ case 'x1':
356
+ case 'x1-dev':
357
+ method = 'POST';
358
+ path = '/data-activation/' + config.api_version + '/domain/' + config.domain + '/identity/tokenize';
359
+ body = JSON.stringify(apiParams);
360
+ break;
361
+ default:
362
+ onError(null, 'Invalid api_version: ' + config.api_version, 'ClientError');
363
+ return;
364
+ }
365
+
366
+ let url = 'https://' + config.api_hostname + path;
367
+ let cb = {
368
+ success: (response, request) => {
369
+ let token = null;
370
+ switch (config.api_version) {
371
+ case 'x1':
372
+ case 'x1-dev':
373
+ token = request.getResponseHeader('Akamai-DAP-Token');
374
+ break;
375
+ }
376
+ onSuccess(token, request.status, request);
377
+ },
378
+ error: (request, error) => {
379
+ onError(request, request.statusText, error);
380
+ }
381
+ };
382
+
383
+ ajax(url, cb, body, {
384
+ method: method,
385
+ customHeaders: {
386
+ 'Content-Type': 'application/json',
387
+ 'Pragma': 'akamai-x-cache-on'
388
+ }
389
+ });
390
+ },
391
+
392
+ /**
393
+ * SYNOPSIS
394
+ *
395
+ * dapMembership( config, token, onSuccess, onError );
396
+ *
397
+ * DESCRIPTION
398
+ *
399
+ * Return the audience segment membership along with a new Secure Advertising
400
+ * ID for this token.
401
+ *
402
+ * PARAMETERS
403
+ *
404
+ * config: an array of system configuration parameters
405
+ *
406
+ * token: the token previously returned from the tokenize API
407
+ *
408
+ * EXAMPLE
409
+ *
410
+ * config = {
411
+ * api_hostname: 'api.dap.akadns.net',
412
+ * };
413
+ *
414
+ * // token from dap_x1_tokenize
415
+ *
416
+ * dapMembership( config, token,
417
+ * function( membership, status, xhr ) {
418
+ * // Run auction with membership.segments and membership.said
419
+ * },
420
+ * function( xhr, status, error ) {
421
+ * // error
422
+ * } );
423
+ *
424
+ */
425
+ dapMembership: function(config, token, onSuccess = null, onError = null) {
426
+ if (onError == null) {
427
+ onError = function(xhr, status, error) {};
428
+ }
429
+
430
+ if (config == null || typeof (config) == typeof (undefined)) {
431
+ onError(null, 'Invalid config object', 'ClientError');
432
+ return;
433
+ }
434
+
435
+ if (!('api_version' in config) || (typeof (config.api_version) == 'string' && config.api_version.length == 0)) {
436
+ config.api_version = 'x1';
437
+ }
438
+
439
+ if (typeof (config.api_version) != 'string') {
440
+ onError(null, "Invalid api_version: must be a string like 'x1', etc.", 'ClientError');
441
+ return;
442
+ }
443
+
444
+ if (!(('api_hostname') in config) || typeof (config.api_hostname) != 'string' || config.api_hostname.length == 0) {
445
+ onError(null, 'Invalid api_hostname: must be a non-empty string', 'ClientError');
446
+ return;
447
+ }
448
+
449
+ if (token == null || typeof (token) != 'string') {
450
+ onError(null, 'Invalid token: must be a non-null string', 'ClientError');
451
+ return;
452
+ }
453
+ let path = '/data-activation/' +
454
+ config.api_version +
455
+ '/token/' + token +
456
+ '/membership';
457
+
458
+ let url = 'https://' + config.api_hostname + path;
459
+
460
+ let cb = {
461
+ success: (response, request) => {
462
+ onSuccess(JSON.parse(response), request.status, request);
463
+ },
464
+ error: (error, request) => {
465
+ onError(request, request.status, error);
466
+ }
467
+ };
468
+
469
+ ajax(url, cb, undefined, {
470
+ method: 'GET',
471
+ customHeaders: {}
472
+ });
473
+ }
474
+ }
@@ -0,0 +1,47 @@
1
+ ### Overview
2
+
3
+ Akamai DAP Real time data Provider automatically invokes the DAP APIs and submit audience segments and the SAID to the bid-stream.
4
+
5
+ ### Integration
6
+
7
+ 1) Build the akamaiDapRTD module into the Prebid.js package with:
8
+
9
+ ```
10
+ gulp build --modules=akamaiDapRtdProvider,...
11
+ ```
12
+
13
+ 2) Use `setConfig` to instruct Prebid.js to initilaize the akamaiDapRtdProvider module, as specified below.
14
+
15
+ ### Configuration
16
+
17
+ ```
18
+ pbjs.setConfig({
19
+ realTimeData: {
20
+ dataProviders: [
21
+ {
22
+ name: "dap",
23
+ waitForIt: true,
24
+ params: {
25
+ apiHostname: '<see your Akamai account rep>',
26
+ apiVersion: "x1",
27
+ domain: 'your-domain.com',
28
+ identityType: 'email' | 'mobile' | ... | 'dap-signature:1.0.0',
29
+ segtax: <Akamai_taxonomy_name>,
30
+ tokenTtl: 5,
31
+ }
32
+ }
33
+ ]
34
+ }
35
+ });
36
+ ```
37
+
38
+ Please reach out to your Akamai account representative(Prebid@akamai.com) to get provisioned on the DAP platform.
39
+
40
+
41
+ ### Testing
42
+ To view an example of available segments returned by dap:
43
+ ```
44
+ ‘gulp serve --modules=rtdModule,akamaiDapRtdProvider,appnexusBidAdapter,sovrnBidAdapter’
45
+ ```
46
+ and then point your browser at:
47
+ "http://localhost:9999/integrationExamples/gpt/akamaidap_segments_example.html"
@@ -598,6 +598,10 @@ function newBid(serverBid, rtbBid, bidderRequest) {
598
598
  bid.meta = Object.assign({}, bid.meta, { advertiserId: rtbBid.advertiser_id });
599
599
  }
600
600
 
601
+ if (rtbBid.brand_id) {
602
+ bid.meta = Object.assign({}, bid.meta, { brandId: rtbBid.brand_id });
603
+ }
604
+
601
605
  if (rtbBid.rtb.video) {
602
606
  // shared video properties used for all 3 contexts
603
607
  Object.assign(bid, {
@@ -696,9 +700,11 @@ function newBid(serverBid, rtbBid, bidderRequest) {
696
700
  });
697
701
  try {
698
702
  if (rtbBid.rtb.trackers) {
699
- const url = rtbBid.rtb.trackers[0].impression_urls[0];
700
- const tracker = createTrackPixelHtml(url);
701
- bid.ad += tracker;
703
+ for (let i = 0; i < rtbBid.rtb.trackers[0].impression_urls.length; i++) {
704
+ const url = rtbBid.rtb.trackers[0].impression_urls[i];
705
+ const tracker = createTrackPixelHtml(url);
706
+ bid.ad += tracker;
707
+ }
702
708
  }
703
709
  } catch (error) {
704
710
  logError('Error appending tracking pixel', error);