• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / #290

03 Apr 2025 06:15PM UTC coverage: 90.188% (-0.3%) from 90.449%
#290

push

travis-ci

prebidjs-release
Prebid 9.38.0 release

42568 of 53450 branches covered (79.64%)

63111 of 69977 relevant lines covered (90.19%)

173.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.74
/modules/byDataAnalyticsAdapter.js
1
import { deepClone, logInfo, logError } from '../src/utils.js';
1✔
2
import Base64 from 'crypto-js/enc-base64';
3
import hmacSHA512 from 'crypto-js/hmac-sha512';
4
import enc from 'crypto-js/enc-utf8';
5
import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js';
6
import { EVENTS, BID_STATUS } from '../src/constants.js';
7
import adapterManager from '../src/adapterManager.js';
8
import {getStorageManager} from '../src/storageManager.js';
9
import { auctionManager } from '../src/auctionManager.js';
10
import { ajax } from '../src/ajax.js';
11
import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js';
12

13
const versionCode = '4.4.1'
1✔
14
const secretKey = 'bydata@123456'
1✔
15
const { NO_BID, BID_TIMEOUT, AUCTION_END, AUCTION_INIT, BID_WON } = EVENTS
1✔
16
const DEFAULT_EVENT_URL = 'https://pbjs-stream.bydata.com/topics/prebid'
1✔
17
const analyticsType = 'endpoint'
1✔
18
const isBydata = isKeyInUrl('bydata_debug')
1✔
19
const adunitsMap = {}
1✔
20
const MODULE_CODE = 'bydata';
1✔
21
const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_CODE});
1✔
22

23
let initOptions = {}
1✔
24
var payload = {}
1✔
25
var winPayload = {}
1✔
26
var isDataSend = window.asc_data || false
1✔
27
var bdNbTo = { 'to': [], 'nb': [] }
1✔
28

29
/* method used for testing parameters */
30
function isKeyInUrl(name) {
31
  const queryString = window.location.search;
1✔
32
  const urlParams = new URLSearchParams(queryString);
1✔
33
  const param = urlParams.get(name)
1✔
34
  return param
1✔
35
}
36

37
/* return ad unit full path wrt custom ad unit code */
38
function getAdunitName(code) {
39
  var name = code;
3✔
40
  for (const [key, value] of Object.entries(adunitsMap)) {
3✔
41
    if (key === code) { name = value; }
×
42
  }
43
  return name;
3✔
44
}
45

46
/* EVENT: auction init */
47
function onAuctionStart(t) {
48
  /* map of ad unit code - ad unit full path */
49
  t.adUnits && t.adUnits.length && t.adUnits.forEach((adu) => {
315✔
50
    const { code, adunit } = adu
354✔
51
    adunitsMap[code] = adunit
354✔
52
  });
53
}
54

55
/* EVENT: bid timeout */
56
function onBidTimeout(t) {
57
  if (payload['visitor_data'] && t && t.length > 0) {
47✔
58
    bdNbTo['to'] = t
33✔
59
  }
60
}
61

62
/* EVENT: no bid */
63
function onNoBidData(t) {
64
  if (payload['visitor_data'] && t) {
159!
65
    bdNbTo['nb'].push(t)
159✔
66
  }
67
}
68

69
/* EVENT: bid won */
70
function onBidWon(t) {
71
  const { isCorrectOption } = initOptions
221✔
72
  if (isCorrectOption && (isDataSend || isBydata)) {
221!
73
    ascAdapter.getBidWonData(t)
×
74
    ascAdapter.sendPayload(winPayload)
×
75
  }
76
}
77

78
/* EVENT: auction end */
79
function onAuctionEnd(t) {
80
  const { isCorrectOption } = initOptions;
356✔
81
  setTimeout(() => {
356✔
82
    if (isCorrectOption && (isDataSend || isBydata)) {
336!
83
      ascAdapter.dataProcess(t);
×
84
      ascAdapter.sendPayload(payload);
×
85
    }
86
  }, 500);
87
}
88

