From 0eba11e360324c2c41a3373db548890f91437370 Mon Sep 17 00:00:00 2001 From: Reda Guermas Date: Wed, 4 Mar 2020 15:39:35 -0800 Subject: [PATCH 01/14] Enable supplyChain support --- modules/nobidBidAdapter.js | 11 +++++- test/spec/modules/nobidBidAdapter_spec.js | 47 ++++++++++++++++++++++- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index cce79efead0..8cb8885549d 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -2,7 +2,7 @@ import * as utils from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'nobid'; -window.nobidVersion = '1.2.1'; +window.nobidVersion = '1.2.3'; window.nobid = window.nobid || {}; window.nobid.bidResponses = window.nobid.bidResponses || {}; window.nobid.timeoutTotal = 0; @@ -57,6 +57,12 @@ function nobidBuildRequests(bids, bidderRequest) { } return uspConsent; } + var schain = function(bids) { + if (bids && bids.length>0) { + return bids[0].schain + } + return null; + } var topLocation = function(bidderRequest) { var ret = ''; if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { @@ -99,6 +105,7 @@ function nobidBuildRequests(bids, bidderRequest) { state['ref'] = document.referrer; state['gdpr'] = gdprConsent(bidderRequest); state['usp'] = uspConsent(bidderRequest); + state['schain'] = schain(bids); return state; } function newAdunit(adunitObject, adunits) { @@ -269,7 +276,7 @@ export const spec = { var buildEndpoint = function() { return resolveEndpoint() + 'adreq?cb=' + Math.floor(Math.random() * 11000); } - log('buildRequests', validBidRequests); + log('validBidRequests', validBidRequests); if (!validBidRequests || validBidRequests.length <= 0) { log('Empty validBidRequests'); return; diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 0005f35523e..91c398b48ad 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -70,7 +70,7 @@ describe('Nobid Adapter', function () { refererInfo: {referer: REFERER} } - it('should add source and verison to the tag', function () { + it('should add source and version to the tag', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.sid).to.equal(SITE_ID); @@ -281,6 +281,51 @@ describe('Nobid Adapter', function () { }); }); + describe('buildRequestsWithSupplyChain', function () { + const SITE_ID = 2; + let bidRequests = [ + { + bidder: 'nobid', + params: { + siteId: SITE_ID + }, + adUnitCode: 'adunit-code', + sizes: [[300, 250]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + schain: { + validation: 'strict', + config: { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'indirectseller.com', + sid: '00001', + name: 'name.com', + hp: 1 + } + ] + } + } + } + ]; + + it('schain exist', function () { + const request = spec.buildRequests(bidRequests); + const payload = JSON.parse(request.data); + expect(payload.schain).to.exist; + expect(payload.schain.validation).to.exist.and.to.equal('strict'); + expect(payload.schain.config.ver).to.exist.and.to.equal('1.0'); + expect(payload.schain.config.complete).to.exist.and.to.equal(1); + expect(payload.schain.config.nodes[0].asi).to.exist.and.to.equal('indirectseller.com'); + expect(payload.schain.config.nodes[0].sid).to.exist.and.to.equal('00001'); + expect(payload.schain.config.nodes[0].name).to.exist.and.to.equal('name.com'); + expect(payload.schain.config.nodes[0].hp).to.exist.and.to.equal(1); + }); + }); + describe('interpretResponseWithUserLimit', function () { const CREATIVE_ID_300x250 = 'CREATIVE-100'; const ADUNIT_300x250 = 'ADUNIT-1'; From c0fb8ee705b3525b386684ebd6484278eb08a003 Mon Sep 17 00:00:00 2001 From: Reda Guermas Date: Wed, 4 Mar 2020 20:10:10 -0800 Subject: [PATCH 02/14] Added support for COPPA --- modules/nobidBidAdapter.js | 19 ++++++++++++++++--- test/spec/modules/nobidBidAdapter_spec.js | 13 +++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 8cb8885549d..7fca2d10534 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -1,4 +1,5 @@ import * as utils from '../src/utils.js'; +import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; const BIDDER_CODE = 'nobid'; @@ -58,11 +59,20 @@ function nobidBuildRequests(bids, bidderRequest) { return uspConsent; } var schain = function(bids) { - if (bids && bids.length>0) { + if (bids && bids.length > 0) { return bids[0].schain } return null; - } + } + var coppa = function() { + if (config.getConfig('coppa') === true) { + return {'coppa': true}; + } + if (bids && bids.length > 0) { + return bids[0].coppa + } + return null; + } var topLocation = function(bidderRequest) { var ret = ''; if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { @@ -105,7 +115,10 @@ function nobidBuildRequests(bids, bidderRequest) { state['ref'] = document.referrer; state['gdpr'] = gdprConsent(bidderRequest); state['usp'] = uspConsent(bidderRequest); - state['schain'] = schain(bids); + const sch = schain(bids); + if (sch) state['schain'] = sch; + const cop = coppa(); + if (cop) state['coppa'] = cop; return state; } function newAdunit(adunitObject, adunits) { diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 91c398b48ad..8d119461d6a 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -2,6 +2,7 @@ import { expect } from 'chai'; import * as utils from 'src/utils.js'; import { spec } from 'modules/nobidBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as bidderFactory from 'src/adapters/bidderFactory.js'; describe('Nobid Adapter', function () { const adapter = newBidder(spec); @@ -294,6 +295,7 @@ describe('Nobid Adapter', function () { bidId: '30b31c1838de1e', bidderRequestId: '22edbae2733bf6', auctionId: '1d1a030790a475', + coppa: true, schain: { validation: 'strict', config: { @@ -323,6 +325,10 @@ describe('Nobid Adapter', function () { expect(payload.schain.config.nodes[0].sid).to.exist.and.to.equal('00001'); expect(payload.schain.config.nodes[0].name).to.exist.and.to.equal('name.com'); expect(payload.schain.config.nodes[0].hp).to.exist.and.to.equal(1); + expect(payload.coppa).to.exist; + expect(payload.coppa).to.exist.and.to.be.true; + expect(payload.a).to.be.lengthOf(1); + expect(request.method).to.equal('POST'); }); }); @@ -404,6 +410,13 @@ describe('Nobid Adapter', function () { expect(pixel.length).to.equal(0); }); + it('should get correct user sync when !iframeEnabled and pixelEnabled', function () { + let pixel = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{body: {syncs: ['sync_url']}}]) + expect(pixel.length).to.equal(1); + expect(pixel[0].type).to.equal('image'); + expect(pixel[0].url).to.equal('sync_url'); + }); + it('should get correct user sync when !iframeEnabled', function () { let pixel = spec.getUserSyncs({}) expect(pixel.length).to.equal(0); From c11a5b3862da1389405bd5a48195398d14d438ea Mon Sep 17 00:00:00 2001 From: Reda Guermas Date: Sun, 27 Sep 2020 16:49:46 -0700 Subject: [PATCH 03/14] rebuilt --- package-lock.json | 47 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4f3d2120d72..23809c767e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "3.27.0-pre", + "version": "4.5.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -7796,13 +7796,22 @@ "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" + "object.assign": "^4.1.0" + }, + "dependencies": { + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + } } }, "es-array-method-boxes-properly": { @@ -7838,6 +7847,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -12076,7 +12086,7 @@ "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "has": "^1.0.3" } }, "is-relative": { @@ -17484,7 +17494,8 @@ "object-inspect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true }, "object-is": { "version": "1.1.2", @@ -20696,26 +20707,6 @@ "es-abstract": "^1.17.0-next.1" } }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", From 9c8440cb0b03dfd964830e46e4413c926ff1a7a0 Mon Sep 17 00:00:00 2001 From: Reda Guermas Date: Mon, 28 Sep 2020 09:34:10 -0700 Subject: [PATCH 04/14] Added support for Extended User IDs. --- modules/nobidBidAdapter.js | 21 ++++++- test/spec/modules/nobidBidAdapter_spec.js | 69 +++++++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 00cb14dc01d..051202cab97 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const storage = getStorageManager(); const BIDDER_CODE = 'nobid'; -window.nobidVersion = '1.2.8'; +window.nobidVersion = '1.2.9'; window.nobid = window.nobid || {}; window.nobid.bidResponses = window.nobid.bidResponses || {}; window.nobid.timeoutTotal = 0; @@ -114,6 +114,23 @@ function nobidBuildRequests(bids, bidderRequest) { utils.logWarn('Could not parse screen dimensions, error details:', e); } } + var getEIDs = function(eids) { + if (utils.isArray(eids) && eids.length > 0) { + let src = []; + eids.forEach((eid) => { + let ids = []; + if (eid.uids) { + eid.uids.forEach(value => { + ids.push({'id': value.id + ''}); + }); + } + if (eid.source && ids.length > 0) { + src.push({source: eid.source, uids: ids}); + } + }); + return src; + } + } var state = {}; state['sid'] = siteId; state['l'] = topLocation(bidderRequest); @@ -131,6 +148,8 @@ function nobidBuildRequests(bids, bidderRequest) { if (sch) state['schain'] = sch; const cop = coppa(); if (cop) state['coppa'] = cop; + const eids = getEIDs(utils.deepAccess(bids, '0.userIdAsEids')); + if (eids && eids.length > 0) state['eids'] = eids; return state; } function newAdunit(adunitObject, adunits) { diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 346356e7d5b..e67d3b41f1d 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -228,6 +228,75 @@ describe('Nobid Adapter', function () { }); }); + describe('buildRequestsEIDs', function () { + const SITE_ID = 2; + const REFERER = 'https://www.examplereferer.com'; + let bidRequests = [ + { + 'bidder': 'nobid', + 'params': { + 'siteId': SITE_ID + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'userIdAsEids': [ + { + 'source': 'criteo.com', + 'uids': [ + { + 'id': 'CRITEO_ID', + 'atype': 1 + } + ] + }, + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': 'ID5_ID', + 'atype': 1 + } + ], + 'ext': { + 'linkType': 0 + } + }, + { + 'source': 'adserver.org', + 'uids': [ + { + 'id': 'TD_ID', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + } + ] + } + ] + } + ]; + + let bidderRequest = { + refererInfo: {referer: REFERER} + } + + it('should criteo eid', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.sid).to.exist.and.to.equal(2); + expect(payload.eids[0].source).to.exist.and.to.equal('criteo.com'); + expect(payload.eids[0].uids[0].id).to.exist.and.to.equal('CRITEO_ID'); + expect(payload.eids[1].source).to.exist.and.to.equal('id5-sync.com'); + expect(payload.eids[1].uids[0].id).to.exist.and.to.equal('ID5_ID'); + expect(payload.eids[2].source).to.exist.and.to.equal('adserver.org'); + expect(payload.eids[2].uids[0].id).to.exist.and.to.equal('TD_ID'); + }); + }); + describe('buildRequests', function () { const SITE_ID = 2; const REFERER = 'https://www.examplereferer.com'; From ff10bbe56edb9b72f31592e2a8d8efa1e144281a Mon Sep 17 00:00:00 2001 From: Reda Guermas Date: Thu, 27 May 2021 18:53:15 -0700 Subject: [PATCH 05/14] Added support for the "meta" attribute in bid response. --- modules/nobidBidAdapter.js | 5 ++- test/spec/modules/nobidBidAdapter_spec.js | 54 +++++++++++++++++------ 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 051202cab97..3aabd8f0635 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const storage = getStorageManager(); const BIDDER_CODE = 'nobid'; -window.nobidVersion = '1.2.9'; +window.nobidVersion = '1.3.0'; window.nobid = window.nobid || {}; window.nobid.bidResponses = window.nobid.bidResponses || {}; window.nobid.timeoutTotal = 0; @@ -298,6 +298,9 @@ function nobidInterpretResponse(response, bidRequest) { if (bid.videoCacheKey) { bidResponse.videoCacheKey = bid.videoCacheKey; } + if (bid.meta) { + bidResponse.meta = bid.meta; + } bidResponses.push(bidResponse); } diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 5916c7fc291..c44b0ce3fc2 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -257,21 +257,12 @@ describe('Nobid Adapter', function () { 'uids': [ { 'id': 'ID5_ID', -<<<<<<< HEAD 'atype': 1 } ], 'ext': { 'linkType': 0 } -======= - 'atype': 1, - 'ext': { - 'linkType': 0 - } - } - ] ->>>>>>> f2befaab4211240897413082724fe6958984af53 }, { 'source': 'adserver.org', @@ -293,11 +284,7 @@ describe('Nobid Adapter', function () { refererInfo: {referer: REFERER} } -<<<<<<< HEAD it('should criteo eid', function () { -======= - it('should get user ids from eids', function () { ->>>>>>> f2befaab4211240897413082724fe6958984af53 const request = spec.buildRequests(bidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.sid).to.exist.and.to.equal(2); @@ -611,6 +598,47 @@ describe('Nobid Adapter', function () { }); }); + describe('interpretResponseWithMeta', function () { + const CREATIVE_ID_300x250 = 'CREATIVE-100'; + const ADUNIT_300x250 = 'ADUNIT-1'; + const ADMARKUP_300x250 = 'ADMARKUP-300x250'; + const PRICE_300x250 = 0.51; + const REQUEST_ID = '3db3773286ee59'; + const DEAL_ID = 'deal123'; + const ADOMAINS = ['adomain1', 'adomain2']; + let response = { + country: 'US', + ip: '68.83.15.75', + device: 'COMPUTER', + site: 2, + bids: [ + {id: 1, + bdrid: 101, + divid: ADUNIT_300x250, + dealid: DEAL_ID, + creativeid: CREATIVE_ID_300x250, + size: {'w': 300, 'h': 250}, + adm: ADMARKUP_300x250, + price: '' + PRICE_300x250, + meta: { + advertiserDomains: ADOMAINS + } + } + ] + }; + + it('should meta.advertiserDomains be respected', function () { + let bidderRequest = { + bids: [{ + bidId: REQUEST_ID, + adUnitCode: ADUNIT_300x250 + }] + } + let result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + expect(result[0].meta.advertiserDomains).to.equal(ADOMAINS); + }); + }); + describe('buildRequestsWithSupplyChain', function () { const SITE_ID = 2; let bidRequests = [ From 388d3e0144d8dcf948e4ca17835abad50571cf85 Mon Sep 17 00:00:00 2001 From: redaguermas Date: Thu, 27 Jul 2023 18:04:17 -0700 Subject: [PATCH 06/14] Delete nobidBidAdapter.js.orig --- modules/nobidBidAdapter.js.orig | 467 -------------------------------- 1 file changed, 467 deletions(-) delete mode 100644 modules/nobidBidAdapter.js.orig diff --git a/modules/nobidBidAdapter.js.orig b/modules/nobidBidAdapter.js.orig deleted file mode 100644 index 859094dadcb..00000000000 --- a/modules/nobidBidAdapter.js.orig +++ /dev/null @@ -1,467 +0,0 @@ -import * as utils from '../src/utils.js'; -import { config } from '../src/config.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { getStorageManager } from '../src/storageManager.js'; - -const storage = getStorageManager(); -const BIDDER_CODE = 'nobid'; -window.nobidVersion = '1.2.9'; -window.nobid = window.nobid || {}; -window.nobid.bidResponses = window.nobid.bidResponses || {}; -window.nobid.timeoutTotal = 0; -window.nobid.bidWonTotal = 0; -window.nobid.refreshCount = 0; -function log(msg, obj) { - utils.logInfo('-NoBid- ' + msg, obj) -} -function nobidSetCookie(cname, cvalue, hours) { - var d = new Date(); - d.setTime(d.getTime() + (hours * 60 * 60 * 1000)); - var expires = 'expires=' + d.toUTCString(); - storage.setCookie(cname, cvalue, expires); -} -function nobidGetCookie(cname) { - return storage.getCookie(cname); -} -function nobidHasPurpose1Consent(bidderRequest) { - let result = true; - if (bidderRequest && bidderRequest.gdprConsent) { - if (bidderRequest.gdprConsent.gdprApplies && bidderRequest.gdprConsent.apiVersion === 2) { - result = !!(utils.deepAccess(bidderRequest.gdprConsent, 'vendorData.purpose.consents.1') === true); - } - } - return result; -} -function nobidBuildRequests(bids, bidderRequest) { - var serializeState = function(divIds, siteId, adunits) { - var filterAdUnitsByIds = function(divIds, adUnits) { - var filtered = []; - if (!divIds || !divIds.length) { - filtered = adUnits; - } else if (adUnits) { - var a = []; - if (!(divIds instanceof Array)) a.push(divIds); - else a = divIds; - for (var i = 0, l = adUnits.length; i < l; i++) { - var adUnit = adUnits[i]; - if (adUnit && adUnit.d && (a.indexOf(adUnit.d) > -1)) { - filtered.push(adUnit); - } - } - } - return filtered; - } - var gdprConsent = function(bidderRequest) { - var gdprConsent = {}; - if (bidderRequest && bidderRequest.gdprConsent) { - gdprConsent = { - consentString: bidderRequest.gdprConsent.consentString, - // will check if the gdprApplies field was populated with a boolean value (ie from page config). If it's undefined, then default to true - consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : false - } - } - return gdprConsent; - } - var uspConsent = function(bidderRequest) { - var uspConsent = ''; - if (bidderRequest && bidderRequest.uspConsent) { - uspConsent = bidderRequest.uspConsent; - } - return uspConsent; - } - var schain = function(bids) { - if (bids && bids.length > 0) { - return bids[0].schain - } - return null; - } - var coppa = function() { - if (config.getConfig('coppa') === true) { - return {'coppa': true}; - } - if (bids && bids.length > 0) { - return bids[0].coppa - } - return null; - } - var topLocation = function(bidderRequest) { - var ret = ''; - if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) { - ret = bidderRequest.refererInfo.referer; - } else { - ret = (window.context && window.context.location && window.context.location.href) ? window.context.location.href : document.location.href; - } - return encodeURIComponent(ret.replace(/\%/g, '')); - } - var timestamp = function() { - var date = new Date(); - var zp = function (val) { return (val <= 9 ? '0' + val : '' + val); } - var d = date.getDate(); - var y = date.getFullYear(); - var m = date.getMonth() + 1; - var h = date.getHours(); - var min = date.getMinutes(); - var s = date.getSeconds(); - return '' + y + '-' + zp(m) + '-' + zp(d) + ' ' + zp(h) + ':' + zp(min) + ':' + zp(s); - }; - var clientDim = function() { - try { - var width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); - var height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); - return `${width}x${height}`; - } catch (e) { - utils.logWarn('Could not parse screen dimensions, error details:', e); - } - } - var getEIDs = function(eids) { - if (utils.isArray(eids) && eids.length > 0) { - let src = []; - eids.forEach((eid) => { - let ids = []; - if (eid.uids) { - eid.uids.forEach(value => { - ids.push({'id': value.id + ''}); - }); - } - if (eid.source && ids.length > 0) { - src.push({source: eid.source, uids: ids}); - } - }); - return src; - } - } - var state = {}; - state['sid'] = siteId; - state['l'] = topLocation(bidderRequest); - state['tt'] = encodeURIComponent(document.title); - state['tt'] = state['tt'].replace(/'|;|quot;|39;|&|&|#|\r\n|\r|\n|\t|\f|\%0A|\"|\%22|\%5C|\%23|\%26|\%26|\%09/gm, ''); - state['a'] = filterAdUnitsByIds(divIds, adunits || []); - state['t'] = timestamp(); - state['tz'] = Math.round(new Date().getTimezoneOffset()); - state['r'] = clientDim(); - state['lang'] = (navigator.languages && navigator.languages[0]) || navigator.language || navigator.userLanguage; - state['ref'] = document.referrer; - state['gdpr'] = gdprConsent(bidderRequest); - state['usp'] = uspConsent(bidderRequest); - const sch = schain(bids); - if (sch) state['schain'] = sch; - const cop = coppa(); - if (cop) state['coppa'] = cop; -<<<<<<< HEAD - const eids = getEIDs(utils.deepAccess(bids, '0.userIdAsEids')); - if (eids && eids.length > 0) state['eids'] = eids; -======= ->>>>>>> Added support for COPPA - return state; - } - function newAdunit(adunitObject, adunits) { - var getAdUnit = function(divid, adunits) { - for (var i = 0; i < adunits.length; i++) { - if (adunits[i].d === divid) { - return adunits[i]; - } - } - return false; - } - var removeByAttrValue = function(array, attribute, value) { - for (var i = array.length - 1; i >= 0; i--) { - var entry = array[i]; - if (entry[attribute] && entry[attribute] === value) { - array.splice(i, 1); - } - } - } - var a = getAdUnit(adunitObject.div, adunits) || {}; - if (adunitObject.account) { - a.s = adunitObject.account; - } - if (adunitObject.sizes) { - a.z = adunitObject.sizes; - } - if (adunitObject.div) { - a.d = adunitObject.div; - } - if (adunitObject.targeting) { - a.g = adunitObject.targeting; - } else { - a.g = {}; - } - if (adunitObject.div) { - removeByAttrValue(adunits, 'd', adunitObject.div); - } - if (adunitObject.sizeMapping) { - a.sm = adunitObject.sizeMapping; - } - if (adunitObject.siteId) { - a.sid = adunitObject.siteId; - } - if (adunitObject.placementId) { - a.pid = adunitObject.placementId; - } - if (adunitObject.ad_type) { - a.at = adunitObject.ad_type; - } - if (adunitObject.params) { - a.params = adunitObject.params; - } - adunits.push(a); - return adunits; - } - if (typeof window.nobid.refreshLimit !== 'undefined') { - if (window.nobid.refreshLimit < window.nobid.refreshCount) return false; - } - let ublock = nobidGetCookie('_ublock'); - if (ublock) { - log('Request blocked for user. hours: ', ublock); - return false; - } - /* DISCOVER SLOTS */ - var divids = []; - var siteId = 0; - var adunits = []; - for (var i = 0; i < bids.length; i++) { - var bid = bids[i]; - var divid = bid.adUnitCode; - divids.push(divid); - var sizes = bid.sizes; - siteId = (typeof bid.params['siteId'] != 'undefined' && bid.params['siteId']) ? bid.params['siteId'] : siteId; - var placementId = bid.params['placementId']; - - var adType = 'banner'; - const videoMediaType = utils.deepAccess(bid, 'mediaTypes.video'); - const context = utils.deepAccess(bid, 'mediaTypes.video.context'); - if (bid.mediaType === VIDEO || (videoMediaType && (context === 'instream' || context === 'outstream'))) { - adType = 'video'; - } - - if (siteId) { - newAdunit({ - div: divid, - sizes: sizes, - siteId: siteId, - placementId: placementId, - ad_type: adType, - params: bid.params - }, - adunits); - } - } - if (siteId) { - return serializeState(divids, siteId, adunits); - } else { - return false; - } -} -function nobidInterpretResponse(response, bidRequest) { - var findBid = function(divid, bids) { - for (var i = 0; i < bids.length; i++) { - if (bids[i].adUnitCode == divid) { - return bids[i]; - } - } - return false; - } - var setRefreshLimit = function(response) { - if (response && typeof response.rlimit !== 'undefined') window.nobid.refreshLimit = response.rlimit; - } - var setUserBlock = function(response) { - if (response && typeof response.ublock !== 'undefined') { - nobidSetCookie('_ublock', '1', response.ublock); - } - } - setRefreshLimit(response); - setUserBlock(response); - var bidResponses = []; - for (var i = 0; response.bids && i < response.bids.length; i++) { - var bid = response.bids[i]; - if (bid.bdrid < 100 || !bidRequest || !bidRequest.bidderRequest || !bidRequest.bidderRequest.bids) continue; - window.nobid.bidResponses['' + bid.id] = bid; - var reqBid = findBid(bid.divid, bidRequest.bidderRequest.bids); - if (!reqBid) continue; - const bidResponse = { - requestId: reqBid.bidId, - cpm: 1 * ((bid.price) ? bid.price : (bid.bucket) ? bid.bucket : 0), - width: bid.size.w, - height: bid.size.h, - creativeId: (bid.creativeid) || '', - dealId: (bid.dealid) || '', - currency: 'USD', - netRevenue: true, - ttl: 300, - ad: bid.adm, - mediaType: bid.atype || BANNER - }; - if (bid.vastUrl) { - bidResponse.vastUrl = bid.vastUrl; - } - if (bid.vastXml) { - bidResponse.vastXml = bid.vastXml; - } - if (bid.videoCacheKey) { - bidResponse.videoCacheKey = bid.videoCacheKey; - } - if (bid.meta) { - bidResponse.meta = bid.meta; - } - bidResponses.push(bidResponse); - } - return bidResponses; -}; -window.nobid.renderTag = function(doc, id, win) { - log('nobid.renderTag()', id); - var bid = window.nobid.bidResponses['' + id]; - if (bid && bid.adm2) { - log('nobid.renderTag() found tag', id); - var markup = bid.adm2; - doc.write(markup); - doc.close(); - return; - } - log('nobid.renderTag() tag NOT FOUND *ERROR*', id); -} -window.addEventListener('message', function (event) { - let key = event.message ? 'message' : 'data'; - var msg = '' + event[key]; - if (msg.substring(0, 'nbTagRenderer.requestAdMarkup|'.length) === 'nbTagRenderer.requestAdMarkup|') { - log('Prebid received nbTagRenderer.requestAdMarkup event'); - var adId = msg.substring(msg.indexOf('|') + 1); - if (window.nobid && window.nobid.bidResponses) { - var bid = window.nobid.bidResponses['' + adId]; - if (bid && bid.adm2) { - var markup = bid.adm2; - if (markup) { - event.source.postMessage('nbTagRenderer.renderAdInSafeFrame|' + markup, '*'); - } - } - } - } -}, false); -export const spec = { - code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: function(bid) { - log('isBidRequestValid', bid); - return !!bid.params.siteId; - }, - /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function(validBidRequests, bidderRequest) { - function resolveEndpoint() { - var ret = 'https://ads.servenobid.com/'; - var env = (typeof utils.getParameterByName === 'function') && (utils.getParameterByName('nobid-env')); - if (!env) ret = 'https://ads.servenobid.com/'; - else if (env == 'beta') ret = 'https://beta.servenobid.com/'; - else if (env == 'dev') ret = '//localhost:8282/'; - else if (env == 'qa') ret = 'https://qa-ads.nobid.com/'; - return ret; - } - var buildEndpoint = function() { - return resolveEndpoint() + 'adreq?cb=' + Math.floor(Math.random() * 11000); - } - log('validBidRequests', validBidRequests); - if (!validBidRequests || validBidRequests.length <= 0) { - log('Empty validBidRequests'); - return; - } - const payload = nobidBuildRequests(validBidRequests, bidderRequest); - if (!payload) return; - window.nobid.refreshCount++; - const payloadString = JSON.stringify(payload).replace(/'|&|#/g, '') - const endpoint = buildEndpoint(); - - let options = {}; - if (!nobidHasPurpose1Consent(bidderRequest)) { - options = { withCredentials: false }; - } - - return { - method: 'POST', - url: endpoint, - data: payloadString, - bidderRequest, - options - }; - }, - /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function(serverResponse, bidRequest) { - log('interpretResponse -> serverResponse', serverResponse); - log('interpretResponse -> bidRequest', bidRequest); - return nobidInterpretResponse(serverResponse.body, bidRequest); - }, - - /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, usPrivacy) { - if (syncOptions.iframeEnabled) { - let params = ''; - if (gdprConsent && typeof gdprConsent.consentString === 'string') { - // add 'gdpr' only if 'gdprApplies' is defined - if (typeof gdprConsent.gdprApplies === 'boolean') { - params += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; - } else { - params += `?gdpr_consent=${gdprConsent.consentString}`; - } - } - if (usPrivacy) { - if (params.length > 0) params += '&'; - else params += '?'; - params += 'usp_consent=' + usPrivacy; - } - return [{ - type: 'iframe', - url: 'https://public.servenobid.com/sync.html' + params - }]; - } else if (syncOptions.pixelEnabled && serverResponses.length > 0) { - let syncs = []; - if (serverResponses[0].body.syncs && serverResponses[0].body.syncs.length > 0) { - serverResponses[0].body.syncs.forEach(element => { - syncs.push({ - type: 'image', - url: element - }); - }) - } - return syncs; - } else { - utils.logWarn('-NoBid- Please enable iframe based user sync.', syncOptions); - return []; - } - }, - - /** - * Register bidder specific code, which will execute if bidder timed out after an auction - * @param {data} Containing timeout specific data - */ - onTimeout: function(data) { - window.nobid.timeoutTotal++; - log('Timeout total: ' + window.nobid.timeoutTotal, data); - return window.nobid.timeoutTotal; - }, - onBidWon: function(data) { - window.nobid.bidWonTotal++; - log('BidWon total: ' + window.nobid.bidWonTotal, data); - return window.nobid.bidWonTotal; - } -} -registerBidder(spec); From 1c3f8534324de58733798fa2de76496e2c4cf6a7 Mon Sep 17 00:00:00 2001 From: redaguermas Date: Thu, 27 Jul 2023 18:04:48 -0700 Subject: [PATCH 07/14] Delete a --- a | 41 ----------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 a diff --git a/a b/a deleted file mode 100644 index 0192e94942c..00000000000 --- a/a +++ /dev/null @@ -1,41 +0,0 @@ -[16:58:25] Using gulpfile ~/Documents/work/nobid/Prebid.js/gulpfile.js -[16:58:25] Starting 'test-coverage'... -[16:58:25] Starting 'clean'... -[16:58:25] Finished 'clean' after 10 ms -[16:58:25] Starting 'testCoverage'... - -START: -ℹ 「wdm」: -ℹ 「wdm」: Compiled successfully. -ℹ 「wdm」: Compiling... -ℹ 「wdm」: Failed to compile. -26 08 2020 16:58:47.749:INFO [karma-server]: Karma v4.4.1 server started at http://0.0.0.0:9876/ -26 08 2020 16:58:47.750:INFO [launcher]: Launching browsers ChromeHeadless with concurrency 5 -26 08 2020 16:58:47.755:INFO [launcher]: Starting browser ChromeHeadless -26 08 2020 16:58:48.077:INFO [HeadlessChrome 84.0.4147 (Mac OS X 10.15.4)]: Connected on socket P5D7-7TxrFvaFTdvAAAA with id 42175759 -26 08 2020 16:58:50.097:WARN [reporter]: SourceMap position not found for trace: Uncaught Error: Cannot find module "src/adloader.js" -at http://localhost:9876/base/test/test_index.js?dc5396d26193ca2528d42dc43b9d6782966d7e60:11837:1 - -Error: Cannot find module "src/adloader.js" - at Object. (http://localhost:9876/base/test/test_index.js?dc5396d26193ca2528d42dc43b9d6782966d7e60:11837:7) - at __webpack_require__ (http://localhost:9876/base/test/test_index.js?dc5396d26193ca2528d42dc43b9d6782966d7e60:20:30) - at Object. (http://localhost:9876/base/test/test_index.js?dc5396d26193ca2528d42dc43b9d6782966d7e60:31966:1) - at __webpack_require__ (http://localhost:9876/base/test/test_index.js?dc5396d26193ca2528d42dc43b9d6782966d7e60:20:30) - at http://localhost:9876/base/test/test_index.js?dc5396d26193ca2528d42dc43b9d6782966d7e60:63:18 - at http://localhost:9876/base/test/test_index.js?dc5396d26193ca2528d42dc43b9d6782966d7e60:66:10 -HeadlessChrome 84.0.4147 (Mac OS X 10.15.4) ERROR - Uncaught Error: Cannot find module "src/adloader.js" - at webpack:///test/mocks/adloaderStub.js:2:1 <- test/test_index.js:11837:1 - - Error: Cannot find module "src/adloader.js" - at Object. (webpack:///test/mocks/adloaderStub.js:2:1 <- test/test_index.js:11837:7) - at __webpack_require__ (webpack:///webpack/bootstrap%20a27aaf5067d3fb4ce44d:19:1 <- test/test_index.js:20:30) - at Object. (webpack:///test/test_index.js:2:1 <- test/test_index.js:31966:1) - at __webpack_require__ (webpack:///webpack/bootstrap%20a27aaf5067d3fb4ce44d:19:1 <- test/test_index.js:20:30) - at webpack:///webpack/bootstrap%20a27aaf5067d3fb4ce44d:62:1 <- test/test_index.js:63:18 - at test/test_index.js:66:10 - -Finished in 2.032 secs / 0 secs @ 16:58:50 GMT-0700 (Pacific Daylight Time) - -SUMMARY: -✔ 0 tests completed From 38dc58a346314a019164af342b26e4bec6a7cdc0 Mon Sep 17 00:00:00 2001 From: redaguermas Date: Thu, 27 Jul 2023 18:05:11 -0700 Subject: [PATCH 08/14] Delete .jsdtscope --- .settings/.jsdtscope | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .settings/.jsdtscope diff --git a/.settings/.jsdtscope b/.settings/.jsdtscope deleted file mode 100644 index cca691f6cc0..00000000000 --- a/.settings/.jsdtscope +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - From 2818cbed0673eed639696e1fbf3c87db624acbea Mon Sep 17 00:00:00 2001 From: redaguermas Date: Thu, 27 Jul 2023 18:05:19 -0700 Subject: [PATCH 09/14] Delete org.eclipse.wst.jsdt.ui.superType.container --- .settings/org.eclipse.wst.jsdt.ui.superType.container | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .settings/org.eclipse.wst.jsdt.ui.superType.container diff --git a/.settings/org.eclipse.wst.jsdt.ui.superType.container b/.settings/org.eclipse.wst.jsdt.ui.superType.container deleted file mode 100644 index 49c8cd4f14a..00000000000 --- a/.settings/org.eclipse.wst.jsdt.ui.superType.container +++ /dev/null @@ -1 +0,0 @@ -org.eclipse.wst.jsdt.launching.JRE_CONTAINER \ No newline at end of file From ad8662d7b4e24aadf823dcd144b84f988934108e Mon Sep 17 00:00:00 2001 From: redaguermas Date: Thu, 27 Jul 2023 18:05:27 -0700 Subject: [PATCH 10/14] Delete org.eclipse.wst.jsdt.ui.superType.name --- .settings/org.eclipse.wst.jsdt.ui.superType.name | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .settings/org.eclipse.wst.jsdt.ui.superType.name diff --git a/.settings/org.eclipse.wst.jsdt.ui.superType.name b/.settings/org.eclipse.wst.jsdt.ui.superType.name deleted file mode 100644 index 11006e2a545..00000000000 --- a/.settings/org.eclipse.wst.jsdt.ui.superType.name +++ /dev/null @@ -1 +0,0 @@ -Global \ No newline at end of file From 8ddb9a9a839cd971394c7e7a4f2a7942a92ba7ff Mon Sep 17 00:00:00 2001 From: redaguermas Date: Thu, 27 Jul 2023 18:05:52 -0700 Subject: [PATCH 11/14] Delete .project --- .project | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .project diff --git a/.project b/.project deleted file mode 100644 index c5b02abf6b8..00000000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - Prebid.js - - - - - - org.eclipse.wst.validation.validationbuilder - - - - - - org.eclipse.wst.jsdt.core.jsNature - - From 53de1347263fee9c20a2795b2952e38ce429a788 Mon Sep 17 00:00:00 2001 From: Reda Guermas Date: Wed, 9 Aug 2023 18:34:33 -0700 Subject: [PATCH 12/14] New NoBid Analytics Adapter. --- modules/nobidAnalyticsAdapter.js | 194 +++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 modules/nobidAnalyticsAdapter.js diff --git a/modules/nobidAnalyticsAdapter.js b/modules/nobidAnalyticsAdapter.js new file mode 100644 index 00000000000..9d2dec30d8c --- /dev/null +++ b/modules/nobidAnalyticsAdapter.js @@ -0,0 +1,194 @@ +import {deepClone, logError, getParameterByName} from '../src/utils.js'; +import {ajax} from '../src/ajax.js'; +import {getStorageManager} from '../src/storageManager.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import CONSTANTS from '../src/constants.json'; +import adapterManager from '../src/adapterManager.js'; +import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; + +const VERSION = '1.0.3'; +const MODULE_NAME = 'nobidAnalyticsAdapter'; +const ANALYTICS_DATA_NAME = 'analytics.nobid.io'; +const RETENTION_DAYS = 7; +const TEST_ALLOCATION_PERCENTAGE = 5; // dont block 5% of the time; +window.nobidAnalyticsVersion = VERSION; +const analyticsType = 'endpoint'; +const url = 'localhost:8383/event'; +const GVLID = 816; +const storage = getStorageManager({gvlid: GVLID, moduleName: MODULE_NAME, moduleType: MODULE_TYPE_ANALYTICS}); +const initOptions = {}; +let topLocation; +const { + EVENTS: { + AUCTION_INIT, + BID_REQUESTED, + BID_TIMEOUT, + BID_RESPONSE, + BID_WON, + AUCTION_END, + AD_RENDER_SUCCEEDED + } +} = CONSTANTS; +function log (msg, obj) { + // eslint-disable-next-line no-console + if (obj) console.log(`%cNoBid Analytics ${VERSION}`, 'padding: 2px 8px 2px 8px; background-color:#f50057; color: white', msg, obj); + // eslint-disable-next-line no-console + else console.log(`%cNoBid Analytics ${VERSION}`, 'padding: 2px 8px 2px 8px; background-color:#f50057; color: white', msg); +} +function isJson (str) { + return str && str.startsWith('{') && str.endsWith('}'); +} +function isExpired (data) { + if (data.ts + RETENTION_DAYS * 24 * 3600 * 1000 < Date.now()) return true; + return false; +} +function processServerResponse (response) { + if (!isJson(response)) return; + const resp = JSON.parse(response); + storage.setDataInLocalStorage(ANALYTICS_DATA_NAME, JSON.stringify({ ...resp, ts: Date.now() })); +} +function isAnalyticsDisabled () { + let stored = storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); + if (!isJson(stored)) return false; + stored = JSON.parse(stored); + if (isExpired(stored)) return false; + return stored.disabled; +} +function sendEvent (event, eventType) { + function resolveEndpoint() { + var ret = 'https://carbon-nv.servenobids.com/admin/status'; + var env = (typeof getParameterByName === 'function') && (getParameterByName('nobid-env')); + env = window.location.href.indexOf('nobid-env=dev') > 0 ? 'dev' : env; + if (!env) ret = 'https://carbon-nv.servenobids.com'; + else if (env == 'dev') ret = 'https://localhost:8383'; + return ret; + } + if (!initOptions || !initOptions.options || !initOptions.options.siteId || !event) return; + if (isAnalyticsDisabled()) { + log('NoBid Analytics is Disabled'); + return; + } + try { + const endpoint = `${resolveEndpoint()}/event/${eventType}?pubid=${initOptions.options.siteId}`; + ajax(endpoint, + function (response) { + try { + processServerResponse(response); + } catch (e) { + logError(e); + } + }, + JSON.stringify(event), + { + contentType: 'application/json', + method: 'POST' + } + ); + } catch (err) { + log('Sending event error: ' + err); + } +} +function cleanupObjectAttributes (obj, attributes) { + if (!obj) return; + if (Array.isArray(obj)) { + obj.forEach(item => { + Object.keys(item).forEach(attr => { if (!attributes.includes(attr)) delete item[attr] }); + }); + } else Object.keys(obj).forEach(attr => { if (!attributes.includes(attr)) delete obj[attr] }); +} +function sendBidWonEvent (event, eventType) { + const data = deepClone(event); + cleanupObjectAttributes(data, ['bidderCode', 'size', 'statusMessage', 'adId', 'requestId', 'mediaType', 'adUnitCode', 'cpm', 'timeToRespond']); + if (topLocation) data.topLocation = topLocation; + sendEvent(data, eventType); +} +function sendAuctionEndEvent (event, eventType) { + if (!topLocation && event && event.bidderRequests && event.bidderRequests.length > 0 && event.bidderRequests[0].refererInfo && + event.bidderRequests[0].refererInfo.topmostLocation) topLocation = event.bidderRequests[0].refererInfo.topmostLocation + const data = deepClone(event); + cleanupObjectAttributes(data, ['timestamp', 'timeout', 'auctionId', 'bidderRequests', 'bidsReceived']); + if (data) cleanupObjectAttributes(data.bidderRequests, ['bidderCode', 'bidderRequestId', 'bids', 'refererInfo']); + if (data) cleanupObjectAttributes(data.bidsReceived, ['bidderCode', 'width', 'height', 'adUnitCode', 'statusMessage', 'requestId', 'mediaType', 'cpm']); + if (data) cleanupObjectAttributes(data.noBids, ['bidder', 'sizes', 'bidId']); + if (data.bidderRequests) cleanupObjectAttributes(data.bidderRequests.bids, ['mediaTypes', 'adUnitCode', 'sizes', 'bidId']); + if (data.bidderRequests) cleanupObjectAttributes(data.bidderRequests.refererInfo, ['topmostLocation']); + sendEvent(data, eventType); +} +function auctionInit (event) { + if (!topLocation && event && event.bidderRequests && event.bidderRequests.length > 0 && event.bidderRequests[0].refererInfo && + event.bidderRequests[0].refererInfo.topmostLocation) topLocation = event.bidderRequests[0].refererInfo.topmostLocation +} +let nobidAnalytics = Object.assign(adapter({url, analyticsType}), { + track({ eventType, args }) { + switch (eventType) { + case AUCTION_INIT: + auctionInit(args); + break; + case BID_REQUESTED: + break; + case BID_RESPONSE: + break; + case BID_WON: + sendBidWonEvent(args, eventType); + break; + case BID_TIMEOUT: + break; + case AUCTION_END: + sendAuctionEndEvent(args, eventType); + break; + case AD_RENDER_SUCCEEDED: + break; + default: + break; + } + } +}); + +// save the base class function +nobidAnalytics.originEnableAnalytics = nobidAnalytics.enableAnalytics; +// override enableAnalytics so we can get access to the config passed in from the page +nobidAnalytics.enableAnalytics = function (config) { + initOptions.options = config.options; + if (!config.options.siteId) { + logError('NoBid Analytics - siteId parameter is not defined. Analytics won\'t work'); + return; + } + nobidAnalytics.originEnableAnalytics(config); // call the base class function +}; + +adapterManager.registerAnalyticsAdapter({ + adapter: nobidAnalytics, + code: 'nobidAnalytics', + gvlid: GVLID +}); +window.nobidCarbonizer = { + isActive: function () { + let stored = storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); + if (!isJson(stored)) return false; + stored = JSON.parse(stored); + if (isExpired(stored)) return false; + return stored.carbonizer_active; + }, + carbonizeAdunits (adunits) { + if (this.isActive()) { + // 5% of the time do not block; + if (Math.floor(Math.random() * 101) <= 5) return; + adunits.forEach(adunit => { + this.carbonizeAdunit(adunit); + }); + } + }, + carbonizeAdunit (adunit) { + if (this.isActive()) { + if (Math.floor(Math.random() * 101) <= TEST_ALLOCATION_PERCENTAGE) return; + let stored = storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); + if (!isJson(stored)) return; + stored = JSON.parse(stored); + if (isExpired(stored)) return; + const carbonizerBidders = stored.bidders || []; + const allowedBidders = adunit.bids.filter(rec => carbonizerBidders.includes(rec.bidder)); + adunit.bids = allowedBidders; + } + } +}; +export default nobidAnalytics; From 579edf6ab07d6aa28f54d30359c22f0fdb3466af Mon Sep 17 00:00:00 2001 From: Reda Guermas Date: Mon, 21 Aug 2023 13:22:02 -0700 Subject: [PATCH 13/14] Added NoBid Analytics Adapter unit tests. --- modules/nobidAnalyticsAdapter.js | 119 +++-- .../modules/nobidAnalyticsAdapter_spec.js | 494 ++++++++++++++++++ 2 files changed, 556 insertions(+), 57 deletions(-) create mode 100644 test/spec/modules/nobidAnalyticsAdapter_spec.js diff --git a/modules/nobidAnalyticsAdapter.js b/modules/nobidAnalyticsAdapter.js index 9d2dec30d8c..27ec1cd9451 100644 --- a/modules/nobidAnalyticsAdapter.js +++ b/modules/nobidAnalyticsAdapter.js @@ -6,18 +6,16 @@ import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js'; -const VERSION = '1.0.3'; +const VERSION = '1.0.4'; const MODULE_NAME = 'nobidAnalyticsAdapter'; const ANALYTICS_DATA_NAME = 'analytics.nobid.io'; -const RETENTION_DAYS = 7; +const RETENTION_SECONDS = 7 * 24 * 3600; const TEST_ALLOCATION_PERCENTAGE = 5; // dont block 5% of the time; window.nobidAnalyticsVersion = VERSION; const analyticsType = 'endpoint'; const url = 'localhost:8383/event'; const GVLID = 816; const storage = getStorageManager({gvlid: GVLID, moduleName: MODULE_NAME, moduleType: MODULE_TYPE_ANALYTICS}); -const initOptions = {}; -let topLocation; const { EVENTS: { AUCTION_INIT, @@ -29,31 +27,18 @@ const { AD_RENDER_SUCCEEDED } } = CONSTANTS; -function log (msg, obj) { +function log (msg) { // eslint-disable-next-line no-console - if (obj) console.log(`%cNoBid Analytics ${VERSION}`, 'padding: 2px 8px 2px 8px; background-color:#f50057; color: white', msg, obj); - // eslint-disable-next-line no-console - else console.log(`%cNoBid Analytics ${VERSION}`, 'padding: 2px 8px 2px 8px; background-color:#f50057; color: white', msg); + console.log(`%cNoBid Analytics ${VERSION}`, 'padding: 2px 8px 2px 8px; background-color:#f50057; color: white', msg); } function isJson (str) { return str && str.startsWith('{') && str.endsWith('}'); } -function isExpired (data) { - if (data.ts + RETENTION_DAYS * 24 * 3600 * 1000 < Date.now()) return true; +function isExpired (data, retentionSeconds) { + retentionSeconds = retentionSeconds || RETENTION_SECONDS; + if (data.ts + retentionSeconds * 1000 < Date.now()) return true; return false; } -function processServerResponse (response) { - if (!isJson(response)) return; - const resp = JSON.parse(response); - storage.setDataInLocalStorage(ANALYTICS_DATA_NAME, JSON.stringify({ ...resp, ts: Date.now() })); -} -function isAnalyticsDisabled () { - let stored = storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); - if (!isJson(stored)) return false; - stored = JSON.parse(stored); - if (isExpired(stored)) return false; - return stored.disabled; -} function sendEvent (event, eventType) { function resolveEndpoint() { var ret = 'https://carbon-nv.servenobids.com/admin/status'; @@ -63,17 +48,17 @@ function sendEvent (event, eventType) { else if (env == 'dev') ret = 'https://localhost:8383'; return ret; } - if (!initOptions || !initOptions.options || !initOptions.options.siteId || !event) return; - if (isAnalyticsDisabled()) { + if (!nobidAnalytics.initOptions || !nobidAnalytics.initOptions.siteId || !event) return; + if (nobidAnalytics.isAnalyticsDisabled()) { log('NoBid Analytics is Disabled'); return; } try { - const endpoint = `${resolveEndpoint()}/event/${eventType}?pubid=${initOptions.options.siteId}`; + const endpoint = `${resolveEndpoint()}/event/${eventType}?pubid=${nobidAnalytics.initOptions.siteId}`; ajax(endpoint, function (response) { try { - processServerResponse(response); + nobidAnalytics.processServerResponse(response); } catch (e) { logError(e); } @@ -85,7 +70,7 @@ function sendEvent (event, eventType) { } ); } catch (err) { - log('Sending event error: ' + err); + log(`Sending event error ${err}`); } } function cleanupObjectAttributes (obj, attributes) { @@ -99,13 +84,15 @@ function cleanupObjectAttributes (obj, attributes) { function sendBidWonEvent (event, eventType) { const data = deepClone(event); cleanupObjectAttributes(data, ['bidderCode', 'size', 'statusMessage', 'adId', 'requestId', 'mediaType', 'adUnitCode', 'cpm', 'timeToRespond']); - if (topLocation) data.topLocation = topLocation; + if (nobidAnalytics.topLocation) data.topLocation = nobidAnalytics.topLocation; sendEvent(data, eventType); } function sendAuctionEndEvent (event, eventType) { - if (!topLocation && event && event.bidderRequests && event.bidderRequests.length > 0 && event.bidderRequests[0].refererInfo && - event.bidderRequests[0].refererInfo.topmostLocation) topLocation = event.bidderRequests[0].refererInfo.topmostLocation + if (event?.bidderRequests?.length > 0 && event?.bidderRequests[0]?.refererInfo?.topmostLocation) { + nobidAnalytics.topLocation = event.bidderRequests[0].refererInfo.topmostLocation; + } const data = deepClone(event); + cleanupObjectAttributes(data, ['timestamp', 'timeout', 'auctionId', 'bidderRequests', 'bidsReceived']); if (data) cleanupObjectAttributes(data.bidderRequests, ['bidderCode', 'bidderRequestId', 'bids', 'refererInfo']); if (data) cleanupObjectAttributes(data.bidsReceived, ['bidderCode', 'width', 'height', 'adUnitCode', 'statusMessage', 'requestId', 'mediaType', 'cpm']); @@ -115,8 +102,9 @@ function sendAuctionEndEvent (event, eventType) { sendEvent(data, eventType); } function auctionInit (event) { - if (!topLocation && event && event.bidderRequests && event.bidderRequests.length > 0 && event.bidderRequests[0].refererInfo && - event.bidderRequests[0].refererInfo.topmostLocation) topLocation = event.bidderRequests[0].refererInfo.topmostLocation + if (event?.bidderRequests?.length > 0 && event?.bidderRequests[0]?.refererInfo?.topmostLocation) { + nobidAnalytics.topLocation = event.bidderRequests[0].refererInfo.topmostLocation; + } } let nobidAnalytics = Object.assign(adapter({url, analyticsType}), { track({ eventType, args }) { @@ -144,17 +132,34 @@ let nobidAnalytics = Object.assign(adapter({url, analyticsType}), { } }); -// save the base class function -nobidAnalytics.originEnableAnalytics = nobidAnalytics.enableAnalytics; -// override enableAnalytics so we can get access to the config passed in from the page -nobidAnalytics.enableAnalytics = function (config) { - initOptions.options = config.options; - if (!config.options.siteId) { - logError('NoBid Analytics - siteId parameter is not defined. Analytics won\'t work'); - return; +nobidAnalytics = { + ...nobidAnalytics, + originEnableAnalytics: nobidAnalytics.enableAnalytics, // save the base class function + enableAnalytics: function (config) { // override enableAnalytics so we can get access to the config passed in from the page + if (!config.options.siteId) { + logError('NoBid Analytics - siteId parameter is not defined. Analytics won\'t work'); + return; + } + this.initOptions = config.options; + this.originEnableAnalytics(config); // call the base class function + }, + retentionSeconds: RETENTION_SECONDS, + isExpired (data) { + return isExpired(data, this.retentionSeconds); + }, + isAnalyticsDisabled () { + let stored = storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); + if (!isJson(stored)) return false; + stored = JSON.parse(stored); + if (this.isExpired(stored)) return false; + return stored.disabled; + }, + processServerResponse (response) { + if (!isJson(response)) return; + const resp = JSON.parse(response); + storage.setDataInLocalStorage(ANALYTICS_DATA_NAME, JSON.stringify({ ...resp, ts: Date.now() })); } - nobidAnalytics.originEnableAnalytics(config); // call the base class function -}; +} adapterManager.registerAnalyticsAdapter({ adapter: nobidAnalytics, @@ -162,33 +167,33 @@ adapterManager.registerAnalyticsAdapter({ gvlid: GVLID }); window.nobidCarbonizer = { + getStoredLocalData: function () { + return storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); + }, isActive: function () { let stored = storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); if (!isJson(stored)) return false; stored = JSON.parse(stored); - if (isExpired(stored)) return false; - return stored.carbonizer_active; - }, - carbonizeAdunits (adunits) { - if (this.isActive()) { - // 5% of the time do not block; - if (Math.floor(Math.random() * 101) <= 5) return; - adunits.forEach(adunit => { - this.carbonizeAdunit(adunit); - }); - } + if (isExpired(stored, nobidAnalytics.retentionSeconds)) return false; + return stored.carbonizer_active || false; }, - carbonizeAdunit (adunit) { - if (this.isActive()) { - if (Math.floor(Math.random() * 101) <= TEST_ALLOCATION_PERCENTAGE) return; + carbonizeAdunits: function (adunits, skipTestGroup) { + function carbonizeAdunit (adunit) { let stored = storage.getDataFromLocalStorage(ANALYTICS_DATA_NAME); if (!isJson(stored)) return; stored = JSON.parse(stored); - if (isExpired(stored)) return; + if (isExpired(stored, nobidAnalytics.retentionSeconds)) return; const carbonizerBidders = stored.bidders || []; const allowedBidders = adunit.bids.filter(rec => carbonizerBidders.includes(rec.bidder)); adunit.bids = allowedBidders; } + if (this.isActive()) { + // 5% of the time do not block; + if (!skipTestGroup && Math.floor(Math.random() * 101) <= TEST_ALLOCATION_PERCENTAGE) return; + adunits.forEach(adunit => { + carbonizeAdunit(adunit); + }); + } } }; export default nobidAnalytics; diff --git a/test/spec/modules/nobidAnalyticsAdapter_spec.js b/test/spec/modules/nobidAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..742b4c16abb --- /dev/null +++ b/test/spec/modules/nobidAnalyticsAdapter_spec.js @@ -0,0 +1,494 @@ +import nobidAnalytics from 'modules/nobidAnalyticsAdapter.js'; +import {expect} from 'chai'; +import {server} from 'test/mocks/xhr.js'; +let events = require('src/events'); +let adapterManager = require('src/adapterManager').default; +let constants = require('src/constants.json'); + +const TOP_LOCATION = 'https://www.somesite.com'; +const SITE_ID = 1234; + +describe('NoBid Prebid Analytic', function () { + var clock; + describe('enableAnalytics', function () { + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]); + clock = sinon.useFakeTimers(Date.now()); + }); + + afterEach(function () { + events.getEvents.restore(); + clock.restore(); + }); + + after(function () { + nobidAnalytics.disableAnalytics(); + }); + + it('auctionInit test', function (done) { + const initOptions = { + options: { + /* siteId: SITE_ID */ + } + }; + + nobidAnalytics.enableAnalytics(initOptions); + expect(nobidAnalytics.initOptions).to.equal(undefined); + + initOptions.options.siteId = SITE_ID; + nobidAnalytics.enableAnalytics(initOptions); + expect(nobidAnalytics.initOptions.siteId).to.equal(SITE_ID); + + // Step 1: Initialize adapter + adapterManager.enableAnalytics({ + provider: 'nobid', + options: initOptions + }); + + // Step 2: Send init auction event + events.emit(constants.EVENTS.AUCTION_INIT, {config: initOptions, + auctionId: '13', + timestamp: Date.now(), + bidderRequests: [{refererInfo: {topmostLocation: TOP_LOCATION}}]}); + expect(nobidAnalytics.initOptions).to.have.property('siteId', SITE_ID); + expect(nobidAnalytics).to.have.property('topLocation', TOP_LOCATION); + + const data = { ts: Date.now() }; + clock.tick(5000); + const expired = nobidAnalytics.isExpired(data); + expect(expired).to.equal(false); + + done(); + }); + + it('BID_REQUESTED/BID_RESPONSE/BID_TIMEOUT/AD_RENDER_SUCCEEDED test', function (done) { + const initOptions = { + options: { + siteId: SITE_ID + } + }; + + nobidAnalytics.enableAnalytics(initOptions); + + // Step 1: Initialize adapter + adapterManager.enableAnalytics({ + provider: 'nobid', + options: initOptions + }); + + // Step 2: Send init auction event + events.emit(constants.EVENTS.AUCTION_INIT, {config: initOptions, + auctionId: '13', + timestamp: Date.now(), + bidderRequests: [{refererInfo: {topmostLocation: TOP_LOCATION}}]}); + events.emit(constants.EVENTS.BID_WON, {}); + clock.tick(5000); + expect(server.requests).to.have.length(1); + + events.emit(constants.EVENTS.BID_REQUESTED, {}); + clock.tick(5000); + expect(server.requests).to.have.length(1); + + events.emit(constants.EVENTS.BID_RESPONSE, {}); + clock.tick(5000); + expect(server.requests).to.have.length(1); + + events.emit(constants.EVENTS.BID_TIMEOUT, {}); + clock.tick(5000); + expect(server.requests).to.have.length(1); + + events.emit(constants.EVENTS.AD_RENDER_SUCCEEDED, {}); + clock.tick(5000); + expect(server.requests).to.have.length(1); + + done(); + }); + + it('bidWon test', function (done) { + const initOptions = { + options: { + siteId: SITE_ID + } + }; + + nobidAnalytics.enableAnalytics(initOptions); + + const TOP_LOCATION = 'https://www.somesite.com'; + + const requestIncoming = { + bidderCode: 'nobid', + width: 728, + height: 9, + statusMessage: 'Bid available', + adId: '106d14b7d06b607', + requestId: '67a7f0e7ea55c4', + transactionId: 'd58cbeae-92c8-4262-ba8d-0e649cbf5470', + auctionId: 'd758cce5-d178-408c-b777-8cac605ef7ca', + mediaType: 'banner', + source: 'client', + cpm: 6.4, + creativeId: 'TEST', + dealId: '', + currency: 'USD', + netRevenue: true, + ttl: 300, + ad: 'AD HERE', + meta: { + advertiserDomains: ['advertiser_domain.com'] + }, + metrics: { + 'requestBids.usp': 0 + }, + adapterCode: 'nobid', + originalCpm: 6.44, + originalCurrency: 'USD', + responseTimestamp: 1692156287517, + requestTimestamp: 1692156286972, + bidder: 'nobid', + adUnitCode: 'leaderboard', + timeToRespond: 545, + pbCg: '', + size: '728x90', + adserverTargeting: { + hb_bidder: 'nobid', + hb_adid: '106d14b7d06b607', + hb_pb: '6.40', + hb_size: '728x90', + hb_source: 'client', + hb_format: 'banner', + hb_adomain: 'advertiser_domain.com', + 'hb_crid': 'TEST' + }, + status: 'rendered', + params: [ + { + siteId: SITE_ID + } + ] + }; + + const requestOutgoing = { + bidderCode: 'nobid', + statusMessage: 'Bid available', + adId: '106d14b7d06b607', + requestId: '67a7f0e7ea55c4', + mediaType: 'banner', + cpm: 6.4, + adUnitCode: 'leaderboard', + timeToRespond: 545, + size: '728x90', + topLocation: TOP_LOCATION + }; + + // Step 1: Initialize adapter + adapterManager.enableAnalytics({ + provider: 'nobid', + options: initOptions + }); + + // Step 2: Send init auction event + events.emit(constants.EVENTS.AUCTION_INIT, {config: initOptions, + auctionId: '13', + timestamp: Date.now(), + bidderRequests: [{refererInfo: {topmostLocation: TOP_LOCATION}}]}); + + // Step 3: Send bid won event + events.emit(constants.EVENTS.BID_WON, requestIncoming); + clock.tick(5000); + expect(server.requests).to.have.length(1); + const bidWonRequest = JSON.parse(server.requests[0].requestBody); + expect(bidWonRequest).to.have.property('bidderCode', requestOutgoing.bidderCode); + expect(bidWonRequest).to.have.property('statusMessage', requestOutgoing.statusMessage); + expect(bidWonRequest).to.have.property('adId', requestOutgoing.adId); + expect(bidWonRequest).to.have.property('requestId', requestOutgoing.requestId); + expect(bidWonRequest).to.have.property('mediaType', requestOutgoing.mediaType); + expect(bidWonRequest).to.have.property('cpm', requestOutgoing.cpm); + expect(bidWonRequest).to.have.property('adUnitCode', requestOutgoing.adUnitCode); + expect(bidWonRequest).to.have.property('timeToRespond', requestOutgoing.timeToRespond); + expect(bidWonRequest).to.have.property('size', requestOutgoing.size); + expect(bidWonRequest).to.have.property('topLocation', requestOutgoing.topLocation); + expect(bidWonRequest).to.not.have.property('pbCg'); + + done(); + }); + + it('auctionEnd test', function (done) { + const initOptions = { + options: { + siteId: SITE_ID + } + }; + + nobidAnalytics.enableAnalytics(initOptions); + + const TOP_LOCATION = 'https://www.somesite.com'; + + const requestIncoming = { + auctionId: '4c056b3c-f1a6-46bd-8d82-58c15b22fcfa', + timestamp: 1692224437573, + auctionEnd: 1692224437986, + auctionStatus: 'completed', + adUnits: [ + { + code: 'leaderboard', + sizes: [[728, 90]], + sizeConfig: [ + { minViewPort: [0, 0], sizes: [[300, 250]] }, + { minViewPort: [750, 0], sizes: [[728, 90]] } + ], + adunit: '/111111/adunit', + bids: [{ bidder: 'nobid', params: { siteId: SITE_ID } }] + } + ], + adUnitCodes: ['leaderboard'], + bidderRequests: [ + { + bidderCode: 'nobid', + auctionId: '4c056b3c-f1a6-46bd-8d82-58c15b22fcfa', + bidderRequestId: '5beedb9f99ad98', + bids: [ + { + bidder: 'nobid', + params: { siteId: SITE_ID }, + mediaTypes: { banner: { sizes: [[728, 90]] } }, + adUnitCode: 'leaderboard', + transactionId: 'bcda424d-f4f4-419b-acf9-1808d2dd22b1', + sizes: [[728, 90]], + bidId: '6ef0277f36c8df', + bidderRequestId: '5beedb9f99ad98', + auctionId: '4c056b3c-f1a6-46bd-8d82-58c15b22fcfa', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + ortb2: { + site: { + domain: 'site.me', + publisher: { + domain: 'site.me' + }, + page: TOP_LOCATION + }, + device: { + w: 2605, + h: 895, + dnt: 0, + ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36', + language: 'en', + } + } + } + ], + auctionStart: 1692224437573, + timeout: 3000, + refererInfo: { + topmostLocation: TOP_LOCATION, + location: TOP_LOCATION, + page: TOP_LOCATION, + domain: 'site.me', + ref: null, + } + } + ], + noBids: [ + ], + bidsReceived: [ + { + bidderCode: 'nobid', + width: 728, + height: 90, + statusMessage: 'Bid available', + adId: '95781b6ae5ef2f', + requestId: '6ef0277f36c8df', + transactionId: 'bcda424d-f4f4-419b-acf9-1808d2dd22b1', + auctionId: '4c056b3c-f1a6-46bd-8d82-58c15b22fcfa', + mediaType: 'banner', + source: 'client', + cpm: 6.44, + creativeId: 'TEST', + dealId: '', + currency: 'USD', + netRevenue: true, + ttl: 300, + ad: '', + meta: { + advertiserDomains: [ + 'advertiser_domain.com' + ] + }, + adapterCode: 'nobid', + originalCpm: 6.44, + originalCurrency: 'USD', + responseTimestamp: 1692224437982, + requestTimestamp: 1692224437576, + bidder: 'nobid', + adUnitCode: 'leaderboard', + timeToRespond: 0, + pbLg: 5.00, + pbCg: '', + size: '728x90', + adserverTargeting: { hb_bidder: 'nobid', hb_pb: '6.40' }, + status: 'targetingSet' + } + ], + bidsRejected: [], + winningBids: [], + timeout: 3000 + }; + + const requestOutgoing = { + auctionId: '4c056b3c-f1a6-46bd-8d82-58c15b22fcfa', + bidderRequests: [ + { + bidderCode: 'nobid', + bidderRequestId: '7c1940bb285731', + bids: [ + { + bidder: 'nobid', + params: { siteId: SITE_ID }, + mediaTypes: { banner: { sizes: [[728, 90]] } }, + adUnitCode: 'leaderboard', + sizes: [[728, 90]], + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1 + } + ], + refererInfo: { + topmostLocation: TOP_LOCATION + } + } + ], + bidsReceived: [ + { + bidderCode: 'nobid', + width: 728, + height: 90, + mediaType: 'banner', + cpm: 6.44, + adUnitCode: 'leaderboard' + } + ] + }; + + // Step 1: Initialize adapter + adapterManager.enableAnalytics({ + provider: 'nobid', + options: initOptions + }); + + // Step 2: Send init auction event + events.emit(constants.EVENTS.AUCTION_INIT, {config: initOptions, + auctionId: '13', + timestamp: Date.now(), + bidderRequests: [{refererInfo: {topmostLocation: `${TOP_LOCATION}_something`}}]}); + + // Step 3: Send bid won event + events.emit(constants.EVENTS.AUCTION_END, requestIncoming); + clock.tick(5000); + expect(server.requests).to.have.length(1); + const auctionEndRequest = JSON.parse(server.requests[0].requestBody); + expect(auctionEndRequest).to.have.property('auctionId', requestOutgoing.auctionId); + expect(auctionEndRequest.bidderRequests).to.have.length(1); + expect(auctionEndRequest.bidderRequests[0]).to.have.property('bidderCode', requestOutgoing.bidderRequests[0].bidderCode); + expect(auctionEndRequest.bidderRequests[0].bids).to.have.length(1); + expect(auctionEndRequest.bidderRequests[0].bids[0]).to.have.property('bidder', requestOutgoing.bidderRequests[0].bids[0].bidder); + expect(auctionEndRequest.bidderRequests[0].bids[0]).to.have.property('adUnitCode', requestOutgoing.bidderRequests[0].bids[0].adUnitCode); + expect(auctionEndRequest.bidderRequests[0].bids[0].params).to.have.property('siteId', requestOutgoing.bidderRequests[0].bids[0].params.siteId); + expect(auctionEndRequest.bidderRequests[0].refererInfo).to.have.property('topmostLocation', requestOutgoing.bidderRequests[0].refererInfo.topmostLocation); + + done(); + }); + + it('Analytics disabled test', function (done) { + let disabled; + nobidAnalytics.processServerResponse(JSON.stringify({disabled: false})); + disabled = nobidAnalytics.isAnalyticsDisabled(); + expect(disabled).to.equal(false); + events.emit(constants.EVENTS.AUCTION_END, {auctionId: '1234567890'}); + clock.tick(1000); + expect(server.requests).to.have.length(1); + events.emit(constants.EVENTS.AUCTION_END, {auctionId: '12345678901'}); + clock.tick(1000); + expect(server.requests).to.have.length(2); + + nobidAnalytics.processServerResponse('disabled: true'); + events.emit(constants.EVENTS.AUCTION_END, {auctionId: '12345678902'}); + clock.tick(1000); + expect(server.requests).to.have.length(3); + + nobidAnalytics.processServerResponse(JSON.stringify({disabled: true})); + disabled = nobidAnalytics.isAnalyticsDisabled(); + expect(disabled).to.equal(true); + events.emit(constants.EVENTS.AUCTION_END, {auctionId: '12345678902'}); + clock.tick(5000); + expect(server.requests).to.have.length(3); + + nobidAnalytics.retentionSeconds = 5; + nobidAnalytics.processServerResponse(JSON.stringify({disabled: true})); + clock.tick(1000); + disabled = nobidAnalytics.isAnalyticsDisabled(); + expect(disabled).to.equal(true); + clock.tick(6000); + disabled = nobidAnalytics.isAnalyticsDisabled(); + expect(disabled).to.equal(false); + + done(); + }); + }); + + describe('NoBid Carbonizer', function () { + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]); + clock = sinon.useFakeTimers(Date.now()); + }); + + afterEach(function () { + events.getEvents.restore(); + clock.restore(); + }); + + after(function () { + nobidAnalytics.disableAnalytics(); + }); + + it('Carbonizer test', function (done) { + let active = nobidCarbonizer.isActive(); + expect(active).to.equal(false); + + active = nobidCarbonizer.isActive(JSON.stringify({carbonizer_active: false})); + expect(active).to.equal(false); + + nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: true})); + active = nobidCarbonizer.isActive(); + expect(active).to.equal(true); + + const previousRetention = nobidAnalytics.retentionSeconds; + nobidAnalytics.retentionSeconds = 3; + nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: true})); + const stored = nobidCarbonizer.getStoredLocalData(); + expect(stored).to.contain(`{"carbonizer_active":true,"ts":`); + clock.tick(5000); + active = nobidCarbonizer.isActive(adunits, true); + expect(active).to.equal(false); + + nobidAnalytics.retentionSeconds = previousRetention; + nobidAnalytics.processServerResponse(JSON.stringify({carbonizer_active: true})); + active = nobidCarbonizer.isActive(adunits, true); + expect(active).to.equal(true); + + let adunits = [ + { + bids: [ + { bidder: 'bidder1' }, + { bidder: 'bidder2' } + ] + } + ] + nobidCarbonizer.carbonizeAdunits(adunits, true); + expect(adunits[0].bids.length).to.equal(0); + + done(); + }); + }); +}); From 4bb3e0252c5b2815139ba082a91730293c691836 Mon Sep 17 00:00:00 2001 From: Reda Guermas Date: Thu, 24 Aug 2023 14:02:26 -0700 Subject: [PATCH 14/14] Added NoBid Analytics Module documentation. --- modules/nobidAnalyticsAdapter.md | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 modules/nobidAnalyticsAdapter.md diff --git a/modules/nobidAnalyticsAdapter.md b/modules/nobidAnalyticsAdapter.md new file mode 100644 index 00000000000..92b9bdbb3cb --- /dev/null +++ b/modules/nobidAnalyticsAdapter.md @@ -0,0 +1,38 @@ +# Overview +Module Name: NoBid Analytics Adapter + +Module Type: Analytics Adapter + +Maintainer: [nobid.io](https://nobid.io) + + +# NoBid Analytics Registration + +The NoBid Analytics Adapter is free to use during our Beta period, but requires a simple registration with NoBid. Please visit [www.nobid.io](https://www.nobid.io/contact-1/) to sign up and request your NoBid Site ID to get started. If you're already using the NoBid Prebid Adapter, you may use your existing Site ID with the NoBid Analytics Adapter. + +The NoBid privacy policy is at [nobid.io/privacy-policy](https://www.nobid.io/privacy-policy/). + +## NoBid Analytics Configuration + +First, make sure to add the NoBid Analytics submodule to your Prebid.js package with: + +``` +gulp build --modules=...,nobidAnalyticsAdapter... +``` + +The following configuration parameters are available: + +```javascript +pbjs.enableAnalytics({ + provider: 'nobidAnalytics', + options: { + siteId: 123 // change to the Site ID you received from NoBid + } +}); +``` + +{: .table .table-bordered .table-striped } +| Parameter | Scope | Type | Description | Example | +| --- | --- | --- | --- | --- | +| provider | Required | String | The name of this module: `nobidAnalytics` | `nobidAnalytics` | +| options.siteId | Required | Number | This is the NoBid Site ID Number obtained from registering with NoBid. | `1234` |