89
const ascAdapter = Object.assign(adapter({ url: DEFAULT_EVENT_URL, analyticsType: analyticsType }), {
1✔
90
  track({ eventType, args }) {
91
    switch (eventType) {
3,931✔
92
      case AUCTION_INIT:
93
        onAuctionStart(args);
315✔
94
        break;
315✔
95
      case NO_BID:
96
        onNoBidData(args);
159✔
97
        break;
159✔
98
      case BID_TIMEOUT:
99
        onBidTimeout(args);
47✔
100
        break;
47✔
101
      case AUCTION_END:
102
        onAuctionEnd(args);
356✔
103
        break;
356✔
104
      case BID_WON:
105
        onBidWon(args);
221✔
106
        break;
221✔
107
      default:
108
        break;
2,833✔
109
    }
110
  }
111
});
112

113
// save the base class function
114
ascAdapter.originEnableAnalytics = ascAdapter.enableAnalytics;
1✔
115
// override enableAnalytics so we can get access to the config passed in from the page
116
ascAdapter.enableAnalytics = function (config) {
1✔
117
  if (this.initConfig(config)) {
4✔
118
    initOptions.isCorrectOption && ascAdapter.getVisitorData();
2✔
119
    ascAdapter.originEnableAnalytics(config);
2✔
120
  }
121
};
122

123
ascAdapter.initConfig = function (config) {
1✔
124
  let isCorrectOption = true;
4✔
125
  initOptions = {};
4✔
126
  var rndNum = Math.floor(Math.random() * 10000 + 1);
4✔
127
  initOptions.options = deepClone(config.options);
4✔
128
  initOptions.clientId = initOptions.options.clientId || null;
4✔
129
  initOptions.logFrequency = initOptions.options.logFrequency;
4✔
130
  if (!initOptions.clientId) {
4✔
131
    _logError('"options.clientId" should not empty!!');
2✔
132
    isCorrectOption = false;
2✔
133
  }
134
  if (rndNum <= initOptions.logFrequency) { window.asc_data = isDataSend = true; }
4!
135
  initOptions.isCorrectOption = isCorrectOption;
4✔
136
  this.initOptions = initOptions;
4✔
137
  return isCorrectOption;
4✔
138
};
139

140
ascAdapter.getBidWonData = function(t) {
1✔
141
  const { auctionId, adUnitCode, size, requestId, bidder, timeToRespond, currency, mediaType, cpm } = t
1✔
142
  const aun = getAdunitName(adUnitCode)
1✔
143
  winPayload['aid'] = auctionId
1✔
144
  winPayload['as'] = '';
1✔
145
  winPayload['auctionData'] = [];
1✔
146
  var data = {}
1✔
147
  data['au'] = aun
1✔
148
  data['auc'] = adUnitCode
1✔
149
  data['aus'] = size
1✔
150
  data['bid'] = requestId
1✔
151
  data['bidadv'] = bidder
1✔
152
  data['br_pb_mg'] = cpm
1✔
153
  data['br_tr'] = timeToRespond
1✔
154
  data['bradv'] = bidder
1✔
155
  data['brid'] = requestId
1✔
156
  data['brs'] = size
1✔
157
  data['cur'] = currency
1✔
158
  data['inb'] = 0
1✔
159
  data['ito'] = 0
1✔
160
  data['ipwb'] = 1
1✔
161
  data['iwb'] = 1
1✔
162
  data['mt'] = mediaType
1✔
163
  winPayload['auctionData'].push(data)
1✔
164
  return winPayload
1✔
165
}
166

167
ascAdapter.getVisitorData = function (data = {}) {
1✔
168
  var ua = data.uid ? data : {};
3✔
169
  var module = {
3✔
170
    options: [],
171
    header: [window.navigator.platform, window.navigator.userAgent, window.navigator.appVersion, window.navigator.vendor, window.opera],
172
    dataos: [
173
      { name: 'Windows Phone', value: 'Windows Phone', version: 'OS' },
174
      { name: 'Windows', value: 'Win', version: 'NT' },
175
      { name: 'iPhone', value: 'iPhone', version: 'OS' },
176
      { name: 'iPad', value: 'iPad', version: 'OS' },
177
      { name: 'Kindle', value: 'Silk', version: 'Silk' },
178
      { name: 'Android', value: 'Android', version: 'Android' },
179
      { name: 'PlayBook', value: 'PlayBook', version: 'OS' },
180
      { name: 'BlackBerry', value: 'BlackBerry', version: '/' },
181
      { name: 'Macintosh', value: 'Mac', version: 'OS X' },
182
      { name: 'Linux', value: 'Linux', version: 'rv' },
183
      { name: 'Palm', value: 'Palm', version: 'PalmOS' }
184
    ],
185
    databrowser: [
186
      { name: 'Chrome', value: 'Chrome', version: 'Chrome' },
187
      { name: 'Firefox', value: 'Firefox', version: 'Firefox' },
188
      { name: 'Safari', value: 'Safari', version: 'Version' },
189
      { name: 'Internet Explorer', value: 'MSIE', version: 'MSIE' },
190
      { name: 'Opera', value: 'Opera', version: 'Opera' },
191
      { name: 'BlackBerry', value: 'CLDC', version: 'CLDC' },
192
      { name: 'Mozilla', value: 'Mozilla', version: 'Mozilla' }
193
    ],
194
    init: function () { var agent = this.header.join(' '); var os = this.matchItem(agent, this.dataos); var browser = this.matchItem(agent, this.databrowser); return { os: os, browser: browser }; },
3✔
195
    matchItem: function (string, data) {
196
      var i = 0; var j = 0; var regex; var regexv; var match; var matches; var version;
6✔
197
      for (i = 0; i < data.length; i += 1) {
6✔
198
        regex = new RegExp(data[i].value, 'i');
33✔
199
        match = regex.test(string);
33✔
200
        if (match) {
33✔
201
          regexv = new RegExp(data[i].version + '[- /:;]([\\d._]+)', 'i');
6✔
202
          matches = string.match(regexv);
6✔
203
          version = '';
6✔
204
          if (matches) { if (matches[1]) { matches = matches[1]; } }
6!
205
          if (matches) {
6✔
206
            matches = matches.split(/[._]+/);
3✔
207
            for (j = 0; j < matches.length; j += 1) {
3✔
208
              if (j === 0) {
12✔
209
                version += matches[j] + '.';
3✔
210
              } else {
211
                version += matches[j];
9✔
212
              }
213
            }
214
          } else {
215
            version = '0';
3✔
216
          }
217
          return {
6✔
218
            name: data[i].name,
219
            version: parseFloat(version)
220
          };
221
        }
222
      }
223
      return { name: 'unknown', version: 0 };
×
224
    }
225
  };
226

227
  function generateUid() {
228
    try {
1✔
229
      var buffer = new Uint8Array(16);
1✔
230
      crypto.getRandomValues(buffer);
1✔
231
      buffer[6] = (buffer[6] & ~176) | 64;
1✔
232
      buffer[8] = (buffer[8] & ~64) | 128;
1✔
233
      var hex = Array.prototype.map.call(new Uint8Array(buffer), function (x) {
1✔
234
        return ('00' + x.toString(16)).slice(-2);
16✔
235
      }).join('');
236
      return hex.slice(0, 5) + '-' + hex.slice(5, 9) + '-' + hex.slice(9, 13) + '-' + hex.slice(13, 18);
1✔
237
    } catch (e) {
238
      return '';
×
239
    }
240
  }
241
  function base64url(source) {
242
    var encodedSource = Base64.stringify(source);
9✔
243
    encodedSource = encodedSource.replace(/=+$/, '');
9✔
244
    encodedSource = encodedSource.replace(/\+/g, '-');
9✔
245
    encodedSource = encodedSource.replace(/\//g, '_');
9✔
246
    return encodedSource;
9✔
247
  }
248
  function getJWToken(data) {
249
    var header = {
3✔
250
      'alg': 'HS256',
251
      'typ': 'JWT'
252
    };
253
    var stringifiedHeader = enc.parse(JSON.stringify(header));
3✔
254
    var encodedHeader = base64url(stringifiedHeader);
3✔
255
    var stringifiedData = enc.parse(JSON.stringify(data));
3✔
256
    var encodedData = base64url(stringifiedData);
3✔
257
    var token = encodedHeader + '.' + encodedData;
3✔
258
    var signature = hmacSHA512(token, secretKey);
3✔
259
    signature = base64url(signature);
3✔
260
    var signedToken = token + '.' + signature;
3✔
261
    return signedToken;
3✔
262
  }
263
  function detectWidth() {
264
    return window.screen.width || (window.innerWidth && document.documentElement.clientWidth) ? Math.min(window.innerWidth, document.documentElement.clientWidth) : window.innerWidth || document.documentElement.clientWidth || document.getElementsByTagName('body')[0].clientWidth;
3!
265
  }
266
  function giveDeviceTypeOnScreenSize() {
267
    var _dWidth = detectWidth();
3✔
268
    return _dWidth > 1024 ? 'Desktop' : (_dWidth <= 1024 && _dWidth >= 768) ? 'Tablet' : 'Mobile';
3!
269
  }
270

271
  const { clientId } = initOptions;
3✔
272
  var userId = storage.getDataFromLocalStorage('userId');
3✔
273
  if (!userId) {
3✔
274
    userId = generateUid();
1✔
275
    storage.setDataInLocalStorage('userId', userId);
1✔
276
  }
277
  var screenSize = { width: window.screen.width, height: window.screen.height };
3✔
278
  var deviceType = giveDeviceTypeOnScreenSize();
3✔
279
  var e = module.init();
3✔
280
  if (!ua['uid']) {
3✔
281
    ua['uid'] = userId;
2✔
282
    ua['cid'] = clientId;
2✔
283
    ua['pid'] = window.location.hostname;
2✔
284
    ua['os'] = e.os.name;
2✔
285
    ua['osv'] = e.os.version;
2✔
286
    ua['br'] = e.browser.name;
2✔
287
    ua['brv'] = e.browser.version;
2✔
288
    ua['ss'] = screenSize;
2✔
289
    ua['de'] = deviceType;
2✔
290
    ua['tz'] = window.Intl.DateTimeFormat().resolvedOptions().timeZone;
2✔
291
  }
292
  var signedToken = getJWToken(ua);
3✔
293
  payload['visitor_data'] = signedToken;
3✔
294
  winPayload['visitor_data'] = signedToken;
3✔
295
  return signedToken;
3✔
296
}
297

298
ascAdapter.dataProcess = function (t) {
1✔
299
  if (isBydata) { payload['bydata_debug'] = 'true'; }
1!
300
  _logInfo('fulldata - ', t);
1✔
301
  payload['aid'] = t.auctionId;
1✔
302
  payload['as'] = t.timestamp;
1✔
303
  payload['auctionData'] = [];
1✔
304
  var bidderRequestsData = []; var bidsReceivedData = [];
1✔
305
  t.bidderRequests && t.bidderRequests.forEach(bidReq => {
1✔
306
    var pObj = {}; pObj['bids'] = [];
1✔
307
    bidReq.bids.forEach(bid => {
1✔
308
      var data = {};
1✔
309
      data['adUnitCode'] = bid.adUnitCode;
1✔
310
      data['sizes'] = bid.sizes;
1✔
311
      data['bidder'] = bid.bidder;
1✔
312
      data['bidId'] = bid.bidId;
1✔
313
      data['mediaTypes'] = [];
1✔
314
      var mt = bid.mediaTypes.banner ? 'display' : 'video';
1!
315
      data['mediaTypes'].push(mt);
1✔
316
      pObj['bids'].push(data);
1✔
317
    })
318
    bidderRequestsData.push(pObj);
1✔
319
  });
320
  t.bidsReceived && t.bidsReceived.forEach(bid => {
1✔
321
    const { requestId, bidder, width, height, cpm, currency, timeToRespond, adUnitCode } = bid;
×
322
    bidsReceivedData.push({ requestId, bidder, width, height, cpm, currency, timeToRespond, adUnitCode });
×
323
  });
324
  bidderRequestsData.length > 0 && bidderRequestsData.forEach(bdObj => {
1✔
325
    var bdsArray = bdObj['bids'];
1✔
326
    bdsArray.forEach(bid => {
1✔
327
      const { adUnitCode, sizes, bidder, bidId, mediaTypes } = bid;
1✔
328
      sizes.forEach(size => {
1✔
329
        var sstr = size[0] + 'x' + size[1]
2✔
330
        payload['auctionData'].push({ au: getAdunitName(adUnitCode), auc: adUnitCode, aus: sstr, mt: mediaTypes[0], bidadv: bidder, bid: bidId, inb: 0, ito: 0, ipwb: 0, iwb: 0 });
2✔
331
      });
332
    });
333
  });
334

335
  bidsReceivedData.length > 0 && bidsReceivedData.forEach(bdRecived => {
1!
336
    const { requestId, bidder, width, height, cpm, currency, timeToRespond } = bdRecived;
×
337
    payload['auctionData'].forEach(rwData => {
×
338
      if (rwData['bid'] === requestId && rwData['aus'] === width + 'x' + height) {
×
339
        rwData['brid'] = requestId; rwData['bradv'] = bidder; rwData['br_pb_mg'] = cpm;
×
340
        rwData['cur'] = currency; rwData['br_tr'] = timeToRespond; rwData['brs'] = width + 'x' + height;
×
341
      }
342
    })
343
  });
344

345
  var prebidWinningBids = auctionManager.getBidsReceived().filter(bid => bid.status === BID_STATUS.BID_TARGETING_SET);
1✔
346
  prebidWinningBids && prebidWinningBids.length > 0 && prebidWinningBids.forEach(pbbid => {
1!
347
    payload['auctionData'] && payload['auctionData'].forEach(rwData => {
×
348
      if (rwData['bid'] === pbbid.requestId && rwData['brs'] === pbbid.size) {
×
349
        rwData['ipwb'] = 1;
×
350
      }
351
    });
352
  })
353

354
  var winningBids = auctionManager.getAllWinningBids();
1✔
355
  winningBids && winningBids.length > 0 && winningBids.forEach(wBid => {
1!
356
    payload['auctionData'] && payload['auctionData'].forEach(rwData => {
×
357
      if (rwData['bid'] === wBid.requestId && rwData['brs'] === wBid.size) {
×
358
        rwData['iwb'] = 1;
×
359
      }
360
    });
361
  })
362

363
  payload['auctionData'] && payload['auctionData'].length > 0 && payload['auctionData'].forEach(u => {
1✔
364
    bdNbTo['to'].forEach(i => {
2✔
365
      if (u.bid === i.bidId) u.ito = 1;
2!
366
    });
367
    bdNbTo['nb'].forEach(i => {
2✔
368
      if (u.bidadv === i.bidder && u.bid === i.bidId) { u.inb = 1; }
2!
369
    })
370
  });
371
  return payload;
1✔
372
}
373

374
ascAdapter.sendPayload = function (data) {
1✔
375
  var obj = { 'records': [{ 'value': data }] };
×
376
  let strJSON = JSON.stringify(obj);
×
377
  sendDataOnKf(strJSON);
×
378
}
379

380
function sendDataOnKf(dataObj) {
381
  ajax(DEFAULT_EVENT_URL, {
×
382
    success: function () {
383
      _logInfo('send data success');
×
384
    },
385
    error: function (e) {
386
      _logInfo('send data error', e);
×
387
    }
388
  }, dataObj, {
389
    contentType: 'application/vnd.kafka.json.v2+json',
390
    method: 'POST',
391
    withCredentials: true
392
  });
393
}
394

395
adapterManager.registerAnalyticsAdapter({
1✔
396
  adapter: ascAdapter,
397
  code: MODULE_CODE,
398
});
399

400
function _logInfo(message, meta) {
401
  logInfo(buildLogMessage(message), meta);
1✔
402
}
403

404
function _logError(message) {
405
  logError(buildLogMessage(message));
2✔
406
}
407

408
function buildLogMessage(message) {
409
  return 'Bydata Prebid Analytics ' + versionCode + ':' + message;
3✔
410
}
411

412
export default ascAdapter;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc