From 1646d1fae4ee8c940331df648b9bea2e03ea3557 Mon Sep 17 00:00:00 2001 From: marc_tappx Date: Wed, 24 Mar 2021 12:27:26 +0100 Subject: [PATCH 01/93] UPDATE: add initial UID --- modules/tappxBidAdapter.js | 33 +++++++++++++++++++++++++++++++++ package-lock.json | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 7782f151802..b28423c750d 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -253,6 +253,35 @@ function buildOneRequest(validBidRequests, bidderRequest) { if (config.getConfig('coppa') === true) { regs.coppa = config.getConfig('coppa') === true ? 1 : 0; } + + // Universal ID + const userId = utils.deepAccess(validBidRequests, 'userId'); + const eidsArr = utils.deepAccess(validBidRequests, 'userIdAsEids'); + + utils.logMessage('-------- UID ------------') + utils.logMessage(validBidRequests); + utils.logMessage(eidsArr); + utils.logMessage(userId); + + let eids = []; + eidsArr.forEach(eidsElement => { + utils.logMessage('-------- EID ELEMENT ------------') + utils.logMessage(eidsElement) + if (eidsElement.source && eidsElement.uids[0].id) { + eids.push({ + source: eidsElement.source, + userId: eidsElement.uids[0].id + }); + } + }); + utils.logMessage('-------- EIDS ------------') + utils.logMessage(eids) + + let user = {}; + user.eids = eidsArr; + payload.user = user; + + utils.logMessage('-------- UID ------------') // < GDPR // > Payload @@ -268,6 +297,10 @@ function buildOneRequest(validBidRequests, bidderRequest) { payload.regs = regs; // < Payload + utils.logMessage('-------- PAYLOAD ------------') + utils.logMessage(JSON.stringify(payload)) + utils.logMessage('-------- PAYLOAD ------------') + return { method: 'POST', url: `https://${HOST}/${ENDPOINT}?type_cnn=prebidjs`, diff --git a/package-lock.json b/package-lock.json index 330298cbaac..a9f20c1ab55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.15.0-pre", + "version": "4.32.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { From 62d04e25cd4bcc97a15bf025f9be92b329989d55 Mon Sep 17 00:00:00 2001 From: marc_tappx Date: Fri, 26 Mar 2021 14:03:14 +0100 Subject: [PATCH 02/93] UPDATE: UID change user ext --- modules/tappxBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index b28423c750d..c73cd51be89 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -278,7 +278,8 @@ function buildOneRequest(validBidRequests, bidderRequest) { utils.logMessage(eids) let user = {}; - user.eids = eidsArr; + user.ext = {}; + user.ext.eids = eidsArr; payload.user = user; utils.logMessage('-------- UID ------------') From 9a2f9cb58c12d57bfa0a59f8bf3774a17e3c7728 Mon Sep 17 00:00:00 2001 From: marc_tappx Date: Fri, 26 Mar 2021 15:01:23 +0100 Subject: [PATCH 03/93] UPDATE: UID clean logs --- modules/tappxBidAdapter.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index c73cd51be89..e7733018ed4 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -258,15 +258,8 @@ function buildOneRequest(validBidRequests, bidderRequest) { const userId = utils.deepAccess(validBidRequests, 'userId'); const eidsArr = utils.deepAccess(validBidRequests, 'userIdAsEids'); - utils.logMessage('-------- UID ------------') - utils.logMessage(validBidRequests); - utils.logMessage(eidsArr); - utils.logMessage(userId); - let eids = []; eidsArr.forEach(eidsElement => { - utils.logMessage('-------- EID ELEMENT ------------') - utils.logMessage(eidsElement) if (eidsElement.source && eidsElement.uids[0].id) { eids.push({ source: eidsElement.source, @@ -274,15 +267,12 @@ function buildOneRequest(validBidRequests, bidderRequest) { }); } }); - utils.logMessage('-------- EIDS ------------') - utils.logMessage(eids) let user = {}; user.ext = {}; user.ext.eids = eidsArr; payload.user = user; - utils.logMessage('-------- UID ------------') // < GDPR // > Payload From 3fedc334f35deaefae3413584547cc80bb3831cd Mon Sep 17 00:00:00 2001 From: marc_tappx Date: Fri, 26 Mar 2021 15:02:38 +0100 Subject: [PATCH 04/93] UPDATE: add host info --- modules/tappxBidAdapter.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index e7733018ed4..1d06714c236 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -134,6 +134,9 @@ function interpretBannerBid(serverBid, request) { */ function buildOneRequest(validBidRequests, bidderRequest) { HOST = utils.deepAccess(validBidRequests, 'params.host'); + let hostInfo = getHostInfo(HOST) + utils.logMessage('-------- hostInfo ------------') + utils.logMessage(hostInfo) hostDomain = HOST.split('/', 1)[0]; const ENDPOINT = utils.deepAccess(validBidRequests, 'params.endpoint'); @@ -310,4 +313,26 @@ function getOs() { if (ua == null) { return 'unknown'; } else if (ua.match(/(iPhone|iPod|iPad)/)) { return 'ios'; } else if (ua.match(/Android/)) { return 'android'; } else if (ua.match(/Window/)) { return 'windows'; } else { return 'unknown'; } } +function getHostInfo(hostParam) { + let domainInfo = {}; + + domainInfo.domain = hostParam.split('/', 1)[0]; + domainInfo.url = hostParam; + + let regexNewEndpoints = new RegExp(`^(zz.*|testing)\.ssp\.tappx\.com$`, 'i'); + let regexClassicEndpoints = new RegExp(`^[a-z]{3}\.[a-z]{3}\.tappx\.com$`, 'i'); + + if (regexNewEndpoints.test(domainInfo.domain)) { + let endpoint = domainInfo.domain.split('.', 1)[0] + if (endpoint.toUpperCase().indexOf('TESTING') === -1) { + domainInfo.endpoint = endpoint + domainInfo.new_endpoint = true; + } + } else if (regexClassicEndpoints.test(domainInfo.domain)) { + domainInfo.new_endpoint = false; + } + + return domainInfo; +} + registerBidder(spec); From 3cfb9438df72f42d41ca51dee1e0244f4424e6fe Mon Sep 17 00:00:00 2001 From: marc_tappx Date: Tue, 30 Mar 2021 14:04:13 +0200 Subject: [PATCH 05/93] UPDATE: tappx bid adapter universal id --- modules/tappxBidAdapter.js | 30 +++--- test/spec/modules/tappxBidAdapter_spec.js | 111 ++++++++++++++++++---- 2 files changed, 110 insertions(+), 31 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 1d06714c236..3f62fbd04c9 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,6 +8,8 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; const TTL = 360; const CUR = 'USD'; +const TAPPX_BIDDER_VERSION = '0.1.10329'; +const TYPE_CNN = 'prebidjs'; var HOST; var hostDomain; @@ -133,11 +135,15 @@ function interpretBannerBid(serverBid, request) { * @return response ad */ function buildOneRequest(validBidRequests, bidderRequest) { + utils.logMessage('---------- validBidRequests'); + utils.logMessage(JSON.stringify(validBidRequests)); + utils.logMessage('---------- bidderRequest'); + utils.logMessage(JSON.stringify(bidderRequest)); + HOST = utils.deepAccess(validBidRequests, 'params.host'); let hostInfo = getHostInfo(HOST) - utils.logMessage('-------- hostInfo ------------') - utils.logMessage(hostInfo) - hostDomain = HOST.split('/', 1)[0]; + // hostDomain = HOST.split('/', 1)[0]; + hostDomain = hostInfo.domain; const ENDPOINT = utils.deepAccess(validBidRequests, 'params.endpoint'); const TAPPXKEY = utils.deepAccess(validBidRequests, 'params.tappxkey'); @@ -209,6 +215,13 @@ function buildOneRequest(validBidRequests, bidderRequest) { imp.secure = 1; imp.bidfloor = utils.deepAccess(validBidRequests, 'params.bidfloor'); + + let tappx = {}; + tappx.tappxkey = TAPPXKEY; + tappx.endpoint = ENDPOINT; + tappx.host = hostInfo.url; + + imp.ext = tappx; // < Imp object // > Device object @@ -233,8 +246,6 @@ function buildOneRequest(validBidRequests, bidderRequest) { // > Params let params = {}; params.host = 'tappx.com'; - params.tappxkey = TAPPXKEY; - params.endpoint = ENDPOINT; params.bidfloor = BIDFLOOR; // < Params @@ -258,9 +269,8 @@ function buildOneRequest(validBidRequests, bidderRequest) { } // Universal ID - const userId = utils.deepAccess(validBidRequests, 'userId'); const eidsArr = utils.deepAccess(validBidRequests, 'userIdAsEids'); - + let eids = []; eidsArr.forEach(eidsElement => { if (eidsElement.source && eidsElement.uids[0].id) { @@ -291,13 +301,9 @@ function buildOneRequest(validBidRequests, bidderRequest) { payload.regs = regs; // < Payload - utils.logMessage('-------- PAYLOAD ------------') - utils.logMessage(JSON.stringify(payload)) - utils.logMessage('-------- PAYLOAD ------------') - return { method: 'POST', - url: `https://${HOST}/${ENDPOINT}?type_cnn=prebidjs`, + url: `https://${HOST}/${ENDPOINT}?type_cnn=${TYPE_CNN}&v=${TAPPX_BIDDER_VERSION}`, data: JSON.stringify(payload), bids: validBidRequests }; diff --git a/test/spec/modules/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js index c4410d8ce5e..945f077c4ab 100644 --- a/test/spec/modules/tappxBidAdapter_spec.js +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -1,29 +1,105 @@ import { assert } from 'chai'; -import {spec} from 'modules/tappxBidAdapter'; +import { spec } from 'modules/tappxBidAdapter'; -describe('Tappx adapter tests', function () { - describe('isBidRequestValid', function () { - let bid = { bidder: 'tappx', params: { host: 'testing.ssp.tappx.com', tappxkey: 'pub-1234-test-1234', endpoint: 'ZZ1234PBJS', bidfloor: 0.005 } }; +const bidRequest = { + data: {}, + bids: [ + { + bidder: 'tappx', + params: { + host: 'testing.ssp.tappx.com\/rtb\/v2\/', + tappxkey: 'pub-1234-android-1234', + endpoint: 'ZZ1234PBJS', + bidfloor: 0.05 + }, + crumbs: { pubcid: 'df2144f7-673f-4440-83f5-cd4a73642d99' }, + fpd: { + context: { + adServer: { name: 'gam', adSlot: '/19968336/header-bid-tag-0' }, + pbAdSlot: '/19968336/header-bid-tag-0', + }, + }, + mediaTypes: { banner: { sizes: [[320, 480]] } }, + adUnitCode: 'div-1', + transactionId: '47dd44e8-e7db-417c-a8f1-621a2e1a117d', + sizes: [[320, 480]], + bidId: '2170932097e505', + bidderRequestId: '140ba7a1ab7aeb', + auctionId: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + }, + ], +}; + +const serverResponse = { + body: { + id: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', + bidid: 'bid3811165568213389257', + seatbid: [ + { + seat: '1', + group: 0, + bid: [ + { + id: '3811165568213389257', + impid: 1, + price: 0.05, + adm: + "\t", + w: 320, + h: 480, + lurl: + 'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', + cid: '01744fbb521e9fb10ffea926190effea', + crid: 'a13cf884e66e7c660afec059c89d98b6', + adomain: [], + }, + ], + }, + ], + cur: 'USD', + }, + headers: {}, +}; + +// const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + +// const bidderRequest = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[300, 250], [300, 600]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[300, 250], [300, 600]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': 'BPDNSKBPD3CGMAAABAENABAAAAAAAA', 'vendorData': {'metadata': 'BPDNSKBPD3CGMAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2021-03-17T15:15:24.900Z', 'lastUpdated': '2021-03-30T07:15:51.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'start': 1617088922136}; +describe('Tappx bid adapter', function () { + /** + * IS REQUEST VALID + */ + describe('isBidRequestValid', function () { it('should return true when required params found', function () { - assert(spec.isBidRequestValid(bid)); + assert.isTrue(spec.isBidRequestValid(bidRequest.bids[0]), JSON.stringify(bidRequest)); }); it('should return false when required params are missing', function () { - const bid = { - host: 'testing.ssp.tappx.com' - }; - assert.isFalse(spec.isBidRequestValid(bid)); + let badBidRequest = bidRequest; + delete badBidRequest.bids[0].params.tappxkey; + delete badBidRequest.bids[0].params.endpoint; + assert.isFalse(spec.isBidRequestValid(badBidRequest.bids[0])); }); }); + /** + * BUILD REQUEST TEST + */ describe('buildRequest', function () { let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; // Web Test - let validBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'api': [3, 5]}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'div-1', 'transactionId': '713f2c01-61e3-45b5-9e4e-2b163033f3d6', 'sizes': [[320, 480]], 'bidId': '27818d05971607', 'bidderRequestId': '1320551a307df5', 'auctionId': '3f1281d3-9860-4657-808d-3c1d42231ef3', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}] + // let validBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'api': [3, 5]}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'div-1', 'transactionId': '713f2c01-61e3-45b5-9e4e-2b163033f3d6', 'sizes': [[320, 480]], 'bidId': '27818d05971607', 'bidderRequestId': '1320551a307df5', 'auctionId': '3f1281d3-9860-4657-808d-3c1d42231ef3', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}] + let validBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; // App Test - let validAppBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'api': [3, 5], 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 50]]}}, 'adUnitCode': 'div-1', 'transactionId': '713f2c01-61e3-45b5-9e4e-2b163033f3d6', 'sizes': [[320, 50]], 'bidId': '27818d05971607', 'bidderRequestId': '1320551a307df5', 'auctionId': '3f1281d3-9860-4657-808d-3c1d42231ef3', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}] - let bidderRequest = {'bidderCode': 'tappx', 'auctionId': '6cca2192-2262-468b-8a3c-d00c58a5d911', 'bidderRequestId': '1ae5c6a02684df', 'bids': [{'bidder': 'tappx', 'params': {'host': 'tests.tappx.com', 'tappxkey': 'pub-1234-test-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'banner-ad-div', 'transactionId': 'c44cdbde-ab6d-47a0-8dde-6b4ff7909a35', 'sizes': [[320, 50]], 'bidId': '2e3a5feb30cfe4', 'bidderRequestId': '1ae5c6a02684df', 'auctionId': '6cca2192-2262-468b-8a3c-d00c58a5d911', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1611308859094, 'timeout': 700, 'refererInfo': {'referer': 'http://tappx.local:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://tappx.local:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': consentString, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; + // let validAppBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 50]]}}, 'adUnitCode': 'div-1', 'transactionId': '713f2c01-61e3-45b5-9e4e-2b163033f3d6', 'sizes': [[320, 50]], 'bidId': '27818d05971607', 'bidderRequestId': '1320551a307df5', 'auctionId': '3f1281d3-9860-4657-808d-3c1d42231ef3', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}] + // let bidderRequest = {'bidderCode': 'tappx', 'auctionId': '6cca2192-2262-468b-8a3c-d00c58a5d911', 'bidderRequestId': '1ae5c6a02684df', 'bids': [{'bidder': 'tappx', 'params': {'host': 'tests.tappx.com', 'tappxkey': 'pub-1234-test-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'banner-ad-div', 'transactionId': 'c44cdbde-ab6d-47a0-8dde-6b4ff7909a35', 'sizes': [[320, 50]], 'bidId': '2e3a5feb30cfe4', 'bidderRequestId': '1ae5c6a02684df', 'auctionId': '6cca2192-2262-468b-8a3c-d00c58a5d911', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1611308859094, 'timeout': 700, 'refererInfo': {'referer': 'http://tappx.local:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://tappx.local:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': consentString, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; + let validAppBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; + + let bidderRequest = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': consentString, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; it('should add gdpr/usp consent information to the request', function () { const request = spec.buildRequests(validBidRequests, bidderRequest); @@ -63,13 +139,10 @@ describe('Tappx adapter tests', function () { }); }); + /** + * INTERPRET RESPONSE TESTS + */ describe('interpretResponse', function () { - const bidRequest = { - data: {}, - bids: [ {'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.05, 'api': [3, 5]}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'div-1', 'transactionId': '47dd44e8-e7db-417c-a8f1-621a2e1a117d', 'sizes': [[320, 480]], 'bidId': '2170932097e505', 'bidderRequestId': '140ba7a1ab7aeb', 'auctionId': '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0} ] - }; - - const serverResponse = {'body': {'id': '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', 'bidid': 'bid3811165568213389257', 'seatbid': [{'seat': '1', 'group': 0, 'bid': [{'id': '3811165568213389257', 'impid': 1, 'price': 0.05, 'adm': "\t", 'w': 320, 'h': 480, 'lurl': 'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', 'cid': '01744fbb521e9fb10ffea926190effea', 'crid': 'a13cf884e66e7c660afec059c89d98b6', 'adomain': []}]}], 'cur': 'USD'}, 'headers': {}}; it('receive reponse with single placement', function () { const bids = spec.interpretResponse(serverResponse, bidRequest); const bid = bids[0]; @@ -77,7 +150,7 @@ describe('Tappx adapter tests', function () { expect(bid.ad).to.match(/^\t", - w: 320, - h: 480, - lurl: - 'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', - cid: '01744fbb521e9fb10ffea926190effea', - crid: 'a13cf884e66e7c660afec059c89d98b6', - adomain: [], - }, - ], - }, - ], - cur: 'USD', - }, - headers: {}, -}; - -// const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; - -// const bidderRequest = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[300, 250], [300, 600]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[300, 250], [300, 600]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': 'BPDNSKBPD3CGMAAABAENABAAAAAAAA', 'vendorData': {'metadata': 'BPDNSKBPD3CGMAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2021-03-17T15:15:24.900Z', 'lastUpdated': '2021-03-30T07:15:51.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'start': 1617088922136}; +const c_BIDREQUEST = {data:{},bids:[{bidder:'tappx',params:{host:'testing.ssp.tappx.com\/rtb\/v2\/',tappxkey:'pub-1234-android-1234',endpoint:'ZZ1234PBJS',bidfloor:0.05},crumbs:{pubcid:'df2144f7-673f-4440-83f5-cd4a73642d99'},fpd:{context:{adServer:{name:'gam',adSlot:'/19968336/header-bid-tag-0'},pbAdSlot:'/19968336/header-bid-tag-0',},},mediaTypes:{banner:{sizes:[[320,480]]}},adUnitCode:'div-1',transactionId:'47dd44e8-e7db-417c-a8f1-621a2e1a117d',sizes:[[320,480]],bidId:'2170932097e505',bidderRequestId:'140ba7a1ab7aeb',auctionId:'1c54b4f1-645f-44e6-b8ae-5d43c923ef1c',src:'client',bidRequestsCount:1,bidderRequestsCount:1,bidderWinsCount:0,}]}; +const c_SERVERRESPONSE = {body:{id:'1c54b4f1-645f-44e6-b8ae-5d43c923ef1c',bidid:'bid3811165568213389257',seatbid:[{seat:'1',group:0,bid:[{id:'3811165568213389257',impid:1,price:0.05,adm:"\t",w:320,h:480,lurl:'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}',cid:'01744fbb521e9fb10ffea926190effea',crid:'a13cf884e66e7c660afec059c89d98b6',adomain:[],},],},],cur:'USD',},headers:{},}; +const c_CONSENTSTRING = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; +const c_VALIDBIDREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000x179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6eFJ7otPYix179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6e', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_rEXbz6UYtYEJelYrDaZOLkh8WcF9J0ZHmEHFKZEBlLXsgP6xqXU3BCj4Ay0Z6fw_jSOaHxMHwd-voRHqFA4Q9NwAxFcVLyPWnNGZ9VbcSAPos1wupq7Xu3MIm-Bw_0vxjhZdWNy4chM9x3i', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': 'xTtLUY7GwqX2MMqSHo9RQ2YUOIBFhlASOR43I9KjvgtcrxIys3RxME96M02LTjWR', 'parrableId': {'eid': '02.YoqC9lWZh8.C8QTSiJTNgI6Pp0KCM5zZgEgwVMSsVP5W51X8cmiUHQESq9WRKB4nreqZJwsWIcNKlORhG4u25Wm6lmDOBmQ0B8hv0KP6uVQ97aouuH52zaz2ctVQTORUKkErPRPcaCJ7dKFcrNoF2i6WOR0S5Nk'}, 'pubcid': 'b1254-152f-12F5-5698-dI1eljK6C7WA', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; +const c_VALIDBIDAPPREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; +const c_BIDDERREQUEST = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': c_CONSENTSTRING, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; describe('Tappx bid adapter', function () { /** @@ -75,11 +14,11 @@ describe('Tappx bid adapter', function () { */ describe('isBidRequestValid', function () { it('should return true when required params found', function () { - assert.isTrue(spec.isBidRequestValid(bidRequest.bids[0]), JSON.stringify(bidRequest)); + assert.isTrue(spec.isBidRequestValid(c_BIDREQUEST.bids[0]), JSON.stringify(c_BIDREQUEST)); }); it('should return false when required params are missing', function () { - let badBidRequest = bidRequest; + let badBidRequest = c_BIDREQUEST; delete badBidRequest.bids[0].params.tappxkey; delete badBidRequest.bids[0].params.endpoint; assert.isFalse(spec.isBidRequestValid(badBidRequest.bids[0])); @@ -90,23 +29,19 @@ describe('Tappx bid adapter', function () { * BUILD REQUEST TEST */ describe('buildRequest', function () { - let consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; // Web Test - // let validBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'api': [3, 5]}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'div-1', 'transactionId': '713f2c01-61e3-45b5-9e4e-2b163033f3d6', 'sizes': [[320, 480]], 'bidId': '27818d05971607', 'bidderRequestId': '1320551a307df5', 'auctionId': '3f1281d3-9860-4657-808d-3c1d42231ef3', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}] - let validBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; + let validBidRequests = c_VALIDBIDREQUESTS; // App Test - // let validAppBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'crumbs': {'pubcid': 'df2144f7-673f-4440-83f5-cd4a73642d99'}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 50]]}}, 'adUnitCode': 'div-1', 'transactionId': '713f2c01-61e3-45b5-9e4e-2b163033f3d6', 'sizes': [[320, 50]], 'bidId': '27818d05971607', 'bidderRequestId': '1320551a307df5', 'auctionId': '3f1281d3-9860-4657-808d-3c1d42231ef3', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}] - // let bidderRequest = {'bidderCode': 'tappx', 'auctionId': '6cca2192-2262-468b-8a3c-d00c58a5d911', 'bidderRequestId': '1ae5c6a02684df', 'bids': [{'bidder': 'tappx', 'params': {'host': 'tests.tappx.com', 'tappxkey': 'pub-1234-test-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005}, 'fpd': {'context': {'adServer': {'name': 'gam', 'adSlot': '/19968336/header-bid-tag-0'}, 'pbAdSlot': '/19968336/header-bid-tag-0'}}, 'mediaTypes': {'banner': {'sizes': [[320, 480]]}}, 'adUnitCode': 'banner-ad-div', 'transactionId': 'c44cdbde-ab6d-47a0-8dde-6b4ff7909a35', 'sizes': [[320, 50]], 'bidId': '2e3a5feb30cfe4', 'bidderRequestId': '1ae5c6a02684df', 'auctionId': '6cca2192-2262-468b-8a3c-d00c58a5d911', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1611308859094, 'timeout': 700, 'refererInfo': {'referer': 'http://tappx.local:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://tappx.local:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': consentString, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; - let validAppBidRequests = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; + let validAppBidRequests = c_VALIDBIDAPPREQUESTS; - let bidderRequest = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': consentString, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; + let bidderRequest = c_BIDDERREQUEST; it('should add gdpr/usp consent information to the request', function () { const request = spec.buildRequests(validBidRequests, bidderRequest); const payload = JSON.parse(request[0].data); expect(payload.regs.gdpr).to.exist.and.to.be.true; - expect(payload.regs.consent).to.exist.and.to.equal(consentString); + expect(payload.regs.consent).to.exist.and.to.equal(c_CONSENTSTRING); expect(payload.regs.ext.us_privacy).to.exist; }); @@ -144,15 +79,15 @@ describe('Tappx bid adapter', function () { */ describe('interpretResponse', function () { it('receive reponse with single placement', function () { - const bids = spec.interpretResponse(serverResponse, bidRequest); + const bids = spec.interpretResponse(c_SERVERRESPONSE, c_BIDREQUEST); const bid = bids[0]; expect(bid.cpm).to.exist; expect(bid.ad).to.match(/^\t",w:320,h:480,lurl:'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}',cid:'01744fbb521e9fb10ffea926190effea',crid:'a13cf884e66e7c660afec059c89d98b6',adomain:[],},],},],cur:'USD',},headers:{},}; +const c_BIDREQUEST = { + data: { + }, + bids: [ + { + bidder: 'tappx', + params: { + host: 'testing.ssp.tappx.com\/rtb\/v2\/', + tappxkey: 'pub-1234-android-1234', + endpoint: 'ZZ1234PBJS', + bidfloor: 0.05 + }, + crumbs: { + pubcid: 'df2144f7-673f-4440-83f5-cd4a73642d99' + }, + fpd: { + context: { + adServer: { + name: 'gam', + adSlot: '/19968336/header-bid-tag-0' + }, + pbAdSlot: '/19968336/header-bid-tag-0', + }, + }, + mediaTypes: { + banner: { + sizes: [ + [ + 320, + 480 + ] + ] + } + }, + adUnitCode: 'div-1', + transactionId: '47dd44e8-e7db-417c-a8f1-621a2e1a117d', + sizes: [ + [ + 320, + 480 + ] + ], + bidId: '2170932097e505', + bidderRequestId: '140ba7a1ab7aeb', + auctionId: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + } + ] +}; +const c_SERVERRESPONSE = { + body: { + id: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', + bidid: 'bid3811165568213389257', + seatbid: [ + { + seat: '1', + group: 0, + bid: [ + { + id: '3811165568213389257', + impid: 1, + price: 0.05, + adm: "\t", + w: 320, + h: 480, + lurl: 'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', + cid: '01744fbb521e9fb10ffea926190effea', + crid: 'a13cf884e66e7c660afec059c89d98b6', + adomain: [ + ], + }, + ], + }, + ], + cur: 'USD', + }, + headers: {} +}; const c_CONSENTSTRING = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; const c_VALIDBIDREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000x179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6eFJ7otPYix179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6e', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_rEXbz6UYtYEJelYrDaZOLkh8WcF9J0ZHmEHFKZEBlLXsgP6xqXU3BCj4Ay0Z6fw_jSOaHxMHwd-voRHqFA4Q9NwAxFcVLyPWnNGZ9VbcSAPos1wupq7Xu3MIm-Bw_0vxjhZdWNy4chM9x3i', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': 'xTtLUY7GwqX2MMqSHo9RQ2YUOIBFhlASOR43I9KjvgtcrxIys3RxME96M02LTjWR', 'parrableId': {'eid': '02.YoqC9lWZh8.C8QTSiJTNgI6Pp0KCM5zZgEgwVMSsVP5W51X8cmiUHQESq9WRKB4nreqZJwsWIcNKlORhG4u25Wm6lmDOBmQ0B8hv0KP6uVQ97aouuH52zaz2ctVQTORUKkErPRPcaCJ7dKFcrNoF2i6WOR0S5Nk'}, 'pubcid': 'b1254-152f-12F5-5698-dI1eljK6C7WA', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; const c_VALIDBIDAPPREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; From 919c2f2d99060608dae6c21221a51b1d3b8b25df Mon Sep 17 00:00:00 2001 From: marc_tappx Date: Wed, 31 Mar 2021 12:57:26 +0200 Subject: [PATCH 09/93] tappxBidAdapter: add test user eids array --- test/spec/modules/tappxBidAdapter_spec.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/spec/modules/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js index c0e536bb8e1..1d3f9676d09 100644 --- a/test/spec/modules/tappxBidAdapter_spec.js +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -138,6 +138,14 @@ describe('Tappx bid adapter', function () { expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); }); + it('should set user eids array', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + + const data = JSON.parse(request[0].data); + expect(data.user.ext.eids, data).to.not.be.null; + expect(data.user.ext.eids[0]).to.have.keys(['source', 'uids']); + }); + it('should properly build a banner request with app params', function () { const request = spec.buildRequests(validAppBidRequests, bidderRequest); expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); From 37d9f962f3c5d18723d529971c87c339e504cf24 Mon Sep 17 00:00:00 2001 From: marc_tappx Date: Wed, 31 Mar 2021 12:58:03 +0200 Subject: [PATCH 10/93] tappxBidAdapter: update eids array --- modules/tappxBidAdapter.js | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 25d0e7402a1..3677c3ce4c9 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -266,22 +266,11 @@ function buildOneRequest(validBidRequests, bidderRequest) { // Universal ID const eidsArr = utils.deepAccess(validBidRequests, 'userIdAsEids'); - - let eids = []; - eidsArr.forEach(eidsElement => { - if (eidsElement.source && eidsElement.uids[0].id) { - eids.push({ - source: eidsElement.source, - userId: eidsElement.uids[0].id - }); + payload.user = { + ext: { + eids: eidsArr } - }); - - let user = {}; - user.ext = {}; - user.ext.eids = eidsArr; - payload.user = user; - + }; // < GDPR // > Payload From e5777a2760f4a92853dc8d453aacd98fec36ea1a Mon Sep 17 00:00:00 2001 From: marc_tappx Date: Wed, 31 Mar 2021 13:29:07 +0200 Subject: [PATCH 11/93] FIX: package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a9f20c1ab55..5131b5f4d23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.32.0-pre", + "version": "4.15.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -24613,4 +24613,4 @@ } } } -} +} \ No newline at end of file From d9a99691a78820ff87413af4c8933100a925c49a Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Wed, 31 Mar 2021 07:57:21 -0400 Subject: [PATCH 12/93] Conversant adapter: add adomain, remove digitrust (#6495) * Update eids.js * Update eids_spec.js * Update eids.js * Update pubmaticBidAdapter_spec.js * Update eids.js * Update eids_spec.js * Update conversantBidAdapter_spec.js * Update rubiconBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Delete test/spec/adapters directory * Update userId_spec.js * Update conversantBidAdapter.js * Update conversantBidAdapter_spec.js * Update conversantBidAdapter_spec.js * Update conversantBidAdapter_spec.js --- modules/conversantBidAdapter.js | 5 ++++- test/spec/modules/conversantBidAdapter_spec.js | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index 3b3d04dc498..74cd97ad019 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -205,6 +205,10 @@ export const spec = { ttl: 300, netRevenue: true }; + bid.meta = {}; + if (conversantBid.adomain && conversantBid.adomain.length > 0) { + bid.meta.advertiserDomains = conversantBid.adomain; + } if (request.video) { if (responseAd.charAt(0) === '<') { @@ -331,7 +335,6 @@ function collectEids(bidRequests) { 'criteo.com': 1, 'id5-sync.com': 1, 'parrable.com': 1, - 'digitru.st': 1, 'liveintent.com': 1 }; request.userIdAsEids.forEach(function(eid) { diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 1cb6bd6c6d6..1c24fb7694a 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -339,6 +339,7 @@ describe('Conversant adapter tests', function() { expect(bid).to.have.property('creativeId', '1000'); expect(bid).to.have.property('width', 300); expect(bid).to.have.property('height', 250); + expect(bid.meta.advertiserDomains).to.deep.equal(['https://example.com']); expect(bid).to.have.property('ad', 'markup000'); expect(bid).to.have.property('ttl', 300); expect(bid).to.have.property('netRevenue', true); From 668c01754240801a3d68e7e97c42123bb9011ff7 Mon Sep 17 00:00:00 2001 From: onlsol <48312668+onlsol@users.noreply.github.com> Date: Wed, 31 Mar 2021 16:18:39 +0200 Subject: [PATCH 13/93] Rads Bid Adapter: add GDPR support & user sync support (#6455) --- modules/radsBidAdapter.js | 54 ++++++++++++++- test/spec/modules/radsBidAdapter_spec.js | 83 ++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 2 deletions(-) diff --git a/modules/radsBidAdapter.js b/modules/radsBidAdapter.js index 1f07cc07b91..8f3cbe02f23 100644 --- a/modules/radsBidAdapter.js +++ b/modules/radsBidAdapter.js @@ -57,7 +57,7 @@ export const spec = { bid_id: bidId, }; } - prepareExtraParams(params, payload); + prepareExtraParams(params, payload, bidderRequest); return { method: 'GET', @@ -97,9 +97,46 @@ export const spec = { bidResponses.push(bidResponse); } return bidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + if (!serverResponses || serverResponses.length === 0) { + return []; + } + + const syncs = [] + + let gdprParams = ''; + if (gdprConsent) { + if ('gdprApplies' in gdprConsent && typeof gdprConsent.gdprApplies === 'boolean') { + gdprParams = `gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + gdprParams = `gdpr_consent=${gdprConsent.consentString}`; + } + } + + if (syncOptions.iframeEnabled) { + serverResponses[0].body.userSync.iframeUrl.forEach((url) => syncs.push({ + type: 'iframe', + url: appendToUrl(url, gdprParams) + })); + } + if (syncOptions.pixelEnabled && serverResponses.length > 0) { + serverResponses[0].body.userSync.imageUrl.forEach((url) => syncs.push({ + type: 'image', + url: appendToUrl(url, gdprParams) + })); + } + return syncs; } } +function appendToUrl(url, what) { + if (!what) { + return url; + } + return url + (url.indexOf('?') !== -1 ? '&' : '?') + what; +} + function objectToQueryString(obj, prefix) { let str = []; let p; @@ -125,10 +162,23 @@ function isVideoRequest(bid) { return bid.mediaType === 'video' || !!utils.deepAccess(bid, 'mediaTypes.video'); } -function prepareExtraParams(params, payload) { +function prepareExtraParams(params, payload, bidderRequest) { if (params.pfilter !== undefined) { payload.pfilter = params.pfilter; } + + if (bidderRequest && bidderRequest.gdprConsent) { + if (payload.pfilter !== undefined) { + payload.pfilter.gdpr_consent = bidderRequest.gdprConsent.consentString; + payload.pfilter.gdpr = bidderRequest.gdprConsent.gdprApplies; + } else { + payload.pfilter = { + 'gdpr_consent': bidderRequest.gdprConsent.consentString, + 'gdpr': bidderRequest.gdprConsent.gdprApplies + }; + } + } + if (params.bcat !== undefined) { payload.bcat = params.bcat; } diff --git a/test/spec/modules/radsBidAdapter_spec.js b/test/spec/modules/radsBidAdapter_spec.js index c629daf3da5..c3c3b4b2746 100644 --- a/test/spec/modules/radsBidAdapter_spec.js +++ b/test/spec/modules/radsBidAdapter_spec.js @@ -87,12 +87,25 @@ describe('radsAdapter', function () { 'auctionId': '1d1a030790a475' }]; + // Without gdprConsent let bidderRequest = { refererInfo: { referer: 'some_referrer.net' } } + // With gdprConsent + var bidderRequestGdprConsent = { + refererInfo: { + referer: 'some_referrer.net' + }, + gdprConsent: { + consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', + vendorData: {someData: 'value'}, + gdprApplies: true + } + }; + // without gdprConsent const request = spec.buildRequests(bidRequests, bidderRequest); it('sends bid request to our endpoint via GET', function () { expect(request[0].method).to.equal('GET'); @@ -105,6 +118,20 @@ describe('radsAdapter', function () { let data = request[1].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); expect(data).to.equal('rt=vast2&_f=prebid_js&_ps=6682&srw=640&srh=480&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgeo%5D%5Bregion%5D=DE-BE&bcat=IAB2%2CIAB4&dvt=desktop'); }); + + // with gdprConsent + const request2 = spec.buildRequests(bidRequests, bidderRequestGdprConsent); + it('sends bid request to our endpoint via GET', function () { + expect(request2[0].method).to.equal('GET'); + let data = request2[0].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); + expect(data).to.equal('rt=bid-response&_f=prebid_js&_ps=6682&srw=300&srh=250&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgdpr_consent%5D=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&pfilter%5Bgdpr%5D=true&bcat=IAB2%2CIAB4&dvt=desktop&i=1.1.1.1'); + }); + + it('sends bid video request to our rads endpoint via GET', function () { + expect(request2[1].method).to.equal('GET'); + let data = request2[1].data.replace(/rnd=\d+\&/g, '').replace(/ref=.*\&bid/g, 'bid'); + expect(data).to.equal('rt=vast2&_f=prebid_js&_ps=6682&srw=640&srh=480&idt=100&p=some_referrer.net&bid_id=30b31c1838de1e&pfilter%5Bfloorprice%5D=1000000&pfilter%5Bgeo%5D%5Bcountry%5D=DE&pfilter%5Bgeo%5D%5Bregion%5D=DE-BE&pfilter%5Bgdpr_consent%5D=BOJ%2FP2HOJ%2FP2HABABMAAAAAZ%2BA%3D%3D&pfilter%5Bgdpr%5D=true&bcat=IAB2%2CIAB4&dvt=desktop'); + }); }); describe('interpretResponse', function () { @@ -203,4 +230,60 @@ describe('radsAdapter', function () { expect(result.length).to.equal(0); }); }); + + describe(`getUserSyncs test usage`, function () { + let serverResponses; + + beforeEach(function () { + serverResponses = [{ + body: { + requestId: '23beaa6af6cdde', + cpm: 0.5, + width: 0, + height: 0, + creativeId: 100500, + dealId: '', + currency: 'EUR', + netRevenue: true, + ttl: 300, + type: 'sspHTML', + ad: '', + userSync: { + iframeUrl: ['anyIframeUrl?a=1'], + imageUrl: ['anyImageUrl', 'anyImageUrl2'] + } + } + }]; + }); + + it(`return value should be an array`, function () { + expect(spec.getUserSyncs({ iframeEnabled: true })).to.be.an('array'); + }); + it(`array should have only one object and it should have a property type = 'iframe'`, function () { + expect(spec.getUserSyncs({ iframeEnabled: true }, serverResponses).length).to.be.equal(1); + let [userSync] = spec.getUserSyncs({ iframeEnabled: true }, serverResponses); + expect(userSync).to.have.property('type'); + expect(userSync.type).to.be.equal('iframe'); + }); + it(`we have valid sync url for iframe`, function () { + let [userSync] = spec.getUserSyncs({ iframeEnabled: true }, serverResponses, {consentString: 'anyString'}); + expect(userSync.url).to.be.equal('anyIframeUrl?a=1&gdpr_consent=anyString') + expect(userSync.type).to.be.equal('iframe'); + }); + it(`we have valid sync url for image`, function () { + let [userSync] = spec.getUserSyncs({ pixelEnabled: true }, serverResponses, {gdprApplies: true, consentString: 'anyString'}); + expect(userSync.url).to.be.equal('anyImageUrl?gdpr=1&gdpr_consent=anyString') + expect(userSync.type).to.be.equal('image'); + }); + it(`we have valid sync url for image and iframe`, function () { + let userSync = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, serverResponses, {gdprApplies: true, consentString: 'anyString'}); + expect(userSync.length).to.be.equal(3); + expect(userSync[0].url).to.be.equal('anyIframeUrl?a=1&gdpr=1&gdpr_consent=anyString') + expect(userSync[0].type).to.be.equal('iframe'); + expect(userSync[1].url).to.be.equal('anyImageUrl?gdpr=1&gdpr_consent=anyString') + expect(userSync[1].type).to.be.equal('image'); + expect(userSync[2].url).to.be.equal('anyImageUrl2?gdpr=1&gdpr_consent=anyString') + expect(userSync[2].type).to.be.equal('image'); + }); + }); }); From 7da67997c9183600f2eac727158f257bb03a91ee Mon Sep 17 00:00:00 2001 From: vincentproxistore <56686565+vincentproxistore@users.noreply.github.com> Date: Wed, 31 Mar 2021 17:00:05 +0200 Subject: [PATCH 14/93] Proxistore Bid Adapter: add cookieless url endpoint & use floor module (#6427) * use floor module * call cookieless endpoint when necessary * test endpoint url * change url endpoint * delete console log * fix tests * add language to url * use ortb interface * unit test * update test unit * create proxistore module * add unit tests and documentation * delete modules * delete module * add proxistore rtd submodule * delete proxistore module * spacing * change url --- modules/proxistoreBidAdapter.js | 123 +++++++------- .../spec/modules/proxistoreBidAdapter_spec.js | 155 +++++++++--------- 2 files changed, 141 insertions(+), 137 deletions(-) diff --git a/modules/proxistoreBidAdapter.js b/modules/proxistoreBidAdapter.js index c546337c47f..0b553ce995b 100644 --- a/modules/proxistoreBidAdapter.js +++ b/modules/proxistoreBidAdapter.js @@ -1,62 +1,83 @@ - import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { getStorageManager } from '../src/storageManager.js'; + const BIDDER_CODE = 'proxistore'; -const storage = getStorageManager(); const PROXISTORE_VENDOR_ID = 418; function _createServerRequest(bidRequests, bidderRequest) { - const sizeIds = []; - + var sizeIds = []; bidRequests.forEach(function (bid) { - const sizeId = { + var sizeId = { id: bid.bidId, sizes: bid.sizes.map(function (size) { return { width: size[0], - height: size[1] + height: size[1], }; - }) + }), + floor: _assignFloor(bid), + segments: _assignSegments(bid), }; sizeIds.push(sizeId); }); - const payload = { + var payload = { auctionId: bidRequests[0].auctionId, transactionId: bidRequests[0].auctionId, bids: sizeIds, website: bidRequests[0].params.website, language: bidRequests[0].params.language, gdpr: { - applies: false - } - }; - const options = { - contentType: 'application/json', - withCredentials: true + applies: false, + }, }; if (bidderRequest && bidderRequest.gdprConsent) { - if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean' && bidderRequest.gdprConsent.gdprApplies) { + if ( + typeof bidderRequest.gdprConsent.gdprApplies === 'boolean' && + bidderRequest.gdprConsent.gdprApplies + ) { payload.gdpr.applies = true; } - if (typeof bidderRequest.gdprConsent.consentString === 'string' && bidderRequest.gdprConsent.consentString) { + if ( + typeof bidderRequest.gdprConsent.consentString === 'string' && + bidderRequest.gdprConsent.consentString + ) { payload.gdpr.consentString = bidderRequest.gdprConsent.consentString; } - if (bidderRequest.gdprConsent.vendorData && bidderRequest.gdprConsent.vendorData.vendorConsents && typeof bidderRequest.gdprConsent.vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)] !== 'undefined') { - payload.gdpr.consentGiven = !!bidderRequest.gdprConsent.vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)]; + if ( + bidderRequest.gdprConsent.vendorData && + bidderRequest.gdprConsent.vendorData.vendorConsents && + typeof bidderRequest.gdprConsent.vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)] !== 'undefined' + ) { + payload.gdpr.consentGiven = !!bidderRequest.gdprConsent.vendorData + .vendorConsents[PROXISTORE_VENDOR_ID.toString(10)]; } } + const options = { + contentType: 'application/json', + withCredentials: !!payload.gdpr.consentGiven, + }; + const endPointUri = payload.gdpr.consentGiven || !payload.gdpr.applies + ? `https://abs.proxistore.com/${payload.language}/v3/rtb/prebid/multi` + : `https://abs.proxistore.com/${payload.language}/v3/rtb/prebid/multi/cookieless`; + return { method: 'POST', - url: bidRequests[0].params.url || 'https://abs.proxistore.com/' + payload.language + '/v3/rtb/prebid/multi', + url: endPointUri, data: JSON.stringify(payload), - options: options + options: options, }; } +function _assignSegments(bid) { + if (bid.ortb2 && bid.ortb2.user && bid.ortb2.user.ext && bid.ortb2.user.ext.data) { + return bid.ortb2.user.ext.data || {segments: [], contextual_categories: {}}; + } + return {segments: [], contextual_categories: {}}; +} + function _createBidResponse(response) { return { requestId: response.requestId, @@ -70,7 +91,7 @@ function _createBidResponse(response) { netRevenue: response.netRevenue, vastUrl: response.vastUrl, vastXml: response.vastXml, - dealId: response.dealId + dealId: response.dealId, }; } /** @@ -81,23 +102,8 @@ function _createBidResponse(response) { */ function isBidRequestValid(bid) { - const hasNoAd = function() { - if (!storage.hasLocalStorage()) { - return false; - } - const pxNoAds = storage.getDataFromLocalStorage(`PX_NoAds_${bid.params.website}`); - if (!pxNoAds) { - return false; - } else { - const storedDate = new Date(pxNoAds); - const now = new Date(); - const diff = Math.abs(storedDate.getTime() - now.getTime()) / 60000; - return diff <= 5; - } - } - return !!(bid.params.website && bid.params.language) && !hasNoAd(); + return !!(bid.params.website && bid.params.language); } - /** * Make a server request from the list of BidRequests. * @@ -107,7 +113,7 @@ function isBidRequestValid(bid) { */ function buildRequests(bidRequests, bidderRequest) { - const request = _createServerRequest(bidRequests, bidderRequest); + var request = _createServerRequest(bidRequests, bidderRequest); return request; } /** @@ -119,34 +125,23 @@ function buildRequests(bidRequests, bidderRequest) { */ function interpretResponse(serverResponse, bidRequest) { - const itemName = `PX_NoAds_${websiteFromBidRequest(bidRequest)}`; - if (serverResponse.body.length > 0) { - storage.removeDataFromLocalStorage(itemName, true); - return serverResponse.body.map(_createBidResponse); - } else { - storage.setDataInLocalStorage(itemName, new Date()); - return []; - } + return serverResponse.body.map(_createBidResponse); } -const websiteFromBidRequest = function(bidR) { - if (bidR.data) { - return JSON.parse(bidR.data).website - } else if (bidR.params.website) { - return bidR.params.website; - } -} +function _assignFloor(bid) { + if (typeof bid.getFloor === 'function') { + var floorInfo = bid.getFloor({ + currency: 'EUR', + mediaType: 'banner', + size: '*', + }); -/** - * Register the user sync pixels which should be dropped after the auction. - * - * @param syncOptions Which user syncs are allowed? - * @param serverResponses List of server's responses. - * @return The user syncs which should be dropped. - */ + if (floorInfo.currency === 'EUR') { + return floorInfo.floor; + } + } -function getUserSyncs(syncOptions, serverResponses) { - return []; + return null; } export const spec = { @@ -154,7 +149,7 @@ export const spec = { isBidRequestValid: isBidRequestValid, buildRequests: buildRequests, interpretResponse: interpretResponse, - getUserSyncs: getUserSyncs + }; registerBidder(spec); diff --git a/test/spec/modules/proxistoreBidAdapter_spec.js b/test/spec/modules/proxistoreBidAdapter_spec.js index 410c3c59fb6..f98d9633320 100644 --- a/test/spec/modules/proxistoreBidAdapter_spec.js +++ b/test/spec/modules/proxistoreBidAdapter_spec.js @@ -1,55 +1,58 @@ import { expect } from 'chai'; let { spec } = require('modules/proxistoreBidAdapter'); - const BIDDER_CODE = 'proxistore'; describe('ProxistoreBidAdapter', function () { const bidderRequest = { - 'bidderCode': BIDDER_CODE, - 'auctionId': '1025ba77-5463-4877-b0eb-14b205cb9304', - 'bidderRequestId': '10edf38ec1a719', - 'gdprConsent': { - 'gdprApplies': true, - 'consentString': 'CONSENT_STRING', - 'vendorData': { - 'vendorConsents': { - '418': true - } - } - } + bidderCode: BIDDER_CODE, + auctionId: '1025ba77-5463-4877-b0eb-14b205cb9304', + bidderRequestId: '10edf38ec1a719', + gdprConsent: { + gdprApplies: true, + consentString: 'CONSENT_STRING', + vendorData: { + vendorConsents: { + 418: true, + }, + }, + }, }; let bid = { sizes: [[300, 600]], params: { website: 'example.fr', - language: 'fr' + language: 'fr', + }, + ortb2: { + user: { ext: { data: { segments: [], contextual_categories: {} } } }, }, auctionId: 442133079, bidId: 464646969, - transactionId: 511916005 + transactionId: 511916005, }; describe('isBidRequestValid', function () { it('it should be true if required params are presents and there is no info in the local storage', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - - it('it should be false if the value in the localstorage is less than 5minutes of the actual time', function() { + it('it should be false if the value in the localstorage is less than 5minutes of the actual time', function () { const date = new Date(); - date.setMinutes(date.getMinutes() - 1) - localStorage.setItem(`PX_NoAds_${bid.params.website}`, date) - expect(spec.isBidRequestValid(bid)).to.equal(false); + date.setMinutes(date.getMinutes() - 1); + localStorage.setItem(`PX_NoAds_${bid.params.website}`, date); + expect(spec.isBidRequestValid(bid)).to.equal(true); }); - - it('it should be true if the value in the localstorage is more than 5minutes of the actual time', function() { + it('it should be true if the value in the localstorage is more than 5minutes of the actual time', function () { const date = new Date(); - date.setMinutes(date.getMinutes() - 10) - localStorage.setItem(`PX_NoAds_${bid.params.website}`, date) + date.setMinutes(date.getMinutes() - 10); + localStorage.setItem(`PX_NoAds_${bid.params.website}`, date); expect(spec.isBidRequestValid(bid)).to.equal(true); }); }); - describe('buildRequests', function () { - const url = 'https://abs.proxistore.com/fr/v3/rtb/prebid/multi'; - const request = spec.buildRequests([bid], bidderRequest); + const url = { + cookieBase: 'https://abs.proxistore.com/fr/v3/rtb/prebid/multi', + cookieLess: + 'https://abs.proxistore.com/fr/v3/rtb/prebid/multi/cookieless', + }; + let request = spec.buildRequests([bid], bidderRequest); it('should return a valid object', function () { expect(request).to.be.an('object'); expect(request.method).to.exist; @@ -59,16 +62,22 @@ describe('ProxistoreBidAdapter', function () { it('request method should be POST', function () { expect(request.method).to.equal('POST'); }); - it('should contain a valid url', function () { - expect(request.url).equal(url); - }); it('should have the value consentGiven to true bc we have 418 in the vendor list', function () { const data = JSON.parse(request.data); - - expect(data.gdpr.consentString).equal(bidderRequest.gdprConsent.consentString); + expect(data.gdpr.consentString).equal( + bidderRequest.gdprConsent.consentString + ); expect(data.gdpr.applies).to.be.true; expect(data.gdpr.consentGiven).to.be.true; }); + it('should contain a valid url', function () { + // has gdpr consent + expect(request.url).equal(url.cookieBase); + // doens't have gpdr consent + bidderRequest.gdprConsent.vendorData = null; + request = spec.buildRequests([bid], bidderRequest); + expect(request.url).equal(url.cookieLess); + }); it('should have a property a length of bids equal to one if there is only one bid', function () { const data = JSON.parse(request.data); expect(data.hasOwnProperty('bids')).to.be.true; @@ -77,51 +86,51 @@ describe('ProxistoreBidAdapter', function () { expect(data.bids[0].hasOwnProperty('id')).to.be.true; expect(data.bids[0].sizes).to.be.an('array'); }); - }); + it('should correctly set bidfloor on imp when getfloor in scope', function () { + let data = JSON.parse(request.data); + expect(data.bids[0].floor).to.be.null; - describe('interpretResponse', function () { - const responses = { - body: - [{ - cpm: 6.25, - creativeId: '48fd47c9-ce35-4fda-804b-17e16c8c36ac', - currency: 'EUR', - dealId: '2019-10_e3ecad8e-d07a-4c90-ad46-cd0f306c8960', - height: 600, - netRevenue: true, - requestId: '923756713', - ttl: 10, - vastUrl: null, - vastXml: null, - width: 300, - }] - }; - const badResponse = { body: [] }; - const interpretedResponse = spec.interpretResponse(responses, bid)[0]; - it('should send an empty array if body is empty', function () { - expect(spec.interpretResponse(badResponse, bid)).to.be.an('array'); - expect(spec.interpretResponse(badResponse, bid).length).equal(0); - }); - it('should interpret the response correctly if it is valid', function () { - expect(interpretedResponse.cpm).equal(6.25); - expect(interpretedResponse.creativeId).equal('48fd47c9-ce35-4fda-804b-17e16c8c36ac'); - expect(interpretedResponse.currency).equal('EUR'); - expect(interpretedResponse.height).equal(600); - expect(interpretedResponse.width).equal(300); - expect(interpretedResponse.requestId).equal('923756713'); - expect(interpretedResponse.netRevenue).to.be.true; - expect(interpretedResponse.netRevenue).to.be.true; - }); - it('should have a value in the local storage if the response is empty', function() { - spec.interpretResponse(badResponse, bid); - expect(localStorage.getItem(`PX_NoAds_${bid.params.website}`)).to.be.string; + // make it respond with a non USD floor should not send it + bid.getFloor = function () { + return { currency: 'EUR', floor: 1.0 }; + }; + let req = spec.buildRequests([bid], bidderRequest); + data = JSON.parse(req.data); + expect(data.bids[0].floor).equal(1); + bid.getFloor = function () { + return { currency: 'USD', floor: 1.0 }; + }; + req = spec.buildRequests([bid], bidderRequest); + data = JSON.parse(req.data); + expect(data.bids[0].floor).to.be.null; }); }); + describe('interpretResponse', function() { + const emptyResponseParam = {body: []}; + const fakeResponseParam = {body: [ + { ad: '', + cpm: 6.25, + creativeId: '22c3290b-8cd5-4cd6-8e8c-28a2de180ccd', + currency: 'EUR', + dealId: '2021-03_a63ec55e-b9bb-4ca4-b2c9-f456be67e656', + height: 600, + netRevenue: true, + requestId: '3543724f2a033c9', + segments: [], + ttl: 10, + vastUrl: null, + vastXml: null, + width: 300} + ] + }; - describe('interpretResponse', function () { - it('should aways return an empty array', function () { - expect(spec.getUserSyncs()).to.be.an('array'); - expect(spec.getUserSyncs().length).equal(0); + it('should always return an array', function() { + let response = spec.interpretResponse(emptyResponseParam, bid); + expect(response).to.be.an('array'); + expect(response.length).equal(0); + response = spec.interpretResponse(fakeResponseParam, bid); + expect(response).to.be.an('array'); + expect(response.length).equal(1); }); }); }); From 1a7a7a3600521c413bedd6c8aefd98082815ac58 Mon Sep 17 00:00:00 2001 From: guiann Date: Wed, 31 Mar 2021 21:03:07 +0200 Subject: [PATCH 15/93] AdYoulike Bid Adapter: Add an "Insertion" tracking for Native mediatype (#6481) * add insertion event * add missing campaign ID parameter * update unit test with new tracking checked --- modules/adyoulikeBidAdapter.js | 5 ++++- test/spec/modules/adyoulikeBidAdapter_spec.js | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 385ada65538..00c7c02dc72 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -339,9 +339,12 @@ function getNativeAssets(response, nativeConfig) { var impressionUrl = adJson.TrackingPrefix + '/pixel?event_kind=IMPRESSION&attempt=' + adJson.Attempt; + var insertionUrl = adJson.TrackingPrefix + + '/pixel?event_kind=INSERTION&attempt=' + adJson.Attempt; if (adJson.Campaign) { impressionUrl += '&campaign=' + adJson.Campaign; + insertionUrl += '&campaign=' + adJson.Campaign; } native.clickUrl = adJson.TrackingPrefix + '/ar?event_kind=CLICK&attempt=' + adJson.Attempt + @@ -355,7 +358,7 @@ function getNativeAssets(response, nativeConfig) { native.impressionTrackers = []; } - native.impressionTrackers.push(impressionUrl); + native.impressionTrackers.push(impressionUrl, insertionUrl); } Object.keys(nativeConfig).map(function(key, index) { diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index 8f60cbbbe3e..c432ad1b32d 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -596,7 +596,8 @@ describe('Adyoulike Adapter', function () { }, impressionTrackers: [ 'https://testPixelIMP.com/fake', - 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991' + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=INSERTION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991' ], javascriptTrackers: [ 'https://testJsIMP.com/fake.js' From 3876d670b802b8beea536ca8bed25f52545faf1a Mon Sep 17 00:00:00 2001 From: onlsol <48312668+onlsol@users.noreply.github.com> Date: Wed, 31 Mar 2021 21:06:37 +0200 Subject: [PATCH 16/93] Dspx Bid Adapter : add user sync support (#6456) * Add sync support for dspx adapter * Dspx Bid Adapter : add user sync support Co-authored-by: Alexander --- modules/dspxBidAdapter.js | 37 ++++++++++++++++ test/spec/modules/dspxBidAdapter_spec.js | 56 ++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/modules/dspxBidAdapter.js b/modules/dspxBidAdapter.js index d05549601e1..dd49a744225 100644 --- a/modules/dspxBidAdapter.js +++ b/modules/dspxBidAdapter.js @@ -123,7 +123,44 @@ export const spec = { bidResponses.push(bidResponse); } return bidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + if (!serverResponses || serverResponses.length === 0) { + return []; + } + + const syncs = [] + + let gdprParams = ''; + if (gdprConsent) { + if ('gdprApplies' in gdprConsent && typeof gdprConsent.gdprApplies === 'boolean') { + gdprParams = `gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + gdprParams = `gdpr_consent=${gdprConsent.consentString}`; + } + } + + if (syncOptions.iframeEnabled) { + serverResponses[0].body.userSync.iframeUrl.forEach((url) => syncs.push({ + type: 'iframe', + url: appendToUrl(url, gdprParams) + })); + } + if (syncOptions.pixelEnabled && serverResponses.length > 0) { + serverResponses[0].body.userSync.imageUrl.forEach((url) => syncs.push({ + type: 'image', + url: appendToUrl(url, gdprParams) + })); + } + return syncs; + } +} + +function appendToUrl(url, what) { + if (!what) { + return url; } + return url + (url.indexOf('?') !== -1 ? '&' : '?') + what; } function objectToQueryString(obj, prefix) { diff --git a/test/spec/modules/dspxBidAdapter_spec.js b/test/spec/modules/dspxBidAdapter_spec.js index cf36c3f62c4..87752f7747a 100644 --- a/test/spec/modules/dspxBidAdapter_spec.js +++ b/test/spec/modules/dspxBidAdapter_spec.js @@ -283,4 +283,60 @@ describe('dspxAdapter', function () { expect(result.length).to.equal(0); }); }); + + describe(`getUserSyncs test usage`, function () { + let serverResponses; + + beforeEach(function () { + serverResponses = [{ + body: { + requestId: '23beaa6af6cdde', + cpm: 0.5, + width: 0, + height: 0, + creativeId: 100500, + dealId: '', + currency: 'EUR', + netRevenue: true, + ttl: 300, + type: 'sspHTML', + ad: '', + userSync: { + iframeUrl: ['anyIframeUrl?a=1'], + imageUrl: ['anyImageUrl', 'anyImageUrl2'] + } + } + }]; + }); + + it(`return value should be an array`, function () { + expect(spec.getUserSyncs({ iframeEnabled: true })).to.be.an('array'); + }); + it(`array should have only one object and it should have a property type = 'iframe'`, function () { + expect(spec.getUserSyncs({ iframeEnabled: true }, serverResponses).length).to.be.equal(1); + let [userSync] = spec.getUserSyncs({ iframeEnabled: true }, serverResponses); + expect(userSync).to.have.property('type'); + expect(userSync.type).to.be.equal('iframe'); + }); + it(`we have valid sync url for iframe`, function () { + let [userSync] = spec.getUserSyncs({ iframeEnabled: true }, serverResponses, {consentString: 'anyString'}); + expect(userSync.url).to.be.equal('anyIframeUrl?a=1&gdpr_consent=anyString') + expect(userSync.type).to.be.equal('iframe'); + }); + it(`we have valid sync url for image`, function () { + let [userSync] = spec.getUserSyncs({ pixelEnabled: true }, serverResponses, {gdprApplies: true, consentString: 'anyString'}); + expect(userSync.url).to.be.equal('anyImageUrl?gdpr=1&gdpr_consent=anyString') + expect(userSync.type).to.be.equal('image'); + }); + it(`we have valid sync url for image and iframe`, function () { + let userSync = spec.getUserSyncs({ iframeEnabled: true, pixelEnabled: true }, serverResponses, {gdprApplies: true, consentString: 'anyString'}); + expect(userSync.length).to.be.equal(3); + expect(userSync[0].url).to.be.equal('anyIframeUrl?a=1&gdpr=1&gdpr_consent=anyString') + expect(userSync[0].type).to.be.equal('iframe'); + expect(userSync[1].url).to.be.equal('anyImageUrl?gdpr=1&gdpr_consent=anyString') + expect(userSync[1].type).to.be.equal('image'); + expect(userSync[2].url).to.be.equal('anyImageUrl2?gdpr=1&gdpr_consent=anyString') + expect(userSync[2].type).to.be.equal('image'); + }); + }); }); From d9e3b12943dbb8f2b241801d6d736baa7f20e3c2 Mon Sep 17 00:00:00 2001 From: mmoschovas <63253416+mmoschovas@users.noreply.github.com> Date: Wed, 31 Mar 2021 15:15:06 -0400 Subject: [PATCH 17/93] Multibid Module: add new module to handle multiple bids from single bidder & update rubicon adapter (#6404) * Multibid module - create new module - Expands the number of key value pairs going to the ad server in the normal Prebid way by establishing the concept of a "dynamic alias" First commit * Continued updates from 1st commit * Adding logWarn for filtered bids * Update to include passing multibid configuration to PBS requests * Update to rubicon bid adapter to pass query param rp_maxbids value taken from bidderRequest.bidLimit * Update to config to look for camelcase property names according to spec. These convert to all lowercase when passed to PBS endpoint * Adjust RP adapter to always include maxbids value - default is 1 * Added support for bidders array in multibid config * Fixed floor comparison to be <= bid cpm as oppossed to just < bid cpm. Updated md file to fix camelCase tpyo * Update to include originalBidderRequest in video call to prebid cache * Update to ignore adpod bids from multibid and allow them to return as normal bids --- modules/multibid/index.js | 234 +++++ modules/multibid/index.md | 40 + modules/prebidServerBidAdapter/index.js | 15 + modules/rubiconAnalyticsAdapter.js | 8 + modules/rubiconBidAdapter.js | 23 +- src/auction.js | 2 +- src/targeting.js | 46 +- test/spec/modules/multibid_spec.js | 845 ++++++++++++++++++ .../modules/prebidServerBidAdapter_spec.js | 24 + .../modules/rubiconAnalyticsAdapter_spec.js | 26 + test/spec/modules/rubiconBidAdapter_spec.js | 180 +++- 11 files changed, 1420 insertions(+), 23 deletions(-) create mode 100644 modules/multibid/index.js create mode 100644 modules/multibid/index.md create mode 100644 test/spec/modules/multibid_spec.js diff --git a/modules/multibid/index.js b/modules/multibid/index.js new file mode 100644 index 00000000000..dd4999b2dca --- /dev/null +++ b/modules/multibid/index.js @@ -0,0 +1,234 @@ +/** + * This module adds Multibid support to prebid.js + * @module modules/multibid + */ + +import {config} from '../../src/config.js'; +import {setupBeforeHookFnOnce, getHook} from '../../src/hook.js'; +import * as utils from '../../src/utils.js'; +import events from '../../src/events.js'; +import CONSTANTS from '../../src/constants.json'; +import {addBidderRequests} from '../../src/auction.js'; +import {getHighestCpmBidsFromBidPool, sortByDealAndPriceBucketOrCpm} from '../../src/targeting.js'; + +const MODULE_NAME = 'multibid'; +let hasMultibid = false; +let multiConfig = {}; +let multibidUnits = {}; + +// Storing this globally on init for easy reference to configuration +config.getConfig(MODULE_NAME, conf => { + if (!Array.isArray(conf.multibid) || !conf.multibid.length || !validateMultibid(conf.multibid)) return; + + resetMultiConfig(); + hasMultibid = true; + + conf.multibid.forEach(entry => { + if (entry.bidder) { + multiConfig[entry.bidder] = { + maxbids: entry.maxBids, + prefix: entry.targetBiddercodePrefix + } + } else { + entry.bidders.forEach(key => { + multiConfig[key] = { + maxbids: entry.maxBids, + prefix: entry.targetBiddercodePrefix + } + }); + } + }); +}); + +/** + * @summary validates multibid configuration entries + * @param {Object[]} multibid - example [{bidder: 'bidderA', maxbids: 2, prefix: 'bidA'}, {bidder: 'bidderB', maxbids: 2}] + * @return {Boolean} +*/ +export function validateMultibid(conf) { + let check = true; + let duplicate = conf.filter(entry => { + // Check if entry.bidder is not defined or typeof string, filter entry and reset configuration + if ((!entry.bidder || typeof entry.bidder !== 'string') && (!entry.bidders || !Array.isArray(entry.bidders))) { + utils.logWarn('Filtering multibid entry. Missing required bidder or bidders property.'); + check = false; + return false; + } + + return true; + }).map(entry => { + // Check if entry.maxbids is not defined, not typeof number, or less than 1, set maxbids to 1 and reset configuration + // Check if entry.maxbids is greater than 9, set maxbids to 9 and reset configuration + if (typeof entry.maxBids !== 'number' || entry.maxBids < 1 || entry.maxBids > 9) { + entry.maxBids = (typeof entry.maxBids !== 'number' || entry.maxBids < 1) ? 1 : 9; + check = false; + } + + return entry; + }); + + if (!check) config.setConfig({multibid: duplicate}); + + return check; +} + +/** + * @summary addBidderRequests before hook + * @param {Function} fn reference to original function (used by hook logic) + * @param {Object[]} array containing copy of each bidderRequest object +*/ +export function adjustBidderRequestsHook(fn, bidderRequests) { + bidderRequests.map(bidRequest => { + // Loop through bidderRequests and check if bidderCode exists in multiconfig + // If true, add bidderRequest.bidLimit to bidder request + if (multiConfig[bidRequest.bidderCode]) { + bidRequest.bidLimit = multiConfig[bidRequest.bidderCode].maxbids + } + return bidRequest; + }) + + fn.call(this, bidderRequests); +} + +/** + * @summary addBidResponse before hook + * @param {Function} fn reference to original function (used by hook logic) + * @param {String} ad unit code for bid + * @param {Object} bid object +*/ +export function addBidResponseHook(fn, adUnitCode, bid) { + let floor = utils.deepAccess(bid, 'floorData.floorValue'); + + if (!config.getConfig('multibid')) resetMultiConfig(); + // Checks if multiconfig exists and bid bidderCode exists within config and is an adpod bid + // Else checks if multiconfig exists and bid bidderCode exists within config + // Else continue with no modifications + if (hasMultibid && multiConfig[bid.bidderCode] && utils.deepAccess(bid, 'video.context') === 'adpod') { + fn.call(this, adUnitCode, bid); + } else if (hasMultibid && multiConfig[bid.bidderCode]) { + // Set property multibidPrefix on bid + if (multiConfig[bid.bidderCode].prefix) bid.multibidPrefix = multiConfig[bid.bidderCode].prefix; + bid.originalBidder = bid.bidderCode; + // Check if stored bids for auction include adUnitCode.bidder and max limit not reach for ad unit + if (utils.deepAccess(multibidUnits, `${adUnitCode}.${bid.bidderCode}`)) { + // Store request id under new property originalRequestId, create new unique bidId, + // and push bid into multibid stored bids for auction if max not reached and bid cpm above floor + if (!multibidUnits[adUnitCode][bid.bidderCode].maxReached && (!floor || floor <= bid.cpm)) { + bid.originalRequestId = bid.requestId; + + bid.requestId = utils.getUniqueIdentifierStr(); + multibidUnits[adUnitCode][bid.bidderCode].ads.push(bid); + + let length = multibidUnits[adUnitCode][bid.bidderCode].ads.length; + + if (multiConfig[bid.bidderCode].prefix) bid.targetingBidder = multiConfig[bid.bidderCode].prefix + length; + if (length === multiConfig[bid.bidderCode].maxbids) multibidUnits[adUnitCode][bid.bidderCode].maxReached = true; + + fn.call(this, adUnitCode, bid); + } else { + utils.logWarn(`Filtering multibid received from bidder ${bid.bidderCode}: ` + ((multibidUnits[adUnitCode][bid.bidderCode].maxReached) ? `Maximum bid limit reached for ad unit code ${adUnitCode}` : 'Bid cpm under floors value.')); + } + } else { + if (utils.deepAccess(bid, 'floorData.floorValue')) utils.deepSetValue(multibidUnits, `${adUnitCode}.${bid.bidderCode}`, {floor: utils.deepAccess(bid, 'floorData.floorValue')}); + + utils.deepSetValue(multibidUnits, `${adUnitCode}.${bid.bidderCode}`, {ads: [bid]}); + if (multibidUnits[adUnitCode][bid.bidderCode].ads.length === multiConfig[bid.bidderCode].maxbids) multibidUnits[adUnitCode][bid.bidderCode].maxReached = true; + + fn.call(this, adUnitCode, bid); + } + } else { + fn.call(this, adUnitCode, bid); + } +} + +/** +* A descending sort function that will sort the list of objects based on the following: +* - bids without dynamic aliases are sorted before bids with dynamic aliases +*/ +export function sortByMultibid(a, b) { + if (a.bidder !== a.bidderCode && b.bidder === b.bidderCode) { + return 1; + } + + if (a.bidder === a.bidderCode && b.bidder !== b.bidderCode) { + return -1; + } + + return 0; +} + +/** + * @summary getHighestCpmBidsFromBidPool before hook + * @param {Function} fn reference to original function (used by hook logic) + * @param {Object[]} array of objects containing all bids from bid pool + * @param {Function} function to reduce to only highest cpm value for each bidderCode + * @param {Number} adUnit bidder targeting limit, default set to 0 + * @param {Boolean} default set to false, this hook modifies targeting and sets to true +*/ +export function targetBidPoolHook(fn, bidsReceived, highestCpmCallback, adUnitBidLimit = 0, hasModified = false) { + if (!config.getConfig('multibid')) resetMultiConfig(); + if (hasMultibid) { + const dealPrioritization = config.getConfig('sendBidsControl.dealPrioritization'); + let modifiedBids = []; + let buckets = utils.groupBy(bidsReceived, 'adUnitCode'); + let bids = [].concat.apply([], Object.keys(buckets).reduce((result, slotId) => { + let bucketBids = []; + // Get bids and group by property originalBidder + let bidsByBidderName = utils.groupBy(buckets[slotId], 'originalBidder'); + let adjustedBids = [].concat.apply([], Object.keys(bidsByBidderName).map(key => { + // Reset all bidderCodes to original bidder values and sort by CPM + return bidsByBidderName[key].sort((bidA, bidB) => { + if (bidA.originalBidder && bidA.originalBidder !== bidA.bidderCode) bidA.bidderCode = bidA.originalBidder; + if (bidA.originalBidder && bidB.originalBidder !== bidB.bidderCode) bidB.bidderCode = bidB.originalBidder; + return bidA.cpm > bidB.cpm ? -1 : (bidA.cpm < bidB.cpm ? 1 : 0); + }).map((bid, index) => { + // For each bid (post CPM sort), set dynamic bidderCode using prefix and index if less than maxbid amount + if (utils.deepAccess(multiConfig, `${bid.bidderCode}.prefix`) && index !== 0 && index < multiConfig[bid.bidderCode].maxbids) { + bid.bidderCode = multiConfig[bid.bidderCode].prefix + (index + 1); + } + + return bid + }) + })); + // Get adjustedBids by bidderCode and reduce using highestCpmCallback + let bidsByBidderCode = utils.groupBy(adjustedBids, 'bidderCode'); + Object.keys(bidsByBidderCode).forEach(key => bucketBids.push(bidsByBidderCode[key].reduce(highestCpmCallback))); + // if adUnitBidLimit is set, pass top N number bids + if (adUnitBidLimit > 0) { + bucketBids = dealPrioritization ? bucketBids.sort(sortByDealAndPriceBucketOrCpm(true)) : bucketBids.sort((a, b) => b.cpm - a.cpm); + bucketBids.sort(sortByMultibid); + modifiedBids.push(...bucketBids.slice(0, adUnitBidLimit)); + } else { + modifiedBids.push(...bucketBids); + } + + return [].concat.apply([], modifiedBids); + }, [])); + + fn.call(this, bids, highestCpmCallback, adUnitBidLimit, true); + } else { + fn.call(this, bidsReceived, highestCpmCallback, adUnitBidLimit); + } +} + +/** +* Resets globally stored multibid configuration +*/ +export const resetMultiConfig = () => { hasMultibid = false; multiConfig = {}; }; + +/** +* Resets globally stored multibid ad unit bids +*/ +export const resetMultibidUnits = () => multibidUnits = {}; + +/** +* Set up hooks on init +*/ +function init() { + events.on(CONSTANTS.EVENTS.AUCTION_INIT, resetMultibidUnits); + setupBeforeHookFnOnce(addBidderRequests, adjustBidderRequestsHook); + getHook('addBidResponse').before(addBidResponseHook, 3); + setupBeforeHookFnOnce(getHighestCpmBidsFromBidPool, targetBidPoolHook); +} + +init(); diff --git a/modules/multibid/index.md b/modules/multibid/index.md new file mode 100644 index 00000000000..a431d9b7960 --- /dev/null +++ b/modules/multibid/index.md @@ -0,0 +1,40 @@ +# Overview + +Module Name: multibid + +Purpose: To expand the number of key value pairs going to the ad server in the normal Prebid way by establishing the concept of a "dynamic alias" -- a bidder code that exists only on the response, not in the adunit. + + +# Description +Allowing a single bidder to multi-bid into an auction has several use cases: + +1. allows a bidder to provide both outstream and banner +2. supports the video VAST fallback scenario +3. allows one bid to be blocked in the ad server and the second one still considered +4. add extra high-value bids to the cache for future refreshes + + +# Example of using config +``` + pbjs.setConfig({ + multibid: [{ + bidder: "bidderA", + maxBids: 3, + targetBiddercodePrefix: "bidA" + },{ + bidder: "bidderB", + maxBids: 3, + targetBiddercodePrefix: "bidB" + },{ + bidder: "bidderC", + maxBids: 3 + },{ + bidders: ["bidderD", "bidderE"], + maxBids: 2 + }] + }); +``` + +# Please Note: +- + diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 453f9118766..ff326f25840 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -740,6 +740,21 @@ const OPEN_RTB_PROTOCOL = { utils.deepSetValue(request, 'ext.prebid.data.eidpermissions', eidPermissions); } + const multibid = config.getConfig('multibid'); + if (multibid) { + utils.deepSetValue(request, 'ext.prebid.multibid', multibid.reduce((result, i) => { + let obj = {}; + + Object.keys(i).forEach(key => { + obj[key.toLowerCase()] = i[key]; + }); + + result.push(obj); + + return result; + }, [])); + } + if (bidRequests) { if (firstBidRequest.gdprConsent) { // note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index f463226ea49..90575cf8cf1 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -119,6 +119,7 @@ function sendMessage(auctionId, bidWonId) { function formatBid(bid) { return utils.pick(bid, [ 'bidder', + 'bidderDetail', 'bidId', bidId => utils.deepAccess(bid, 'bidResponse.pbsBidId') || utils.deepAccess(bid, 'bidResponse.seatBidId') || bidId, 'status', 'error', @@ -673,6 +674,13 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { break; case BID_RESPONSE: let auctionEntry = cache.auctions[args.auctionId]; + + if (!auctionEntry.bids[args.requestId] && args.originalRequestId) { + auctionEntry.bids[args.requestId] = {...auctionEntry.bids[args.originalRequestId]}; + auctionEntry.bids[args.requestId].bidId = args.requestId; + auctionEntry.bids[args.requestId].bidderDetail = args.targetingBidder; + } + let bid = auctionEntry.bids[args.requestId]; // If floor resolved gptSlot but we have not yet, then update the adUnit to have the adSlot name if (!utils.deepAccess(bid, 'adUnit.gam.adSlot') && utils.deepAccess(args, 'floorData.matchedFields.gptSlot')) { diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 9905498edee..7d1659fb0f7 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -256,6 +256,21 @@ export const spec = { utils.deepSetValue(data, 'source.ext.schain', bidRequest.schain); } + const multibid = config.getConfig('multibid'); + if (multibid) { + utils.deepSetValue(data, 'ext.prebid.multibid', multibid.reduce((result, i) => { + let obj = {}; + + Object.keys(i).forEach(key => { + obj[key.toLowerCase()] = i[key]; + }); + + result.push(obj); + + return result; + }, [])); + } + applyFPD(bidRequest, VIDEO, data); // if storedAuctionResponse has been set, pass SRID @@ -510,6 +525,8 @@ export const spec = { data['us_privacy'] = encodeURIComponent(bidderRequest.uspConsent); } + data['rp_maxbids'] = bidderRequest.bidLimit || 1; + applyFPD(bidRequest, BANNER, data); if (config.getConfig('coppa') === true) { @@ -640,6 +657,8 @@ export const spec = { } let ads = responseObj.ads; + let lastImpId; + let multibid = 0; // video ads array is wrapped in an object if (typeof bidRequest === 'object' && !Array.isArray(bidRequest) && bidType(bidRequest) === 'video' && typeof ads === 'object') { @@ -652,12 +671,14 @@ export const spec = { } return ads.reduce((bids, ad, i) => { + (ad.impression_id && lastImpId === ad.impression_id) ? multibid++ : lastImpId = ad.impression_id; + if (ad.status !== 'ok') { return bids; } // associate bidRequests; assuming ads matches bidRequest - const associatedBidRequest = Array.isArray(bidRequest) ? bidRequest[i] : bidRequest; + const associatedBidRequest = Array.isArray(bidRequest) ? bidRequest[i - multibid] : bidRequest; if (associatedBidRequest && typeof associatedBidRequest === 'object') { let bid = { diff --git a/src/auction.js b/src/auction.js index 7005d56827e..cb7e0f60352 100644 --- a/src/auction.js +++ b/src/auction.js @@ -452,7 +452,7 @@ export function addBidToAuction(auctionInstance, bidResponse) { function tryAddVideoBid(auctionInstance, bidResponse, bidRequests, afterBidAdded) { let addBid = true; - const bidderRequest = getBidRequest(bidResponse.requestId, [bidRequests]); + const bidderRequest = getBidRequest(bidResponse.originalRequestId || bidResponse.requestId, [bidRequests]); const videoMediaType = bidderRequest && deepAccess(bidderRequest, 'mediaTypes.video'); const context = videoMediaType && deepAccess(videoMediaType, 'context'); diff --git a/src/targeting.js b/src/targeting.js index cb53ea0fed3..365453e1e8f 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -4,6 +4,7 @@ import { NATIVE_TARGETING_KEYS } from './native.js'; import { auctionManager } from './auctionManager.js'; import { sizeSupported } from './sizeMapping.js'; import { ADPOD } from './mediaTypes.js'; +import { hook } from './hook.js'; import includes from 'core-js-pure/features/array/includes.js'; import find from 'core-js-pure/features/array/find.js'; @@ -33,26 +34,31 @@ export let filters = { // If two bids are found for same adUnitCode, we will use the highest one to take part in auction // This can happen in case of concurrent auctions // If adUnitBidLimit is set above 0 return top N number of bids -export function getHighestCpmBidsFromBidPool(bidsReceived, highestCpmCallback, adUnitBidLimit = 0) { - const bids = []; - const dealPrioritization = config.getConfig('sendBidsControl.dealPrioritization'); - // bucket by adUnitcode - let buckets = groupBy(bidsReceived, 'adUnitCode'); - // filter top bid for each bucket by bidder - Object.keys(buckets).forEach(bucketKey => { - let bucketBids = []; - let bidsByBidder = groupBy(buckets[bucketKey], 'bidderCode'); - Object.keys(bidsByBidder).forEach(key => bucketBids.push(bidsByBidder[key].reduce(highestCpmCallback))); - // if adUnitBidLimit is set, pass top N number bids - if (adUnitBidLimit > 0) { - bucketBids = dealPrioritization ? bucketBids.sort(sortByDealAndPriceBucketOrCpm(true)) : bucketBids.sort((a, b) => b.cpm - a.cpm); - bids.push(...bucketBids.slice(0, adUnitBidLimit)); - } else { - bids.push(...bucketBids); - } - }); - return bids; -} +export const getHighestCpmBidsFromBidPool = hook('sync', function(bidsReceived, highestCpmCallback, adUnitBidLimit = 0, hasModified = false) { + if (!hasModified) { + const bids = []; + const dealPrioritization = config.getConfig('sendBidsControl.dealPrioritization'); + // bucket by adUnitcode + let buckets = groupBy(bidsReceived, 'adUnitCode'); + // filter top bid for each bucket by bidder + Object.keys(buckets).forEach(bucketKey => { + let bucketBids = []; + let bidsByBidder = groupBy(buckets[bucketKey], 'bidderCode'); + Object.keys(bidsByBidder).forEach(key => bucketBids.push(bidsByBidder[key].reduce(highestCpmCallback))); + // if adUnitBidLimit is set, pass top N number bids + if (adUnitBidLimit > 0) { + bucketBids = dealPrioritization ? bucketBids.sort(sortByDealAndPriceBucketOrCpm(true)) : bucketBids.sort((a, b) => b.cpm - a.cpm); + bids.push(...bucketBids.slice(0, adUnitBidLimit)); + } else { + bids.push(...bucketBids); + } + }); + + return bids; + } + + return bidsReceived; +}) /** * A descending sort function that will sort the list of objects based on the following two dimensions: diff --git a/test/spec/modules/multibid_spec.js b/test/spec/modules/multibid_spec.js new file mode 100644 index 00000000000..e849392ee4b --- /dev/null +++ b/test/spec/modules/multibid_spec.js @@ -0,0 +1,845 @@ +import {expect} from 'chai'; +import { + validateMultibid, + adjustBidderRequestsHook, + addBidResponseHook, + resetMultibidUnits, + sortByMultibid, + targetBidPoolHook, + resetMultiConfig +} from 'modules/multibid/index.js'; +import {parse as parseQuery} from 'querystring'; +import {config} from 'src/config.js'; +import * as utils from 'src/utils.js'; +import find from 'core-js-pure/features/array/find.js'; + +describe('multibid adapter', function () { + let bidArray = [{ + 'bidderCode': 'bidderA', + 'requestId': '1c5f0a05d3629a', + 'cpm': 75, + 'originalCpm': 75, + 'bidder': 'bidderA', + }, { + 'bidderCode': 'bidderA', + 'cpm': 52, + 'requestId': '2e6j8s05r4363h', + 'originalCpm': 52, + 'bidder': 'bidderA', + }]; + let bidCacheArray = [{ + 'bidderCode': 'bidderA', + 'requestId': '1c5f0a05d3629a', + 'cpm': 66, + 'originalCpm': 66, + 'bidder': 'bidderA', + 'originalBidder': 'bidderA', + 'multibidPrefix': 'bidA' + }, { + 'bidderCode': 'bidderA', + 'cpm': 38, + 'requestId': '2e6j8s05r4363h', + 'originalCpm': 38, + 'bidder': 'bidderA', + 'originalBidder': 'bidderA', + 'multibidPrefix': 'bidA' + }]; + let bidArrayAlt = [{ + 'bidderCode': 'bidderA', + 'requestId': '1c5f0a05d3629a', + 'cpm': 29, + 'originalCpm': 29, + 'bidder': 'bidderA' + }, { + 'bidderCode': 'bidderA', + 'cpm': 52, + 'requestId': '2e6j8s05r4363h', + 'originalCpm': 52, + 'bidder': 'bidderA' + }, { + 'bidderCode': 'bidderB', + 'cpm': 3, + 'requestId': '7g8h5j45l7654i', + 'originalCpm': 3, + 'bidder': 'bidderB' + }, { + 'bidderCode': 'bidderC', + 'cpm': 12, + 'requestId': '9d7f4h56t6483u', + 'originalCpm': 12, + 'bidder': 'bidderC' + }]; + let bidderRequests = [{ + 'bidderCode': 'bidderA', + 'auctionId': 'e6bd4400-28fc-459b-9905-ad64d044daaa', + 'bidderRequestId': '10e78266423c0e', + 'bids': [{ + 'bidder': 'bidderA', + 'params': {'placementId': 1234567}, + 'crumbs': {'pubcid': 'fb4cfc66-ff3d-4fda-bef8-3f2cb6fe9412'}, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'test-div', + 'transactionId': 'c153f3da-84f0-4be8-95cb-0647c458bc60', + 'sizes': [[300, 250]], + 'bidId': '2408ef83b84c9d', + 'bidderRequestId': '10e78266423c0e', + 'auctionId': 'e6bd4400-28fc-459b-9905-ad64d044daaa', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }] + }, { + 'bidderCode': 'bidderB', + 'auctionId': 'e6bd4400-28fc-459b-9905-ad64d044daaa', + 'bidderRequestId': '10e78266423c0e', + 'bids': [{ + 'bidder': 'bidderB', + 'params': {'placementId': 1234567}, + 'crumbs': {'pubcid': 'fb4cfc66-ff3d-4fda-bef8-3f2cb6fe9412'}, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'test-div', + 'transactionId': 'c153f3da-84f0-4be8-95cb-0647c458bc60', + 'sizes': [[300, 250]], + 'bidId': '2408ef83b84c9d', + 'bidderRequestId': '10e78266423c0e', + 'auctionId': 'e6bd4400-28fc-459b-9905-ad64d044daaa', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + }] + }]; + + afterEach(function () { + config.resetConfig(); + resetMultiConfig(); + resetMultibidUnits(); + }); + + describe('adjustBidderRequestsHook', function () { + let result; + let callbackFn = function (bidderRequests) { + result = bidderRequests; + }; + + beforeEach(function() { + result = null; + }); + + it('does not modify bidderRequest when no multibid config exists', function () { + let bidRequests = [{...bidderRequests[0]}]; + + adjustBidderRequestsHook(callbackFn, bidRequests); + + expect(result).to.not.equal(null); + expect(result).to.deep.equal(bidRequests); + }); + + it('does modify bidderRequest when multibid config exists', function () { + let bidRequests = [{...bidderRequests[0]}]; + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2}]}); + + adjustBidderRequestsHook(callbackFn, [{...bidderRequests[0]}]); + + expect(result).to.not.equal(null); + expect(result).to.not.deep.equal(bidRequests); + expect(result[0].bidLimit).to.equal(2); + }); + + it('does modify bidderRequest when multibid config exists using bidders array', function () { + let bidRequests = [{...bidderRequests[0]}]; + + config.setConfig({multibid: [{bidders: ['bidderA'], maxBids: 2}]}); + + adjustBidderRequestsHook(callbackFn, [{...bidderRequests[0]}]); + + expect(result).to.not.equal(null); + expect(result).to.not.deep.equal(bidRequests); + expect(result[0].bidLimit).to.equal(2); + }); + + it('does only modifies bidderRequest when multibid config exists for bidder', function () { + let bidRequests = [{...bidderRequests[0]}, {...bidderRequests[1]}]; + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2}]}); + + adjustBidderRequestsHook(callbackFn, [{...bidderRequests[0]}, {...bidderRequests[1]}]); + + expect(result).to.not.equal(null); + expect(result[0]).to.not.deep.equal(bidRequests[0]); + expect(result[0].bidLimit).to.equal(2); + expect(result[1]).to.deep.equal(bidRequests[1]); + expect(result[1].bidLimit).to.equal(undefined); + }); + }); + + describe('addBidResponseHook', function () { + let result; + let callbackFn = function (adUnitCode, bid) { + result = { + 'adUnitCode': adUnitCode, + 'bid': bid + }; + }; + + beforeEach(function() { + result = null; + }); + + it('adds original bids and does not modify', function () { + let adUnitCode = 'test-div'; + let bids = [{...bidArray[0]}, {...bidArray[1]}]; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[0]); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[1]); + }); + + it('modifies and adds both bids based on multibid configuration', function () { + let adUnitCode = 'test-div'; + let bids = [{...bidArray[0]}, {...bidArray[1]}]; + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + + addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + + bids[0].multibidPrefix = 'bidA'; + bids[0].originalBidder = 'bidderA'; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[0]); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + + bids[1].multibidPrefix = 'bidA'; + bids[1].originalBidder = 'bidderA'; + bids[1].targetingBidder = 'bidA2'; + bids[1].originalRequestId = '2e6j8s05r4363h'; + + delete bids[1].requestId; + delete result.bid.requestId; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[1]); + }); + + it('only modifies bids defined in the multibid configuration', function () { + let adUnitCode = 'test-div'; + let bids = [{...bidArray[0]}, {...bidArray[1]}]; + + bids.push({ + 'bidderCode': 'bidderB', + 'cpm': 33, + 'requestId': '1j8s5f89y2345l', + 'originalCpm': 33, + 'bidder': 'bidderB', + }); + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + + addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + + bids[0].multibidPrefix = 'bidA'; + bids[0].originalBidder = 'bidderA'; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[0]); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + + bids[1].multibidPrefix = 'bidA'; + bids[1].originalBidder = 'bidderA'; + bids[1].targetingBidder = 'bidA2'; + bids[1].originalRequestId = '2e6j8s05r4363h'; + bids[1].requestId = result.bid.requestId; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[1]); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[2]}); + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[2]); + }); + + it('only modifies and returns bids under limit for a specifc bidder in the multibid configuration', function () { + let adUnitCode = 'test-div'; + let bids = [{...bidArray[0]}, {...bidArray[1]}]; + + bids.push({ + 'bidderCode': 'bidderA', + 'cpm': 33, + 'requestId': '1j8s5f89y2345l', + 'originalCpm': 33, + 'bidder': 'bidderA', + }); + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + + addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + + bids[0].multibidPrefix = 'bidA'; + bids[0].originalBidder = 'bidderA'; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[0]); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + + bids[1].multibidPrefix = 'bidA'; + bids[1].originalBidder = 'bidderA'; + bids[1].targetingBidder = 'bidA2'; + bids[1].originalRequestId = '2e6j8s05r4363h'; + bids[1].requestId = result.bid.requestId; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[1]); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[2]}); + + expect(result).to.equal(null); + }); + + it('if no prefix in multibid configuration, modifies and returns bids under limit without preifx property', function () { + let adUnitCode = 'test-div'; + let bids = [{...bidArray[0]}, {...bidArray[1]}]; + + bids.push({ + 'bidderCode': 'bidderA', + 'cpm': 33, + 'requestId': '1j8s5f89y2345l', + 'originalCpm': 33, + 'bidder': 'bidderA', + }); + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2}]}); + + addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + + bids[0].originalBidder = 'bidderA'; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[0]); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + + bids[1].originalBidder = 'bidderA'; + bids[1].originalRequestId = '2e6j8s05r4363h'; + bids[1].requestId = result.bid.requestId; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid).to.deep.equal(bids[1]); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[2]}); + + expect(result).to.equal(null); + }); + + it('does not include extra bids if cpm is less than floor value', function () { + let adUnitCode = 'test-div'; + let bids = [{...bidArrayAlt[1]}, {...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}]; + + bids.map(bid => { + bid.floorData = { + cpmAfterAdjustments: bid.cpm, + enforcements: { + enforceJS: true, + enforcePBS: false, + floorDeals: false, + bidAdjustment: true + }, + floorCurrency: 'USD', + floorRule: '*|banner', + floorRuleValue: 65, + floorValue: 65, + matchedFields: { + gptSlot: 'test-div', + mediaType: 'banner' + } + } + + return bid; + }); + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + + addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + + bids[0].multibidPrefix = 'bidA'; + bids[0].originalBidder = 'bidderA'; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid.bidder).to.equal('bidderA'); + expect(result.bid.targetingBidder).to.equal(undefined); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[1]}); + + expect(result).to.equal(null); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[2]}); + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid.bidder).to.equal('bidderB'); + expect(result.bid.targetingBidder).to.equal(undefined); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[3]}); + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid.bidder).to.equal('bidderC'); + expect(result.bid.targetingBidder).to.equal(undefined); + }); + + it('does include extra bids if cpm is not less than floor value', function () { + let adUnitCode = 'test-div'; + let bids = [{...bidArrayAlt[1]}, {...bidArrayAlt[0]}]; + + bids.map(bid => { + bid.floorData = { + cpmAfterAdjustments: bid.cpm, + enforcements: { + enforceJS: true, + enforcePBS: false, + floorDeals: false, + bidAdjustment: true + }, + floorCurrency: 'USD', + floorRule: '*|banner', + floorRuleValue: 25, + floorValue: 25, + matchedFields: { + gptSlot: 'test-div', + mediaType: 'banner' + } + } + + return bid; + }); + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + + addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + + bids[0].multibidPrefix = 'bidA'; + bids[0].originalBidder = 'bidderA'; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid.bidder).to.equal('bidderA'); + expect(result.bid.targetingBidder).to.equal(undefined); + + result = null; + + addBidResponseHook(callbackFn, adUnitCode, {...bids[0]}); + + bids[0].multibidPrefix = 'bidA'; + bids[0].originalBidder = 'bidderA'; + + expect(result).to.not.equal(null); + expect(result.adUnitCode).to.not.equal(null); + expect(result.adUnitCode).to.equal('test-div'); + expect(result.bid).to.not.equal(null); + expect(result.bid.bidder).to.equal('bidderA'); + expect(result.bid.targetingBidder).to.equal('bidA2'); + }); + }); + + describe('targetBidPoolHook', function () { + let result; + let bidResult; + let callbackFn = function (bidsReceived, highestCpmCallback, adUnitBidLimit = 0, hasModified = false) { + result = { + 'bidsReceived': bidsReceived, + 'adUnitBidLimit': adUnitBidLimit, + 'hasModified': hasModified + }; + }; + let bidResponseCallback = function (adUnitCode, bid) { + bidResult = bid; + }; + + beforeEach(function() { + result = null; + bidResult = null; + }); + + it('it does not run filter on bidsReceived if no multibid configuration found', function () { + let bids = [{...bidArray[0]}, {...bidArray[1]}]; + targetBidPoolHook(callbackFn, bids, utils.getHighestCpm); + + expect(result).to.not.equal(null); + expect(result.bidsReceived).to.not.equal(null); + expect(result.bidsReceived.length).to.equal(2); + expect(result.bidsReceived).to.deep.equal(bids); + expect(result.adUnitBidLimit).to.not.equal(null); + expect(result.adUnitBidLimit).to.equal(0); + expect(result.hasModified).to.not.equal(null); + expect(result.hasModified).to.equal(false); + }); + + it('it does filter on bidsReceived if multibid configuration found with no prefix', function () { + let bids = [{...bidArray[0]}, {...bidArray[1]}]; + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2}]}); + + targetBidPoolHook(callbackFn, bids, utils.getHighestCpm); + bids.pop(); + + expect(result).to.not.equal(null); + expect(result.bidsReceived).to.not.equal(null); + expect(result.bidsReceived.length).to.equal(1); + expect(result.bidsReceived).to.deep.equal(bids); + expect(result.adUnitBidLimit).to.not.equal(null); + expect(result.adUnitBidLimit).to.equal(0); + expect(result.hasModified).to.not.equal(null); + expect(result.hasModified).to.equal(true); + }); + + it('it sorts and creates dynamic alias on bidsReceived if multibid configuration found with prefix', function () { + let modifiedBids = [{...bidArray[1]}, {...bidArray[0]}].map(bid => { + addBidResponseHook(bidResponseCallback, 'test-div', {...bid}); + + return bidResult; + }); + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + + targetBidPoolHook(callbackFn, modifiedBids, utils.getHighestCpm); + + expect(result).to.not.equal(null); + expect(result.bidsReceived).to.not.equal(null); + expect(result.bidsReceived.length).to.equal(2); + expect(result.bidsReceived).to.deep.equal([modifiedBids[1], modifiedBids[0]]); + expect(result.bidsReceived[0].bidderCode).to.equal('bidderA'); + expect(result.bidsReceived[0].bidder).to.equal('bidderA'); + expect(result.bidsReceived[1].bidderCode).to.equal('bidA2'); + expect(result.bidsReceived[1].bidder).to.equal('bidderA'); + expect(result.adUnitBidLimit).to.not.equal(null); + expect(result.adUnitBidLimit).to.equal(0); + expect(result.hasModified).to.not.equal(null); + expect(result.hasModified).to.equal(true); + }); + + it('it sorts by cpm treating dynamic alias as unique bid when no bid limit defined', function () { + let modifiedBids = [{...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}, {...bidArrayAlt[1]}].map(bid => { + addBidResponseHook(bidResponseCallback, 'test-div', {...bid}); + + return bidResult; + }); + + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}]}); + + targetBidPoolHook(callbackFn, modifiedBids, utils.getHighestCpm); + + expect(result).to.not.equal(null); + expect(result.bidsReceived).to.not.equal(null); + expect(result.bidsReceived.length).to.equal(4); + expect(result.bidsReceived).to.deep.equal([modifiedBids[3], modifiedBids[0], modifiedBids[2], modifiedBids[1]]); + expect(result.bidsReceived[0].bidderCode).to.equal('bidderA'); + expect(result.bidsReceived[0].bidder).to.equal('bidderA'); + expect(result.bidsReceived[0].cpm).to.equal(52); + expect(result.bidsReceived[1].bidderCode).to.equal('bidA2'); + expect(result.bidsReceived[1].bidder).to.equal('bidderA'); + expect(result.bidsReceived[1].cpm).to.equal(29); + expect(result.bidsReceived[2].bidderCode).to.equal('bidderC'); + expect(result.bidsReceived[2].bidder).to.equal('bidderC'); + expect(result.bidsReceived[2].cpm).to.equal(12); + expect(result.bidsReceived[3].bidderCode).to.equal('bidderB'); + expect(result.bidsReceived[3].bidder).to.equal('bidderB'); + expect(result.bidsReceived[3].cpm).to.equal(3); + expect(result.adUnitBidLimit).to.not.equal(null); + expect(result.adUnitBidLimit).to.equal(0); + expect(result.hasModified).to.not.equal(null); + expect(result.hasModified).to.equal(true); + }); + + it('it should filter out dynamic bid when bid limit is less than unique bid pool', function () { + let modifiedBids = [{...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}, {...bidArrayAlt[1]}].map(bid => { + addBidResponseHook(bidResponseCallback, 'test-div', {...bid}); + + return bidResult; + }); + + config.setConfig({ multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}] }); + + targetBidPoolHook(callbackFn, modifiedBids, utils.getHighestCpm, 3); + + expect(result).to.not.equal(null); + expect(result.bidsReceived).to.not.equal(null); + expect(result.bidsReceived.length).to.equal(3); + expect(result.bidsReceived).to.deep.equal([modifiedBids[3], modifiedBids[2], modifiedBids[1]]); + expect(result.bidsReceived[0].bidderCode).to.equal('bidderA'); + expect(result.bidsReceived[1].bidderCode).to.equal('bidderC'); + expect(result.bidsReceived[2].bidderCode).to.equal('bidderB'); + expect(result.adUnitBidLimit).to.not.equal(null); + expect(result.adUnitBidLimit).to.equal(3); + expect(result.hasModified).to.not.equal(null); + expect(result.hasModified).to.equal(true); + }); + + it('it should collect all bids from auction and bid cache then sort and filter', function () { + config.setConfig({ multibid: [{bidder: 'bidderA', maxBids: 2, targetBiddercodePrefix: 'bidA'}] }); + + let modifiedBids = [{...bidArrayAlt[0]}, {...bidArrayAlt[2]}, {...bidArrayAlt[3]}, {...bidArrayAlt[1]}].map(bid => { + addBidResponseHook(bidResponseCallback, 'test-div', {...bid}); + + return bidResult; + }); + + let bidPool = [].concat.apply(modifiedBids, [{...bidCacheArray[0]}, {...bidCacheArray[1]}]); + + expect(bidPool.length).to.equal(6); + + targetBidPoolHook(callbackFn, bidPool, utils.getHighestCpm); + + expect(result).to.not.equal(null); + expect(result.bidsReceived).to.not.equal(null); + expect(result.bidsReceived.length).to.equal(4); + expect(result.bidsReceived).to.deep.equal([bidPool[4], bidPool[3], bidPool[2], bidPool[1]]); + expect(result.bidsReceived[0].bidderCode).to.equal('bidderA'); + expect(result.bidsReceived[1].bidderCode).to.equal('bidA2'); + expect(result.bidsReceived[2].bidderCode).to.equal('bidderC'); + expect(result.bidsReceived[3].bidderCode).to.equal('bidderB'); + expect(result.adUnitBidLimit).to.not.equal(null); + expect(result.adUnitBidLimit).to.equal(0); + expect(result.hasModified).to.not.equal(null); + expect(result.hasModified).to.equal(true); + }); + }); + + describe('validate multibid', function () { + it('should fail validation for missing bidder name in entry', function () { + let conf = [{maxBids: 1}]; + let result = validateMultibid(conf); + + expect(result).to.equal(false); + }); + + it('should pass validation on all multibid entries', function () { + let conf = [{bidder: 'bidderA', maxBids: 1}, {bidder: 'bidderB', maxBids: 2}]; + let result = validateMultibid(conf); + + expect(result).to.equal(true); + }); + + it('should fail validation for maxbids less than 1 in entry', function () { + let conf = [{bidder: 'bidderA', maxBids: 0}, {bidder: 'bidderB', maxBids: 2}]; + let result = validateMultibid(conf); + + expect(result).to.equal(false); + }); + + it('should fail validation for maxbids greater than 9 in entry', function () { + let conf = [{bidder: 'bidderA', maxBids: 10}, {bidder: 'bidderB', maxBids: 2}]; + let result = validateMultibid(conf); + + expect(result).to.equal(false); + }); + + it('should add multbid entries to global config', function () { + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 1}]}); + let conf = config.getConfig('multibid'); + + expect(conf).to.deep.equal([{bidder: 'bidderA', maxBids: 1}]); + }); + + it('should modify multbid entries and add to global config', function () { + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 0}, {bidder: 'bidderB', maxBids: 15}]}); + let conf = config.getConfig('multibid'); + + expect(conf).to.deep.equal([{bidder: 'bidderA', maxBids: 1}, {bidder: 'bidderB', maxBids: 9}]); + }); + + it('should filter multbid entry and add modified to global config', function () { + config.setConfig({multibid: [{bidder: 'bidderA', maxBids: 0}, {maxBids: 15}]}); + let conf = config.getConfig('multibid'); + + expect(conf.length).to.equal(1); + expect(conf).to.deep.equal([{bidder: 'bidderA', maxBids: 1}]); + }); + }); + + describe('sort multibid', function () { + it('should not alter order', function () { + let bids = [{ + 'bidderCode': 'bidderA', + 'cpm': 75, + 'originalCpm': 75, + 'multibidPrefix': 'bidA', + 'originalBidder': 'bidderA', + 'bidder': 'bidderA', + }, { + 'bidderCode': 'bidA2', + 'cpm': 75, + 'originalCpm': 75, + 'multibidPrefix': 'bidA', + 'originalBidder': 'bidderA', + 'bidder': 'bidderA', + }]; + + let expected = [{ + 'bidderCode': 'bidderA', + 'cpm': 75, + 'originalCpm': 75, + 'multibidPrefix': 'bidA', + 'originalBidder': 'bidderA', + 'bidder': 'bidderA', + }, { + 'bidderCode': 'bidA2', + 'cpm': 75, + 'originalCpm': 75, + 'multibidPrefix': 'bidA', + 'originalBidder': 'bidderA', + 'bidder': 'bidderA', + }]; + let result = bids.sort(sortByMultibid); + + expect(result).to.deep.equal(expected); + }); + + it('should sort dynamic alias bidders to end', function () { + let bids = [{ + 'bidderCode': 'bidA2', + 'cpm': 75, + 'originalCpm': 75, + 'multibidPrefix': 'bidA', + 'originalBidder': 'bidderA', + 'bidder': 'bidderA', + }, { + 'bidderCode': 'bidderA', + 'cpm': 22, + 'originalCpm': 22, + 'multibidPrefix': 'bidA', + 'originalBidder': 'bidderA', + 'bidder': 'bidderA', + }, { + 'bidderCode': 'bidderB', + 'cpm': 4, + 'originalCpm': 4, + 'multibidPrefix': 'bidB', + 'originalBidder': 'bidderB', + 'bidder': 'bidderB', + }, { + 'bidderCode': 'bidB', + 'cpm': 2, + 'originalCpm': 2, + 'multibidPrefix': 'bidB', + 'originalBidder': 'bidderB', + 'bidder': 'bidderB', + }]; + let expected = [{ + 'bidderCode': 'bidderA', + 'cpm': 22, + 'originalCpm': 22, + 'multibidPrefix': 'bidA', + 'originalBidder': 'bidderA', + 'bidder': 'bidderA', + }, { + 'bidderCode': 'bidderB', + 'cpm': 4, + 'originalCpm': 4, + 'multibidPrefix': 'bidB', + 'originalBidder': 'bidderB', + 'bidder': 'bidderB', + }, { + 'bidderCode': 'bidA2', + 'cpm': 75, + 'originalCpm': 75, + 'multibidPrefix': 'bidA', + 'originalBidder': 'bidderA', + 'bidder': 'bidderA', + }, { + 'bidderCode': 'bidB', + 'cpm': 2, + 'originalCpm': 2, + 'multibidPrefix': 'bidB', + 'originalBidder': 'bidderB', + 'bidder': 'bidderB', + }]; + let result = bids.sort(sortByMultibid); + + expect(result).to.deep.equal(expected); + }); + }); +}); diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 809e3933eb9..937d67677d9 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -1575,6 +1575,30 @@ describe('S2S Adapter', function () { expect(parsedRequestBody.source.ext.schain).to.deep.equal(schainObject); }); + it('passes multibid array in request', function () { + const bidRequests = utils.deepClone(BID_REQUESTS); + const multibid = [{ + bidder: 'bidderA', + maxBids: 2 + }, { + bidder: 'bidderB', + maxBids: 2 + }]; + const expected = [{ + bidder: 'bidderA', + maxbids: 2 + }, { + bidder: 'bidderB', + maxbids: 2 + }]; + + config.setConfig({multibid: multibid}); + + adapter.callBids(REQUEST, bidRequests, addBidResponse, done, ajax); + const parsedRequestBody = JSON.parse(server.requests[0].requestBody); + expect(parsedRequestBody.ext.prebid.multibid).to.deep.equal(expected); + }); + it('passes first party data in request', () => { const s2sBidRequest = utils.deepClone(REQUEST); const bidRequests = utils.deepClone(BID_REQUESTS); diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index bdfd2ad64de..0239eff5883 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -1899,6 +1899,32 @@ describe('rubicon analytics adapter', function () { expect(message.auctions[0].adUnits[1].pattern).to.equal('1234/mycoolsite/*&gpt_skyscraper&deviceType=mobile'); }); + it('should pass bidderDetail for multibid auctions', function () { + let bidResponse = utils.deepClone(MOCK.BID_RESPONSE[1]); + bidResponse.targetingBidder = 'rubi2'; + bidResponse.originalRequestId = bidResponse.requestId; + bidResponse.requestId = '1a2b3c4d5e6f7g8h9'; + + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, bidResponse); + events.emit(BIDDER_DONE, MOCK.BIDDER_DONE); + events.emit(AUCTION_END, MOCK.AUCTION_END); + events.emit(SET_TARGETING, MOCK.SET_TARGETING); + events.emit(BID_WON, MOCK.BID_WON[0]); + + clock.tick(SEND_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + + let message = JSON.parse(server.requests[0].requestBody); + validate(message); + + expect(message.auctions[0].adUnits[1].bids[1].bidder).to.equal('rubicon'); + expect(message.auctions[0].adUnits[1].bids[1].bidderDetail).to.equal('rubi2'); + }); + it('should successfully convert bid price to USD in parseBidResponse', function () { // Set the rates setConfig({ diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 0b9f8e0da33..c106d057245 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -488,6 +488,17 @@ describe('the rubicon adapter', function () { data = parseQuery(request.data); expect(data.rp_hard_floor).to.equal('1.23'); }); + + it('should send rp_maxbids to AE if rubicon multibid config exists', function () { + var multibidRequest = utils.deepClone(bidderRequest); + multibidRequest.bidLimit = 5; + + let [request] = spec.buildRequests(multibidRequest.bids, multibidRequest); + let data = parseQuery(request.data); + + expect(data['rp_maxbids']).to.equal('5'); + }); + it('should not send p_pos to AE if not params.position specified', function () { var noposRequest = utils.deepClone(bidderRequest); delete noposRequest.bids[0].params.position; @@ -554,7 +565,7 @@ describe('the rubicon adapter', function () { sandbox.stub(Math, 'random').callsFake(() => 0.1); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); - const referenceOrdering = ['account_id', 'site_id', 'zone_id', 'size_id', 'alt_size_ids', 'p_pos', 'rf', 'p_geo.latitude', 'p_geo.longitude', 'kw', 'tg_v.ucat', 'tg_v.lastsearch', 'tg_v.likes', 'tg_i.rating', 'tg_i.prodtype', 'tk_flint', 'x_source.tid', 'x_source.pchain', 'p_screen_res', 'rp_secure', 'tk_user_key', 'tg_fl.eid', 'slots', 'rand']; + const referenceOrdering = ['account_id', 'site_id', 'zone_id', 'size_id', 'alt_size_ids', 'p_pos', 'rf', 'p_geo.latitude', 'p_geo.longitude', 'kw', 'tg_v.ucat', 'tg_v.lastsearch', 'tg_v.likes', 'tg_i.rating', 'tg_i.prodtype', 'tk_flint', 'x_source.tid', 'x_source.pchain', 'p_screen_res', 'rp_secure', 'tk_user_key', 'tg_fl.eid', 'rp_maxbids', 'slots', 'rand']; request.data.split('&').forEach((item, i) => { expect(item.split('=')[0]).to.equal(referenceOrdering[i]); @@ -1626,6 +1637,33 @@ describe('the rubicon adapter', function () { expect(request.data.imp[0].ext).to.not.haveOwnProperty('rubicon'); }); + it('should add multibid configuration to PBS Request', function () { + createVideoBidderRequest(); + + const multibid = [{ + bidder: 'bidderA', + maxBids: 2 + }, { + bidder: 'bidderB', + maxBids: 2 + }]; + const expected = [{ + bidder: 'bidderA', + maxbids: 2 + }, { + bidder: 'bidderB', + maxbids: 2 + }]; + + config.setConfig({multibid: multibid}); + + let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); + + // should have the aliases object sent to PBS + expect(request.data.ext.prebid).to.haveOwnProperty('multibid'); + expect(request.data.ext.prebid.multibid).to.deep.equal(expected); + }); + it('should send video exp param correctly when set', function () { createVideoBidderRequest(); config.setConfig({s2sConfig: {defaultTtl: 600}}); @@ -2556,6 +2594,146 @@ describe('the rubicon adapter', function () { expect(bids[0].cpm).to.be.equal(0); }); + it('should create bids with matching requestIds if imp id matches', function () { + let bidRequests = [{ + 'bidder': 'rubicon', + 'params': { + 'accountId': 1001, + 'siteId': 12345, + 'zoneId': 67890, + 'floor': null + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': '404a7b28-f276-41cc-a5cf-c1d3dc5671f9', + 'sizes': [[300, 250]], + 'bidId': '557ba307cef098', + 'bidderRequestId': '46a00704ffeb7', + 'auctionId': '3fdc6494-da94-44a0-a292-b55a90b08b2c', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0, + 'startTime': 1615412098213 + }, { + 'bidder': 'rubicon', + 'params': { + 'accountId': 1001, + 'siteId': 12345, + 'zoneId': 67890, + 'floor': null + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[300, 250]] + } + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-1', + 'transactionId': '404a7b28-f276-41cc-a5cf-c1d3dc5671f9', + 'sizes': [[300, 250]], + 'bidId': '456gt123jkl098', + 'bidderRequestId': '46a00704ffeb7', + 'auctionId': '3fdc6494-da94-44a0-a292-b55a90b08b2c', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0, + 'startTime': 1615412098213 + }]; + + let response = { + 'status': 'ok', + 'account_id': 14062, + 'site_id': 70608, + 'zone_id': 530022, + 'size_id': 15, + 'alt_size_ids': [ + 43 + ], + 'tracking': '', + 'inventory': {}, + 'ads': [ + { + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374c', + 'size_id': '15', + 'ad_id': '6', + 'advertiser': 7, + 'network': 8, + 'creative_id': 'crid-9', + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 0.811, + 'targeting': [ + { + 'key': 'rpfl_14062', + 'values': [ + '15_tier_all_test' + ] + } + ] + }, + { + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374c', + 'size_id': '15', + 'ad_id': '7', + 'advertiser': 7, + 'network': 8, + 'creative_id': 'crid-9', + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 0.911, + 'targeting': [ + { + 'key': 'rpfl_14062', + 'values': [ + '43_tier_all_test' + ] + } + ] + }, + { + 'status': 'ok', + 'impression_id': '153dc240-8229-4604-b8f5-256933b9374d', + 'size_id': '43', + 'ad_id': '7', + 'advertiser': 7, + 'network': 8, + 'creative_id': 'crid-9', + 'type': 'script', + 'script': 'alert(\'foo\')', + 'campaign_id': 10, + 'cpm': 1.911, + 'targeting': [ + { + 'key': 'rpfl_14062', + 'values': [ + '43_tier_all_test' + ] + } + ] + } + ] + }; + + config.setConfig({ multibid: [{bidder: 'rubicon', maxbids: 2, targetbiddercodeprefix: 'rubi'}] }); + + let bids = spec.interpretResponse({body: response}, { + bidRequest: bidRequests + }); + + expect(bids).to.be.lengthOf(3); + expect(bids[0].requestId).to.not.equal(bids[1].requestId); + expect(bids[1].requestId).to.equal(bids[2].requestId); + }); + it('should handle an error with no ads returned', function () { let response = { 'status': 'ok', From 709e967bc236e031239233fac98e7a86805febcc Mon Sep 17 00:00:00 2001 From: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Date: Wed, 31 Mar 2021 14:34:08 -0700 Subject: [PATCH 18/93] Adding uid2 to submodules.json (#6508) --- modules/.submodules.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/.submodules.json b/modules/.submodules.json index 0f62627822a..066d8ef7f52 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -22,7 +22,8 @@ "pubProvidedIdSystem", "mwOpenLinkIdSystem", "tapadIdSystem", - "novatiqIdSystem" + "novatiqIdSystem", + "uid2IdSystem" ], "adpod": [ "freeWheelAdserverVideo", From b87bfb1220ec9555cda68f4053231f1c5f7c6e90 Mon Sep 17 00:00:00 2001 From: Abimael Martinez Date: Thu, 1 Apr 2021 00:58:24 -0600 Subject: [PATCH 19/93] NextRoll ID System: add new ID module (#6396) * Add Nextroll ID Module * Add nextroll to eids * Make configuration value names consistent with Adapter Module * Use parnerId instead of sellerId * Add nextroll to userId and eids md files * Remove storage configuration * Rename nextroll -> nextrollId * Add nextrollId to common ID specs --- modules/.submodules.json | 1 + modules/nextrollIdSystem.js | 50 +++++++++++++++++++ modules/userId/eids.js | 6 +++ modules/userId/eids.md | 8 ++++ modules/userId/userId.md | 7 ++- test/spec/modules/eids_spec.js | 12 +++++ test/spec/modules/nextrollIdSystem_spec.js | 56 ++++++++++++++++++++++ test/spec/modules/userId_spec.js | 21 ++++---- 8 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 modules/nextrollIdSystem.js create mode 100644 test/spec/modules/nextrollIdSystem_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 066d8ef7f52..d622ebe96d1 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -16,6 +16,7 @@ "zeotapIdPlusIdSystem", "haloIdSystem", "quantcastIdSystem", + "nextrollIdSystem", "idxIdSystem", "fabrickIdSystem", "verizonMediaIdSystem", diff --git a/modules/nextrollIdSystem.js b/modules/nextrollIdSystem.js new file mode 100644 index 00000000000..5a59e216394 --- /dev/null +++ b/modules/nextrollIdSystem.js @@ -0,0 +1,50 @@ +/** + * This module adds Nextroll ID to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/nextrollIdSystem + * @requires module:modules/userId + */ + +import { deepAccess } from '../src/utils.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const NEXTROLL_ID_LS_KEY = 'dca0.com'; +const KEY_PREFIX = 'AdID:' + +export const storage = getStorageManager(); + +/** @type {Submodule} */ +export const nextrollIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'nextrollId', + + /** + * decode the stored id value for passing to bid requests + * @function + * @return {{nextrollId: string} | undefined} + */ + decode(value) { + return value; + }, + + /** + * performs action to obtain id and return a value. + * @function + * @param {SubmoduleConfig} [config] + * @returns {{id: {nextrollId: string} | undefined}} + */ + getId(config) { + const key = KEY_PREFIX + deepAccess(config, 'params.partnerId', 'undefined'); + const dataString = storage.getDataFromLocalStorage(NEXTROLL_ID_LS_KEY) || '{}'; + const data = JSON.parse(dataString); + const idValue = deepAccess(data, `${key}.value`); + + return { id: idValue ? {nextrollId: idValue} : undefined }; + } +}; + +submodule('userId', nextrollIdSubmodule); diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 4d1d9880581..2f552eece8b 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -152,6 +152,12 @@ const USER_IDS_CONFIG = { atype: 1 }, + // nextroll + 'nextrollId': { + source: 'nextroll.com', + atype: 1 + }, + // IDx 'idx': { source: 'idx.lat', diff --git a/modules/userId/eids.md b/modules/userId/eids.md index 53d9196e255..89e696b47a1 100644 --- a/modules/userId/eids.md +++ b/modules/userId/eids.md @@ -119,6 +119,14 @@ userIdAsEids = [ }] }, + { + source: 'nextroll.com', + uids: [{ + id: 'some-random-id-value', + atype: 1 + }] + }, + { source: 'audigent.com', uids: [{ diff --git a/modules/userId/userId.md b/modules/userId/userId.md index 4038e9b00e4..a8ac311a853 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -158,7 +158,12 @@ pbjs.setConfig({ expires: 90, // Expiration in days refreshInSeconds: 8*3600 // User Id cache lifetime in seconds, defaulting to 'expires' }, - }, { + }, { + name: 'nextrollId', + params: { + partnerId: "1009", // Set your real NextRoll partner ID here for production + } + }, { name: 'criteo', storage: { // It is best not to specify this parameter since the module needs to be called as many times as possible type: 'html5', diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 05d4aa57061..fe803296b09 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -195,6 +195,18 @@ describe('eids array generation for known sub-modules', function() { uids: [{id: 'some-random-id-value', atype: 1}] }); }); + + it('NextRollId', function() { + const userId = { + nextrollId: 'some-random-id-value' + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'nextroll.com', + uids: [{id: 'some-random-id-value', atype: 1}] + }); + }); it('Sharedid', function() { const userId = { sharedid: { diff --git a/test/spec/modules/nextrollIdSystem_spec.js b/test/spec/modules/nextrollIdSystem_spec.js new file mode 100644 index 00000000000..d89c7fe3c98 --- /dev/null +++ b/test/spec/modules/nextrollIdSystem_spec.js @@ -0,0 +1,56 @@ +import { nextrollIdSubmodule, storage } from 'modules/nextrollIdSystem.js'; + +const LS_VALUE = `{ + "AdID":{"id":"adid","key":"AdID"}, + "AdID:1002": {"id":"adid","key":"AdID:1002","value":"id_value"}}`; + +describe('NextrollId module', function () { + let sandbox = sinon.sandbox.create(); + let hasLocalStorageStub; + let getLocalStorageStub; + + beforeEach(function() { + hasLocalStorageStub = sandbox.stub(storage, 'hasLocalStorage'); + getLocalStorageStub = sandbox.stub(storage, 'getDataFromLocalStorage'); + }); + + afterEach(function () { + sandbox.restore(); + }) + + const testCases = [ + { + expect: { + id: {nextrollId: 'id_value'}, + }, + params: {partnerId: '1002'}, + localStorage: LS_VALUE + }, + { + expect: {id: undefined}, + params: {partnerId: '1003'}, + localStorage: LS_VALUE + }, + { + expect: {id: undefined}, + params: {partnerId: ''}, + localStorage: LS_VALUE + }, + { + expect: {id: undefined}, + params: {partnerId: '102'}, + localStorage: undefined + }, + { + expect: {id: undefined}, + params: undefined, + localStorage: undefined + } + ] + testCases.forEach( + (testCase, i) => it(`getId() (TC #${i}) should return the nextroll id if it exists`, function () { + getLocalStorageStub.withArgs('dca0.com').returns(testCase.localStorage); + const id = nextrollIdSubmodule.getId({params: testCase.params}); + expect(id).to.be.deep.equal(testCase.expect); + })) +}); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index edac0c652fc..127298072b9 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -32,6 +32,7 @@ import {identityLinkSubmodule} from 'modules/identityLinkIdSystem.js'; import {liveIntentIdSubmodule} from 'modules/liveIntentIdSystem.js'; import {merkleIdSubmodule} from 'modules/merkleIdSystem.js'; import {netIdSubmodule} from 'modules/netIdSystem.js'; +import {nextrollIdSubmodule} from 'modules/nextrollIdSystem.js'; import {intentIqIdSubmodule} from 'modules/intentIqIdSystem.js'; import {zeotapIdPlusSubmodule} from 'modules/zeotapIdPlusIdSystem.js'; import {sharedIdSubmodule} from 'modules/sharedIdSystem.js'; @@ -475,7 +476,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -486,7 +487,7 @@ describe('User ID', function () { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, merkleIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -503,15 +504,15 @@ describe('User ID', function () { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 1 submodules'); }); - it('config with 15 configurations should result in 16 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); + it('config with 16 configurations should result in 17 submodules add', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, liveIntentIdSubmodule, britepoolIdSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -538,6 +539,8 @@ describe('User ID', function () { }, { name: 'netId', storage: {name: 'netId', type: 'cookie'} + }, { + name: 'nextrollId' }, { name: 'sharedId', storage: {name: 'sharedid', type: 'cookie'} @@ -561,11 +564,11 @@ describe('User ID', function () { }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 16 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.contain('User ID - usersync config updated for 17 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -580,7 +583,7 @@ describe('User ID', function () { }); it('config auctionDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { @@ -595,7 +598,7 @@ describe('User ID', function () { }); it('config auctionDelay defaults to 0 if not a number', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule, identityLinkSubmodule, netIdSubmodule, nextrollIdSubmodule, sharedIdSubmodule, intentIqIdSubmodule, zeotapIdPlusSubmodule, haloIdSubmodule, pubProvidedIdSubmodule, criteoIdSubmodule, mwOpenLinkIdSubModule, tapadIdSubmodule, uid2IdSubmodule]); init(config); config.setConfig({ userSync: { From 1fae3c49c71e9a24571fda6bb8f027a077e4695a Mon Sep 17 00:00:00 2001 From: artemiokost Date: Thu, 1 Apr 2021 16:30:48 +0700 Subject: [PATCH 20/93] Qwarry Bid Adapter: add GDPR and consent string handling (#6489) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter * add referer JS detection * use bidderRequest.refererInfo * fix tests * GDPR consent string support * NPE fix Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev Co-authored-by: pro-nsk <32703851+pro-nsk@users.noreply.github.com> --- modules/qwarryBidAdapter.js | 29 ++++++++++++++++------ test/spec/modules/qwarryBidAdapter_spec.js | 11 +++++++- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index 5ff48ec53f6..d19ad0b4fe4 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -24,16 +24,31 @@ export const spec = { }) }) + let payload = { + requestId: bidderRequest.bidderRequestId, + bids, + referer: bidderRequest.refererInfo.referer + } + + if (bidderRequest && bidderRequest.gdprConsent) { + payload.gdprConsent = { + consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : false, + consentString: bidderRequest.gdprConsent.consentString + } + } + + const options = { + contentType: 'application/json', + customHeaders: { + 'Rtb-Direct': true + } + } + return { method: 'POST', url: ENDPOINT, - data: { requestId: bidderRequest.bidderRequestId, bids, referer: bidderRequest.refererInfo.referer }, - options: { - contentType: 'application/json', - customHeaders: { - 'Rtb-Direct': true - } - } + data: payload, + options }; }, diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index bc776f7ebe7..f15d7b488cc 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -70,7 +70,16 @@ describe('qwarryBidAdapter', function () { describe('buildRequests', function () { let bidRequests = [REQUEST] - const bidderRequest = spec.buildRequests(bidRequests, { bidderRequestId: '123', refererInfo: { referer: 'http://test.com/path.html' } }) + const bidderRequest = spec.buildRequests(bidRequests, { + bidderRequestId: '123', + gdprConsent: { + gdprApplies: true, + consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' + }, + refererInfo: { + referer: 'http://test.com/path.html' + } + }) it('sends bid request to ENDPOINT via POST', function () { expect(bidderRequest.method).to.equal('POST') From f2ff4ddb72bed3ce5ce3f59462d20a449467ca2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rok=20Su=C5=A1nik?= Date: Thu, 1 Apr 2021 13:43:07 +0200 Subject: [PATCH 21/93] Zemanta Bid Adapter: add support for new params & consent strings to usersync URL (#6468) * add gvl id to spec * add support for bcat and badv params * add consent strings to usersync url * add bcat and badv params to doc --- modules/zemantaBidAdapter.js | 24 ++++++++++--- modules/zemantaBidAdapter.md | 8 +++-- test/spec/modules/zemantaBidAdapter_spec.js | 39 +++++++++++++++++++-- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/modules/zemantaBidAdapter.js b/modules/zemantaBidAdapter.js index d0ac64e48e4..582f0853d05 100644 --- a/modules/zemantaBidAdapter.js +++ b/modules/zemantaBidAdapter.js @@ -10,6 +10,7 @@ import { ajax } from '../src/ajax.js'; import { config } from '../src/config.js'; const BIDDER_CODE = 'zemanta'; +const GVLID = 164; const CURRENCY = 'USD'; const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; const NATIVE_PARAMS = { @@ -23,7 +24,10 @@ const NATIVE_PARAMS = { export const spec = { code: BIDDER_CODE, - aliases: ['outbrain'], + gvlid: GVLID, + aliases: [ + { code: 'outbrain', gvlid: GVLID } + ], supportedMediaTypes: [ NATIVE, BANNER ], isBidRequestValid: (bid) => { return ( @@ -37,6 +41,8 @@ export const spec = { const ua = navigator.userAgent; const test = setOnAny(validBidRequests, 'params.test'); const publisher = setOnAny(validBidRequests, 'params.publisher'); + const bcat = setOnAny(validBidRequests, 'params.bcat'); + const badv = setOnAny(validBidRequests, 'params.badv'); const cur = CURRENCY; const endpointUrl = config.getConfig('zemanta.bidderUrl') || config.getConfig('outbrain.bidderUrl'); const timeout = bidderRequest.timeout; @@ -73,7 +79,9 @@ export const spec = { source: { fd: 1 }, cur: [cur], tmax: timeout, - imp: imps + imp: imps, + bcat: bcat, + badv: badv, }; if (test) { @@ -135,10 +143,18 @@ export const spec = { } }).filter(Boolean); }, - getUserSyncs: (syncOptions) => { + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { const syncs = []; - const syncUrl = config.getConfig('zemanta.usersyncUrl') || config.getConfig('outbrain.usersyncUrl'); + let syncUrl = config.getConfig('zemanta.usersyncUrl') || config.getConfig('outbrain.usersyncUrl'); if (syncOptions.pixelEnabled && syncUrl) { + if (gdprConsent) { + syncUrl += '&gdpr=' + (gdprConsent.gdprApplies & 1); + syncUrl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); + } + if (uspConsent) { + syncUrl += '&us_privacy=' + encodeURIComponent(uspConsent); + } + syncs.push({ type: 'image', url: syncUrl diff --git a/modules/zemantaBidAdapter.md b/modules/zemantaBidAdapter.md index d991b67d429..fa933ecd922 100644 --- a/modules/zemantaBidAdapter.md +++ b/modules/zemantaBidAdapter.md @@ -65,7 +65,9 @@ pbjs.setConfig({ name: 'Publishers Name', domain: 'publisher.com' }, - tagid: 'tag-id' + tagid: 'tag-id', + bcat: ['IAB1-1'], + badv: ['example.com'] } }] ]; @@ -94,7 +96,9 @@ pbjs.setConfig({ name: 'Publishers Name', domain: 'publisher.com' }, - tagid: 'tag-id' + tagid: 'tag-id', + bcat: ['IAB1-1'], + badv: ['example.com'] }, }] ]; diff --git a/test/spec/modules/zemantaBidAdapter_spec.js b/test/spec/modules/zemantaBidAdapter_spec.js index 0284abd41fd..8ee88b1eb23 100644 --- a/test/spec/modules/zemantaBidAdapter_spec.js +++ b/test/spec/modules/zemantaBidAdapter_spec.js @@ -251,16 +251,24 @@ describe('Zemanta Adapter', function () { expect(res.data).to.deep.equal(JSON.stringify(expectedData)) }) - it('should pass optional tagid in request', function () { + it('should pass optional parameters in request', function () { const bidRequest = { ...commonBidRequest, ...nativeBidRequestParams, } bidRequest.params.tagid = 'test-tag' + bidRequest.params.publisher.name = 'test-publisher' + bidRequest.params.publisher.domain = 'test-publisher.com' + bidRequest.params.bcat = ['bad-category'] + bidRequest.params.badv = ['bad-advertiser'] const res = spec.buildRequests([bidRequest], commonBidderRequest) const resData = JSON.parse(res.data) expect(resData.imp[0].tagid).to.equal('test-tag') + expect(resData.site.publisher.name).to.equal('test-publisher') + expect(resData.site.publisher.domain).to.equal('test-publisher.com') + expect(resData.bcat).to.deep.equal(['bad-category']) + expect(resData.badv).to.deep.equal(['bad-advertiser']) }); it('should pass bidder timeout', function () { @@ -462,10 +470,11 @@ describe('Zemanta Adapter', function () { }) describe('getUserSyncs', function () { - before(() => { + const usersyncUrl = 'https://usersync-url.com'; + beforeEach(() => { config.setConfig({ zemanta: { - usersyncUrl: 'https://usersync-url.com', + usersyncUrl: usersyncUrl, } } ) @@ -499,6 +508,30 @@ describe('Zemanta Adapter', function () { const ret = spec.getUserSyncs({pixelEnabled: true}) expect(ret).to.be.an('array').that.is.empty }) + + it('should pass GDPR consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: false, consentString: 'foo'}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}&gdpr=0&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: undefined}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=` + }]); + }); + + it('should pass US consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined, '1NYN')).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}&us_privacy=1NYN` + }]); + }); + + it('should pass GDPR and US consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, '1NYN')).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=foo&us_privacy=1NYN` + }]); + }); }) describe('onBidWon', function () { From 6697dc9cb64a3494e435a4e92b7051243e16c365 Mon Sep 17 00:00:00 2001 From: Kanchika - Automatad Date: Thu, 1 Apr 2021 20:01:26 +0530 Subject: [PATCH 22/93] Automatad Bid Adapter: Add meta.advertiserDomains to bid response (#6509) * added bid meta with advertiserDomains --- modules/automatadBidAdapter.js | 4 +++- test/spec/modules/automatadBidAdapter_spec.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/automatadBidAdapter.js b/modules/automatadBidAdapter.js index 6b66044f5e5..415c52ba6d3 100644 --- a/modules/automatadBidAdapter.js +++ b/modules/automatadBidAdapter.js @@ -78,7 +78,9 @@ export const spec = { requestId: bid.impid, cpm: bid.price, ad: bid.adm, - adDomain: bid.adomain[0], + meta: { + advertiserDomains: bid.adomain + }, currency: DEFAULT_CURRENCY, ttl: DEFAULT_BID_TTL, creativeId: bid.crid, diff --git a/test/spec/modules/automatadBidAdapter_spec.js b/test/spec/modules/automatadBidAdapter_spec.js index fca1a464ff2..9d828bad4c3 100644 --- a/test/spec/modules/automatadBidAdapter_spec.js +++ b/test/spec/modules/automatadBidAdapter_spec.js @@ -111,6 +111,7 @@ describe('automatadBidAdapter', function () { it('should get the correct bid response', function () { let result = spec.interpretResponse(expectedResponse[0]) expect(result).to.be.an('array').that.is.not.empty + expect(result[0].meta.advertiserDomains[0]).to.equal('someAdDomain'); }) it('should interpret multiple bids in seatbid', function () { From 4230a2c587db52854d27dd00de9aef6f85972b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20L?= Date: Thu, 1 Apr 2021 18:26:04 +0200 Subject: [PATCH 23/93] Adhese Bid Adapter: add support for caching video content (#6501) * adpod category support test * Revert "adpod category support test" This reverts commit 70a3cf2ad5db94757addd9e08c3a083caca282d0. * adpod category support test * Revert "adpod category support test" This reverts commit 70a3cf2ad5db94757addd9e08c3a083caca282d0. * Adhese Bid Adapter: cache video content Co-authored-by: Tim Sturtewagen Co-authored-by: Mateusz --- modules/adheseBidAdapter.js | 14 +-- test/spec/modules/adheseBidAdapter_spec.js | 119 ++++++++++++++++++++- 2 files changed, 122 insertions(+), 11 deletions(-) diff --git a/modules/adheseBidAdapter.js b/modules/adheseBidAdapter.js index 4fae85e82ad..b9dbae529ba 100644 --- a/modules/adheseBidAdapter.js +++ b/modules/adheseBidAdapter.js @@ -34,6 +34,7 @@ export const spec = { const payload = { slots: slots, parameters: commonParams, + vastContentAsUrl: true, user: { ext: { eids: getEids(validBidRequests), @@ -95,7 +96,7 @@ function adResponse(bid, ad) { const bidResponse = getbaseAdResponse({ requestId: bid.bidId, - mediaType: getMediaType(markup), + mediaType: ad.extension.mediaType, cpm: Number(price.amount), currency: price.currency, width: Number(ad.width), @@ -110,7 +111,11 @@ function adResponse(bid, ad) { }); if (bidResponse.mediaType === VIDEO) { - bidResponse.vastXml = markup; + if (ad.cachedBodyUrl) { + bidResponse.vastUrl = ad.cachedBodyUrl + } else { + bidResponse.vastXml = markup; + } } else { const counter = ad.impressionCounter ? "" : ''; bidResponse.ad = markup + counter; @@ -172,11 +177,6 @@ function isAdheseAd(ad) { return !ad.origin || ad.origin === 'JERLICIA'; } -function getMediaType(markup) { - const isVideo = markup.trim().toLowerCase().match(/<\?xml|', tracker: 'https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a', impressionCounter: 'https://hosts-demo.adhese.com/rtb_gateway/handlers/client/track/?id=a2f39296-6dd0-4b3c-be85-7baa22e7ff4a', - extension: {'prebid': {'cpm': {'amount': '1.000000', 'currency': 'USD'}}} + extension: {'prebid': {'cpm': {'amount': '1.000000', 'currency': 'USD'}}, mediaType: 'banner'} } ] }; @@ -227,7 +233,7 @@ describe('AdheseAdapter', function () { width: '640', height: '350', body: '', - extension: {'prebid': {'cpm': {'amount': '2.1', 'currency': 'USD'}}} + extension: {'prebid': {'cpm': {'amount': '2.1', 'currency': 'USD'}}, mediaType: 'video'} } ] }; @@ -253,6 +259,43 @@ describe('AdheseAdapter', function () { expect(spec.interpretResponse(sspVideoResponse, bidRequest)).to.deep.equal(expectedResponse); }); + it('should get correct ssp cache video response', () => { + let sspCachedVideoResponse = { + body: [ + { + origin: 'RUBICON', + ext: 'js', + slotName: '_main_page_-leaderboard', + adType: 'leaderboard', + width: '640', + height: '350', + cachedBodyUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', + extension: {'prebid': {'cpm': {'amount': '2.1', 'currency': 'USD'}}, mediaType: 'video'} + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + vastUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', + cpm: 2.1, + currency: 'USD', + creativeId: 'RUBICON', + dealId: '', + width: 640, + height: 350, + mediaType: 'video', + netRevenue: NET_REVENUE, + ttl: TTL, + adhese: { + origin: 'RUBICON', + originInstance: '', + originData: {} + } + }]; + expect(spec.interpretResponse(sspCachedVideoResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + it('should get correct Adhese banner response', () => { const adheseBannerResponse = { body: [ @@ -290,7 +333,8 @@ describe('AdheseAdapter', function () { amount: '5.96', currency: 'USD' } - } + }, + mediaType: 'banner' } } ] @@ -352,7 +396,10 @@ describe('AdheseAdapter', function () { impressionCounter: 'https://hosts-demo.adhese.com/track/742898', origin: 'JERLICIA', originData: {}, - auctionable: true + auctionable: true, + extension: { + mediaType: 'video' + } } ] }; @@ -390,6 +437,70 @@ describe('AdheseAdapter', function () { expect(spec.interpretResponse(adheseVideoResponse, bidRequest)).to.deep.equal(expectedResponse); }); + it('should get correct Adhese cached video response', () => { + const adheseVideoResponse = { + body: [ + { + adType: 'preroll', + adFormat: '', + orderId: '22248', + adspaceId: '164196', + body: '', + height: '360', + width: '640', + extension: { + mediaType: 'video' + }, + cachedBodyUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', + libId: '89860', + id: '742470', + advertiserId: '2263', + ext: 'advar', + orderName: 'Smartphoto EOY-20181112', + creativeName: 'PREROLL', + slotName: '_main_page_-leaderboard', + slotID: '41711', + impressionCounter: 'https://hosts-demo.adhese.com/track/742898', + origin: 'JERLICIA', + originData: {}, + auctionable: true + } + ] + }; + + let expectedResponse = [{ + requestId: BID_ID, + vastUrl: 'https://ads-demo.adhese.com/content/38983ccc-4083-4c24-932c-96f798d969b3', + adhese: { + origin: '', + originInstance: '', + originData: { + adFormat: '', + adId: '742470', + adType: 'preroll', + adspaceId: '164196', + libId: '89860', + orderProperty: undefined, + priority: undefined, + viewableImpressionCounter: undefined, + slotId: '41711', + slotName: '_main_page_-leaderboard', + advertiserId: '2263', + } + }, + cpm: 0, + currency: 'USD', + creativeId: '742470', + dealId: '22248', + width: 640, + height: 360, + mediaType: 'video', + netRevenue: NET_REVENUE, + ttl: TTL, + }]; + expect(spec.interpretResponse(adheseVideoResponse, bidRequest)).to.deep.equal(expectedResponse); + }); + it('should return no bids for empty adserver response', () => { let adserverResponse = { body: [] }; expect(spec.interpretResponse(adserverResponse, bidRequest)).to.be.empty; From f94d9b475c8a7b2d4f6cfa3d87391655b87675fa Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Thu, 1 Apr 2021 12:33:37 -0400 Subject: [PATCH 24/93] update apacdex unit test to disable debug mode (#6511) --- test/spec/modules/apacdexBidAdapter_spec.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/spec/modules/apacdexBidAdapter_spec.js b/test/spec/modules/apacdexBidAdapter_spec.js index c3d0d025c0c..3a71833bc3e 100644 --- a/test/spec/modules/apacdexBidAdapter_spec.js +++ b/test/spec/modules/apacdexBidAdapter_spec.js @@ -336,10 +336,17 @@ describe('ApacdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.data.us_privacy).to.equal('someCCPAString'); }); - it('should return a properly formatted request with pbjs_debug is true', function () { - config.setConfig({debug: true}); - const bidRequests = spec.buildRequests(bidRequest, bidderRequests); - expect(bidRequests.data.test).to.equal(1) + describe('debug test', function() { + beforeEach(function() { + config.setConfig({debug: true}); + }); + afterEach(function() { + config.setConfig({debug: false}); + }); + it('should return a properly formatted request with pbjs_debug is true', function () { + const bidRequests = spec.buildRequests(bidRequest, bidderRequests); + expect(bidRequests.data.test).to.equal(1); + }); }); }); From d6d29a712209a850fa46e779f70e8dcda770a92f Mon Sep 17 00:00:00 2001 From: bretg Date: Thu, 1 Apr 2021 12:48:13 -0400 Subject: [PATCH 25/93] Telaria: not setting adid (#6507) --- modules/telariaBidAdapter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/telariaBidAdapter.js b/modules/telariaBidAdapter.js index 71651b5af94..b2904045144 100644 --- a/modules/telariaBidAdapter.js +++ b/modules/telariaBidAdapter.js @@ -276,7 +276,6 @@ function createBid(status, reqBid, response, width, height, bidderCode) { width: width, height: height, bidderCode: bidderCode, - adId: response.id, currency: 'USD', netRevenue: true, ttl: 300, From cb19d28db7809dbb6b7ee7bc77614b23c1386e2d Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Thu, 1 Apr 2021 13:24:24 -0400 Subject: [PATCH 26/93] Prebid 4.33.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aca216767cd..4aef2d23911 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.33.0-pre", + "version": "4.33.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 2b865a306fd621f2b582577bec4b41c7357393ff Mon Sep 17 00:00:00 2001 From: Jason Snellbaker Date: Thu, 1 Apr 2021 13:52:02 -0400 Subject: [PATCH 27/93] increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4aef2d23911..60c295f53ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.33.0", + "version": "4.34.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 0ce4ae3f22fa19cff72ff9d59b4f4a6cc8d5b072 Mon Sep 17 00:00:00 2001 From: bretg Date: Thu, 1 Apr 2021 18:47:53 -0400 Subject: [PATCH 28/93] rubicon: removing maxduration as a required bidder parameter (#6513) --- modules/rubiconBidAdapter.js | 1 - test/spec/modules/rubiconBidAdapter_spec.js | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 7d1659fb0f7..a8d10d4d91b 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -1108,7 +1108,6 @@ export function hasValidVideoParams(bid) { var requiredParams = { mimes: arrayType, protocols: arrayType, - maxduration: numberType, linearity: numberType, api: arrayType } diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index c106d057245..f53cde3c8ab 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -1821,16 +1821,6 @@ describe('the rubicon adapter', function () { delete bidderRequest.bids[0].mediaTypes.video.protocols; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - // change maxduration to an string, no good - createVideoBidderRequest(); - bidderRequest.bids[0].mediaTypes.video.maxduration = 'string'; - expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - - // delete maxduration, no good - createVideoBidderRequest(); - delete bidderRequest.bids[0].mediaTypes.video.maxduration; - expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); - // change linearity to an string, no good createVideoBidderRequest(); bidderRequest.bids[0].mediaTypes.video.linearity = 'string'; From 61ac2eb507091fa30c572243d4e671a979d4a583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rok=20Su=C5=A1nik?= Date: Fri, 2 Apr 2021 12:56:11 +0200 Subject: [PATCH 29/93] Zemanta adapter: add advertiserDomains (#6517) --- modules/zemantaBidAdapter.js | 4 ++++ test/spec/modules/zemantaBidAdapter_spec.js | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/modules/zemantaBidAdapter.js b/modules/zemantaBidAdapter.js index 582f0853d05..b6dafcaed77 100644 --- a/modules/zemantaBidAdapter.js +++ b/modules/zemantaBidAdapter.js @@ -139,6 +139,10 @@ export const spec = { bidObject.width = bidResponse.w; bidObject.height = bidResponse.h; } + bidObject.meta = {}; + if (bidResponse.adomain && bidResponse.adomain.length > 0) { + bidObject.meta.advertiserDomains = bidResponse.adomain; + } return bidObject; } }).filter(Boolean); diff --git a/test/spec/modules/zemantaBidAdapter_spec.js b/test/spec/modules/zemantaBidAdapter_spec.js index 8ee88b1eb23..523cdcd2eb3 100644 --- a/test/spec/modules/zemantaBidAdapter_spec.js +++ b/test/spec/modules/zemantaBidAdapter_spec.js @@ -352,7 +352,7 @@ describe('Zemanta Adapter', function () { nurl: 'http://example.com/win/${AUCTION_PRICE}', adm: '{"ver":"1.2","assets":[{"id":3,"required":1,"img":{"url":"http://example.com/img/url","w":120,"h":100}},{"id":0,"required":1,"title":{"text":"Test title"}},{"id":5,"data":{"value":"Test sponsor"}}],"link":{"url":"http://example.com/click/url"},"eventtrackers":[{"event":1,"method":1,"url":"http://example.com/impression"}]}', adomain: [ - 'example.co' + 'example.com' ], cid: '3487171', crid: '28023739', @@ -386,6 +386,11 @@ describe('Zemanta Adapter', function () { currency: 'USD', mediaType: 'native', nurl: 'http://example.com/win/${AUCTION_PRICE}', + meta: { + 'advertiserDomains': [ + 'example.com' + ] + }, native: { clickTrackers: undefined, clickUrl: 'http://example.com/click/url', @@ -459,7 +464,12 @@ describe('Zemanta Adapter', function () { nurl: 'http://example.com/win/${AUCTION_PRICE}', ad: '
ad
', width: 300, - height: 250 + height: 250, + meta: { + 'advertiserDomains': [ + 'example.com' + ] + }, } ] From d6426dcf1468c6b2c44e694c25e8e16635c90d9d Mon Sep 17 00:00:00 2001 From: Lemma Dev <54662130+lemmadev@users.noreply.github.com> Date: Fri, 2 Apr 2021 16:39:05 +0530 Subject: [PATCH 30/93] Lemma Bid Adapter: accepting the floor to use the getFloor function (#6497) * lemmaBidAdapter.js Added lemma bid adapter file * lemmaBidAdapter.md Added lemma bid adapter md file * lemmaBidAdapter_spec.js Added lemma bid adapter test spec file * Update lemmaBidAdapter.js Fixed automated code review alert comparison between inconvertible types * Update lemmaBidAdapter.js Fixed review changes * Update lemmaBidAdapter.md Correct parameter value. * Update lemmaBidAdapter.js Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter_spec.js Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter.md Lemma Bid Adapter - v3.0 compliance * Update lemmaBidAdapter.js Added user sync support into bid adapter. * updated include modules file extension. updated include modules js file extension. * Update lemmaBidAdapter_spec.js Added unit test for user sync feature. * Update lemmaBidAdapter.js Fixed format error. * Update lemmaBidAdapter_spec.js Fixed format error and typo error. * Set mediaType key value into bid object Set mediaType key value into the bid object. * Update lemmaBidAdapter.js remove duplicate function * Update lemmaBidAdapter.js Remove non supported code. * Update lemmaBidAdapter_spec.js Remove GDPR test cases. * Update lemmaBidAdapter.js Made changes for accepting the floor to use the getFloor function * Update lemmaBidAdapter.js correct undefined keyword name. * Update lemmaBidAdapter_spec.js Added test coverage floor value * Update lemmaBidAdapter.js Remove trailing spaces on lines 379 and 381. * Update lemmaBidAdapter_spec.js Added getFloor function test case changes, Please review it. * Update lemmaBidAdapter_spec.js * Update lemmaBidAdapter.js * Update lemmaBidAdapter.js Fixed lint issue. * Update lemmaBidAdapter_spec.js Fixed test cases. * Update lemmaBidAdapter_spec.js Made suggested changes. Please review it. Co-authored-by: Abhijit Mane --- modules/lemmaBidAdapter.js | 28 ++++++++- test/spec/modules/lemmaBidAdapter_spec.js | 75 +++++++++++++++++++++++ 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/modules/lemmaBidAdapter.js b/modules/lemmaBidAdapter.js index 926761e5ab2..c7440743d2c 100644 --- a/modules/lemmaBidAdapter.js +++ b/modules/lemmaBidAdapter.js @@ -84,6 +84,30 @@ function _initConf(refererInfo) { return conf; } +function _setFloor(impObj, bid) { + let bidFloor = -1; + // get lowest floor from floorModule + if (typeof bid.getFloor === 'function') { + [BANNER, VIDEO].forEach(mediaType => { + if (impObj.hasOwnProperty(mediaType)) { + let floorInfo = bid.getFloor({ currency: impObj.bidfloorcur, mediaType: mediaType, size: '*' }); + if (typeof floorInfo === 'object' && floorInfo.currency === impObj.bidfloorcur && !isNaN(parseInt(floorInfo.floor))) { + let mediaTypeFloor = parseFloat(floorInfo.floor); + bidFloor = (bidFloor == -1 ? mediaTypeFloor : Math.min(mediaTypeFloor, bidFloor)) + } + } + }); + } + // get highest from impObj.bidfllor and floor from floor module + // as we are using Math.max, it is ok if we have not got any floor from floorModule, then value of bidFloor will be -1 + if (impObj.bidfloor) { + bidFloor = Math.max(bidFloor, impObj.bidfloor) + } + + // assign value only if bidFloor is > 0 + impObj.bidfloor = ((!isNaN(bidFloor) && bidFloor > 0) ? bidFloor : undefined); +} + function parseRTBResponse(request, response) { var bidResponses = []; try { @@ -352,11 +376,9 @@ function _getImpressionObject(bid) { secure: window.location.protocol === 'https:' ? 1 : 0, bidfloorcur: params.currency ? params.currency : DEFAULT_CURRENCY }; - if (params.bidFloor) { impression.bidfloor = params.bidFloor; } - if (bid.hasOwnProperty('mediaTypes')) { for (mediaTypes in bid.mediaTypes) { switch (mediaTypes) { @@ -392,7 +414,7 @@ function _getImpressionObject(bid) { } impression.banner = bObj; } - + _setFloor(impression, bid); return impression.hasOwnProperty(BANNER) || impression.hasOwnProperty(VIDEO) ? impression : undefined; } diff --git a/test/spec/modules/lemmaBidAdapter_spec.js b/test/spec/modules/lemmaBidAdapter_spec.js index a22f5650e50..a3a70a39731 100644 --- a/test/spec/modules/lemmaBidAdapter_spec.js +++ b/test/spec/modules/lemmaBidAdapter_spec.js @@ -23,6 +23,7 @@ describe('lemmaBidAdapter', function() { pubId: 1001, adunitId: 1, currency: 'AUD', + bidFloor: 1.3, geo: { lat: '12.3', lon: '23.7', @@ -46,6 +47,7 @@ describe('lemmaBidAdapter', function() { params: { pubId: 1001, adunitId: 1, + bidFloor: 1.3, video: { mimes: ['video/mp4', 'video/x-flv'], skippable: true, @@ -161,6 +163,7 @@ describe('lemmaBidAdapter', function() { expect(data.site.publisher.id).to.equal(bidRequests[0].params.pubId.toString()); // publisher Id expect(data.imp[0].tagid).to.equal('1'); // tagid expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); + expect(data.imp[0].bidfloor).to.equal(bidRequests[0].params.bidFloor); }); it('Request params check without mediaTypes object', function() { var bidRequests = [{ @@ -192,6 +195,7 @@ describe('lemmaBidAdapter', function() { expect(data.site.publisher.id).to.equal(bidRequests[0].params.pubId.toString()); // publisher Id expect(data.imp[0].tagid).to.equal(undefined); // tagid expect(data.imp[0].bidfloorcur).to.equal(bidRequests[0].params.currency); + expect(data.imp[0].bidfloor).to.equal(bidRequests[0].params.bidFloor); }); it('Request params multi size format object check', function() { var bidRequests = [{ @@ -309,6 +313,77 @@ describe('lemmaBidAdapter', function() { expect(data.imp[0]['video']['w']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[0]); expect(data.imp[0]['video']['h']).to.equal(videoBidRequests[0].mediaTypes.video.playerSize[1]); }); + describe('setting imp.floor using floorModule', function() { + /* + Use the minimum value among floor from floorModule per mediaType + If params.bidFloor is set then take max(floor, min(floors from floorModule)) + set imp.bidfloor only if it is more than 0 + */ + + let newRequest; + let floorModuleTestData; + let getFloor = function(req) { + return floorModuleTestData[req.mediaType]; + }; + + beforeEach(() => { + floorModuleTestData = { + 'banner': { + 'currency': 'AUD', + 'floor': 1.50 + }, + 'video': { + 'currency': 'AUD', + 'floor': 2.00 + } + }; + newRequest = utils.deepClone(bidRequests); + newRequest[0].getFloor = getFloor; + }); + + it('bidfloor should be undefined if calculation is <= 0', function() { + floorModuleTestData.banner.floor = 0; // lowest of them all + newRequest[0].params.bidFloor = undefined; + let request = spec.buildRequests(newRequest); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.bidfloor).to.equal(undefined); + }); + + it('ignore floormodule o/p if floor is not number', function() { + floorModuleTestData.banner.floor = 'INR'; + newRequest[0].params.bidFloor = undefined; + let request = spec.buildRequests(newRequest); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.bidfloor).to.equal(undefined); // video will be lowest now + }); + + it('ignore floormodule o/p if currency is not matched', function() { + floorModuleTestData.banner.currency = 'INR'; + newRequest[0].params.bidFloor = undefined; + let request = spec.buildRequests(newRequest); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.bidfloor).to.equal(undefined); // video will be lowest now + }); + + it('bidFloor is not passed, use minimum from floorModule', function() { + newRequest[0].params.bidFloor = undefined; + let request = spec.buildRequests(newRequest); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.bidfloor).to.equal(1.5); + }); + + it('bidFloor is passed as 1, use min of floorModule as it is highest', function() { + newRequest[0].params.bidFloor = '1.0';// yes, we want it as a string + let request = spec.buildRequests(newRequest); + let data = JSON.parse(request.data); + data = data.imp[0]; + expect(data.bidfloor).to.equal(1.5); + }); + }); describe('Response checking', function() { it('should check for valid response values', function() { var request = spec.buildRequests(bidRequests); From 22f7de5baed54617ec6f115389434c2a31176b2d Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Fri, 2 Apr 2021 16:57:02 +0200 Subject: [PATCH 31/93] Mediasquare Bid Adapter: fix getUserSyncs issue with empty bids + add metrics to onBidWon Event (#6480) * Mediasquare bidder: fix getUserSyncs issue with empty bids + add metrics to onBidWon Event * Mediasquare bidder: fix getUserSyncs issue with empty bids + add metrics to onBidWon Event * removing status as it does not seem populated when called * add tests --- modules/mediasquareBidAdapter.js | 5 +++-- test/spec/modules/mediasquareBidAdapter_spec.js | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index cb9bf5285a1..84783eb0991 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -128,7 +128,8 @@ export const spec = { getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { let params = ''; let endpoint = document.location.search.match(/msq_test=true/) ? BIDDER_URL_TEST : BIDDER_URL_PROD; - if (serverResponses[0].body.hasOwnProperty('cookies') && typeof serverResponses[0].body.cookies === 'object') { + if (typeof serverResponses === 'object' && serverResponses != null && serverResponses.length > 0 && serverResponses[0].hasOwnProperty('body') && + serverResponses[0].body.hasOwnProperty('cookies') && typeof serverResponses[0].body.cookies === 'object') { return serverResponses[0].body.cookies; } else { if (gdprConsent && typeof gdprConsent.consentString === 'string') { params += typeof gdprConsent.gdprApplies === 'boolean' ? `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}` : `&gdpr_consent=${gdprConsent.consentString}`; } @@ -148,7 +149,7 @@ export const spec = { // fires a pixel to confirm a winning bid let params = []; let endpoint = document.location.search.match(/msq_test=true/) ? BIDDER_URL_TEST : BIDDER_URL_PROD; - let paramsToSearchFor = ['cpm', 'size', 'mediaType', 'currency', 'creativeId', 'adUnitCode', 'timeToRespond'] + let paramsToSearchFor = ['cpm', 'size', 'mediaType', 'currency', 'creativeId', 'adUnitCode', 'timeToRespond', 'requestId', 'auctionId'] if (bid.hasOwnProperty('mediasquare')) { if (bid['mediasquare'].hasOwnProperty('bidder')) { params.push('bidder=' + bid['mediasquare']['bidder']); } if (bid['mediasquare'].hasOwnProperty('code')) { params.push('code=' + bid['mediasquare']['code']); } diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index 3c18cfe0be7..796be3420e8 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -165,6 +165,16 @@ describe('MediaSquare bid adapter tests', function () { expect(syncs[0]).to.have.property('type').and.to.equal('image'); expect(syncs[0]).to.have.property('url').and.to.equal('http://www.cookie.sync.org/'); }); + it('Verifies user sync with no bid response', function() { + var syncs = spec.getUserSyncs({}, null, DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.property('type').and.to.equal('iframe'); + }); + it('Verifies user sync with no bid body response', function() { + var syncs = spec.getUserSyncs({}, [], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.property('type').and.to.equal('iframe'); + var syncs = spec.getUserSyncs({}, [{}], DEFAULT_OPTIONS.gdprConsent, DEFAULT_OPTIONS.uspConsent); + expect(syncs).to.have.property('type').and.to.equal('iframe'); + }); it('Verifies native in bid response', function () { const request = spec.buildRequests(NATIVE_PARAMS, DEFAULT_OPTIONS); BID_RESPONSE.body.responses[0].native = {'title': 'native title'}; From fcc2928bfe468a6bba94fe48a311f16e135efa52 Mon Sep 17 00:00:00 2001 From: Abimael Martinez Date: Fri, 2 Apr 2021 15:30:13 -0600 Subject: [PATCH 32/93] Update nextroll ID variable name to match published ID module (#6519) --- modules/nextrollBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nextrollBidAdapter.js b/modules/nextrollBidAdapter.js index 02ebcd3f87a..cb317190bea 100644 --- a/modules/nextrollBidAdapter.js +++ b/modules/nextrollBidAdapter.js @@ -177,7 +177,7 @@ function _getNativeAssets(mediaTypeNative) { } function _getUser(requests) { - const id = utils.deepAccess(requests, '0.userId.nextroll'); + const id = utils.deepAccess(requests, '0.userId.nextrollId'); if (id === undefined) { return; } From 391cbb710b790ae4070fe35fa19950221021b758 Mon Sep 17 00:00:00 2001 From: SKOCHERI <37454420+SKOCHERI@users.noreply.github.com> Date: Sat, 3 Apr 2021 04:47:42 -0700 Subject: [PATCH 33/93] Merkle User ID Module: updates to user id submodule (#6503) --- integrationExamples/gpt/userId_example.html | 15 +++--- modules/merkleIdSystem.js | 58 ++++++++++++++++++--- modules/userId/eids.js | 10 +++- modules/userId/userId.md | 28 +++++++++- test/spec/modules/eids_spec.js | 9 +++- test/spec/modules/userId_spec.js | 6 +-- 6 files changed, 106 insertions(+), 20 deletions(-) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index fae5ca2b539..973c295325a 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -197,13 +197,16 @@ }, { name: "merkleId", params: { - ptk: '12345678-aaaa-bbbb-cccc-123456789abc', //Set your real merkle partner key here - pubid: 'EXAMPLE' //Set your real merkle publisher id here - }, + vendor:'sdfg', + sv_cid:'dfg', + sv_pubid:'xcv', + sv_domain:'zxv' + } + , storage: { - type: "html5", - name: "merkleId", - expires: 30 + type: "html5", + name: "merkleId", + expires: 30 }, },{ diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index c55233af6a0..353cc45473d 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -8,8 +8,41 @@ import * as utils from '../src/utils.js' import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js' +import { getStorageManager } from '../src/storageManager.js'; const MODULE_NAME = 'merkleId'; +const SESSION_COOKIE_NAME = '_svsid'; +const ID_URL = 'https://id2.sv.rkdms.com/identity/'; + +export const storage = getStorageManager(); + +function getSession(configParams) { + let session = null; + if (typeof configParams.sv_session !== 'string') { + session = configParams.sv_session; + } else { + session = readCookie() || readFromLocalStorage(); + } + return session; +} + +function readCookie() { + return storage.cookiesAreEnabled() ? storage.getCookie(SESSION_COOKIE_NAME) : null; +} + +function readFromLocalStorage() { + return storage.localStorageIsEnabled() ? storage.getDataFromLocalStorage(SESSION_COOKIE_NAME) : null; +} + +function constructUrl(configParams) { + const session = getSession(configParams); + let url = ID_URL + `?vendor=${configParams.vendor}&sv_cid=${configParams.sv_cid}&sv_domain=${configParams.sv_domain}&sv_pubid=${configParams.sv_pubid}`; + if (session) { + url.append(`&sv_session=${session}`); + } + utils.logInfo('Merkle url :' + url); + return url; +} /** @type {Submodule} */ export const merkleIdSubmodule = { @@ -25,7 +58,8 @@ export const merkleIdSubmodule = { * @returns {{merkleId:string}} */ decode(value) { - const id = (value && value.ppid && typeof value.ppid.id === 'string') ? value.ppid.id : undefined; + const id = (value && value.pam_id && typeof value.pam_id.id === 'string') ? value.pam_id : undefined; + utils.logInfo('Merkle id ' + JSON.stringify(id)); return id ? { 'merkleId': id } : undefined; }, /** @@ -37,13 +71,23 @@ export const merkleIdSubmodule = { */ getId(config, consentData) { const configParams = (config && config.params) || {}; - if (!configParams || typeof configParams.pubid !== 'string') { - utils.logError('User ID - merkleId submodule requires a valid pubid to be defined'); + if (!configParams || typeof configParams.vendor !== 'string') { + utils.logError('User ID - merkleId submodule requires a valid vendor to be defined'); return; } - if (typeof configParams.ptk !== 'string') { - utils.logError('User ID - merkleId submodule requires a valid ptk string to be defined'); + if (typeof configParams.sv_cid !== 'string') { + utils.logError('User ID - merkleId submodule requires a valid sv_cid string to be defined'); + return; + } + + if (typeof configParams.sv_pubid !== 'string') { + utils.logError('User ID - merkleId submodule requires a valid sv_pubid string to be defined'); + return; + } + + if (typeof configParams.sv_domain !== 'string') { + utils.logError('User ID - merkleId submodule requires a valid sv_domain string to be defined'); return; } @@ -51,8 +95,7 @@ export const merkleIdSubmodule = { utils.logError('User ID - merkleId submodule does not currently handle consent strings'); return; } - - const url = `https://mid.rkdms.com/idsv2?ptk=${configParams.ptk}&pubid=${configParams.pubid}`; + const url = constructUrl(configParams); const resp = function (callback) { const callbacks = { @@ -61,6 +104,7 @@ export const merkleIdSubmodule = { if (response) { try { responseObj = JSON.parse(response); + utils.logInfo('Merkle responseObj ' + JSON.stringify(responseObj)); } catch (error) { utils.logError(error); } diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 2f552eece8b..465788864b1 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -111,7 +111,15 @@ const USER_IDS_CONFIG = { // merkleId 'merkleId': { source: 'merkleinc.com', - atype: 3 + atype: 3, + getValue: function(data) { + return data.id; + }, + getUidExt: function(data) { + return (data && data.keyID) ? { + keyID: data.keyID + } : undefined; + } }, // NetId diff --git a/modules/userId/userId.md b/modules/userId/userId.md index a8ac311a853..d5d3d53f6d1 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -84,7 +84,20 @@ pbjs.setConfig({ partnerId: 0000, uid: '12345xyz' } - }, { + },{ + name: "merkleId", + params: { + vendor:'sdfg', + sv_cid:'dfg', + sv_pubid:'xcv', + sv_domain:'zxv' + }, + storage: { + type: "cookie", + name: "merkleId", + expires: 30 + } + },{ name: 'uid2' } }], @@ -170,6 +183,19 @@ pbjs.setConfig({ name: '_criteoId', expires: 1 } + },{ + name: "merkleId", + params: { + vendor:'sdfg', + sv_cid:'dfg', + sv_pubid:'xcv', + sv_domain:'zxv' + }, + storage: { + type: "html5", + name: "merkleId", + expires: 30 + } }], syncDelay: 5000 } diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index fe803296b09..86a6dff2205 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -84,13 +84,18 @@ describe('eids array generation for known sub-modules', function() { it('merkleId', function() { const userId = { - merkleId: 'some-random-id-value' + merkleId: { + id: 'some-random-id-value', keyID: 1 + } }; const newEids = createEidsArray(userId); expect(newEids.length).to.equal(1); expect(newEids[0]).to.deep.equal({ source: 'merkleinc.com', - uids: [{id: 'some-random-id-value', atype: 3}] + uids: [{id: 'some-random-id-value', + atype: 3, + ext: { keyID: 1 + }}] }); }); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 127298072b9..d405bc31f4f 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1585,7 +1585,7 @@ describe('User ID', function () { it('test hook from merkleId cookies', function (done) { // simulate existing browser local storage values - coreStorage.setCookie('merkleId', JSON.stringify({'ppid': {'id': 'testmerkleId'}}), (new Date(Date.now() + 5000).toUTCString())); + coreStorage.setCookie('merkleId', JSON.stringify({'pam_id': {'id': 'testmerkleId', 'keyID': 1}}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([merkleIdSubmodule]); init(config); @@ -1595,10 +1595,10 @@ describe('User ID', function () { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.merkleId'); - expect(bid.userId.merkleId).to.equal('testmerkleId'); + expect(bid.userId.merkleId).to.deep.equal({'id': 'testmerkleId', 'keyID': 1}); expect(bid.userIdAsEids[0]).to.deep.equal({ source: 'merkleinc.com', - uids: [{id: 'testmerkleId', atype: 3}] + uids: [{id: 'testmerkleId', atype: 3, ext: {keyID: 1}}] }); }); }); From 40c85ca596b3236a1a0646b71152fc435fafebed Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Mon, 5 Apr 2021 16:51:31 +0300 Subject: [PATCH 34/93] AdKernel Bid/Analytics Adapters: user privacy related changes (#6488) --- modules/adkernelAdnAnalyticsAdapter.js | 29 ++++++++++++++++--- modules/adkernelAdnBidAdapter.js | 6 ++++ modules/adkernelBidAdapter.js | 8 +++-- .../spec/modules/adkernelAdnAnalytics_spec.js | 21 ++++++++++---- .../modules/adkernelAdnBidAdapter_spec.js | 11 ++++++- test/spec/modules/adkernelBidAdapter_spec.js | 15 ++++++++-- 6 files changed, 74 insertions(+), 16 deletions(-) diff --git a/modules/adkernelAdnAnalyticsAdapter.js b/modules/adkernelAdnAnalyticsAdapter.js index b6db6fc22de..23501f7dd63 100644 --- a/modules/adkernelAdnAnalyticsAdapter.js +++ b/modules/adkernelAdnAnalyticsAdapter.js @@ -3,12 +3,14 @@ import CONSTANTS from '../src/constants.json'; import adapterManager from '../src/adapterManager.js'; import * as utils from '../src/utils.js'; import {ajax} from '../src/ajax.js'; -import { getStorageManager } from '../src/storageManager.js'; +import {getStorageManager} from '../src/storageManager.js'; +import {config} from '../src/config.js'; -const ANALYTICS_VERSION = '1.0.1'; +const GVLID = 14; +const ANALYTICS_VERSION = '1.0.2'; const DEFAULT_QUEUE_TIMEOUT = 4000; const DEFAULT_HOST = 'tag.adkernel.com'; -const storageObj = getStorageManager(); +const storageObj = getStorageManager(GVLID); const ADK_HB_EVENTS = { AUCTION_INIT: 'auctionInit', @@ -34,6 +36,7 @@ function buildRequestTemplate(pubId) { }, lang: navigator.language }, + user: {}, src: getUmtSource(loc.href, ref) } } @@ -50,6 +53,7 @@ let analyticsAdapter = Object.assign(adapter({analyticsType: 'endpoint'}), if (analyticsAdapter.context.queue) { analyticsAdapter.context.queue.init(); } + initPrivacy(analyticsAdapter.context.requestTemplate, args.bidderRequests); handler = trackAuctionInit; break; case CONSTANTS.EVENTS.BID_REQUESTED: @@ -100,7 +104,8 @@ analyticsAdapter.enableAnalytics = (config) => { adapterManager.registerAnalyticsAdapter({ adapter: analyticsAdapter, - code: 'adkernelAdn' + code: 'adkernelAdn', + gvlid: GVLID }); export default analyticsAdapter; @@ -390,3 +395,19 @@ function getLocationAndReferrer(win) { loc: win.location }; } + +function initPrivacy(template, requests) { + let consent = requests[0].gdprConsent; + if (consent && consent.gdprApplies) { + template.user.gdpr = ~~consent.gdprApplies; + } + if (consent && consent.consentString) { + template.user.gdpr_consent = consent.consentString; + } + if (requests[0].uspConsent) { + template.user.us_privacy = requests[0].uspConsent; + } + if (config.getConfig('coppa')) { + template.user.coppa = 1; + } +} diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index 0a0317e1f59..c838d93b8f1 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -1,11 +1,13 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {config} from '../src/config.js'; const DEFAULT_ADKERNEL_DSP_DOMAIN = 'tag.adkernel.com'; const DEFAULT_MIMES = ['video/mp4', 'video/webm', 'application/x-shockwave-flash', 'application/javascript']; const DEFAULT_PROTOCOLS = [2, 3, 5, 6]; const DEFAULT_APIS = [1, 2]; +const GVLID = 14; function isRtbDebugEnabled(refInfo) { return refInfo.referer.indexOf('adk_debug=true') !== -1; @@ -67,6 +69,9 @@ function buildRequestParams(tags, bidderRequest) { if (uspConsent) { utils.deepSetValue(req, 'user.us_privacy', uspConsent); } + if (config.getConfig('coppa')) { + utils.deepSetValue(req, 'user.coppa', 1); + } return req; } @@ -110,6 +115,7 @@ function buildBid(tag) { export const spec = { code: 'adkernelAdn', + gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], aliases: ['engagesimply'], diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 03fa5c2b2d9..b6d82aec976 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -22,6 +22,7 @@ const SYNC_TYPES = Object.freeze({ 1: 'iframe', 2: 'image' }); +const GVLID = 14; const NATIVE_MODEL = [ {name: 'title', assetType: 'title'}, @@ -50,8 +51,8 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => { * Adapter for requesting bids from AdKernel white-label display platform */ export const spec = { - code: 'adkernel', + gvlid: GVLID, aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs', 'torchad', 'stringads', 'bcm'], supportedMediaTypes: [BANNER, VIDEO, NATIVE], @@ -320,7 +321,7 @@ function getAllowedSyncMethod(bidderCode) { */ function buildRtbRequest(imps, bidderRequest, schain) { let {bidderCode, gdprConsent, auctionId, refererInfo, timeout, uspConsent} = bidderRequest; - + let coppa = config.getConfig('coppa'); let req = { 'id': auctionId, 'imp': imps, @@ -349,6 +350,9 @@ function buildRtbRequest(imps, bidderRequest, schain) { if (uspConsent) { utils.deepSetValue(req, 'regs.ext.us_privacy', uspConsent); } + if (coppa) { + utils.deepSetValue(req, 'regs.coppa', 1); + } let syncMethod = getAllowedSyncMethod(bidderCode); if (syncMethod) { utils.deepSetValue(req, 'ext.adk_usersync', syncMethod); diff --git a/test/spec/modules/adkernelAdnAnalytics_spec.js b/test/spec/modules/adkernelAdnAnalytics_spec.js index e7ef831080c..7af96c9321c 100644 --- a/test/spec/modules/adkernelAdnAnalytics_spec.js +++ b/test/spec/modules/adkernelAdnAnalytics_spec.js @@ -1,6 +1,6 @@ -import analyticsAdapter, {ExpiringQueue, getUmtSource, storage} from 'modules/adkernelAdnAnalyticsAdapter.js'; +import analyticsAdapter, {ExpiringQueue, getUmtSource, storage} from 'modules/adkernelAdnAnalyticsAdapter'; import {expect} from 'chai'; -import adapterManager from 'src/adapterManager.js'; +import adapterManager from 'src/adapterManager'; import CONSTANTS from 'src/constants.json'; const events = require('../../../src/events'); @@ -158,11 +158,16 @@ describe('', function () { sizes: [[300, 250]], bidId: '208750227436c1', bidderRequestId: '1a6fc81528d0f6', - auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f' + auctionId: '5018eb39-f900-4370-b71e-3bb5b48d324f', + gdprConsent: { + consentString: 'CONSENT', + gdprApplies: true, + apiVersion: 2 + }, + uspConsent: '1---' }], auctionStart: 1509369418387, - timeout: 3000, - start: 1509369418389 + timeout: 3000 }; const RESPONSE = { @@ -202,6 +207,10 @@ describe('', function () { events.getEvents.restore(); }); + after(function() { + sandbox.restore(); + }); + it('should be configurable', function () { adapterManager.registerAnalyticsAdapter({ code: 'adkernelAdn', @@ -221,7 +230,7 @@ describe('', function () { }); it('should handle auction init event', function () { - events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {config: {}, timeout: 3000}); + events.emit(CONSTANTS.EVENTS.AUCTION_INIT, {config: {}, bidderRequests: [REQUEST], timeout: 3000}); const ev = analyticsAdapter.context.queue.peekAll(); expect(ev).to.have.length(1); expect(ev[0]).to.be.eql({event: 'auctionInit'}); diff --git a/test/spec/modules/adkernelAdnBidAdapter_spec.js b/test/spec/modules/adkernelAdnBidAdapter_spec.js index 3d3e64aeec9..a3063df84fe 100644 --- a/test/spec/modules/adkernelAdnBidAdapter_spec.js +++ b/test/spec/modules/adkernelAdnBidAdapter_spec.js @@ -1,5 +1,6 @@ import {expect} from 'chai'; -import {spec} from 'modules/adkernelAdnBidAdapter.js'; +import {spec} from 'modules/adkernelAdnBidAdapter'; +import {config} from 'src/config'; describe('AdkernelAdn adapter', function () { const bid1_pub1 = { @@ -263,6 +264,14 @@ describe('AdkernelAdn adapter', function () { expect(bidRequests[0].user).to.have.property('gdpr', 0); expect(bidRequests[0].user).to.not.have.property('consent'); }); + + it('should\'t contain consent string if gdpr isn\'t applied', function () { + config.setConfig({coppa: true}); + let [_, bidRequests] = buildRequest([bid1_pub1]); + config.resetConfig(); + expect(bidRequests[0]).to.have.property('user'); + expect(bidRequests[0].user).to.have.property('coppa', 1); + }); }); describe('video request building', () => { diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 0d772423a22..9881acc68df 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -1,8 +1,8 @@ import {expect} from 'chai'; -import {spec} from 'modules/adkernelBidAdapter.js'; -import * as utils from 'src/utils.js'; +import {spec} from 'modules/adkernelBidAdapter'; +import * as utils from 'src/utils'; import {NATIVE, BANNER, VIDEO} from 'src/mediaTypes'; -import {config} from 'src/config.js'; +import {config} from 'src/config'; describe('Adkernel adapter', function () { const bid1_zone1 = { @@ -241,6 +241,7 @@ describe('Adkernel adapter', function () { afterEach(function () { sandbox.restore(); + config.resetConfig(); }); function buildBidderRequest(url = 'https://example.com/index.html', params = {}) { @@ -343,6 +344,14 @@ describe('Adkernel adapter', function () { expect(bidRequest.user.ext).to.be.eql({'consent': 'test-consent-string'}); }); + it('should contain coppa if configured', function () { + config.setConfig({coppa: true}); + let [_, bidRequests] = buildRequest([bid1_zone1]); + let bidRequest = bidRequests[0]; + expect(bidRequest).to.have.property('regs'); + expect(bidRequest.regs).to.have.property('coppa', 1); + }); + it('should\'t contain consent string if gdpr isn\'t applied', function () { let [_, bidRequests] = buildRequest([bid1_zone1], buildBidderRequest('https://example.com/index.html', {gdprConsent: {gdprApplies: false}})); let bidRequest = bidRequests[0]; From 7a7af27422b8e3971d60c55d44814ad9012823b2 Mon Sep 17 00:00:00 2001 From: RAJKUMAR NATARAJAN Date: Mon, 5 Apr 2021 11:25:07 -0400 Subject: [PATCH 35/93] SynacorMedia: remove adId from the bid response (#6520) --- modules/synacormediaBidAdapter.js | 1 - .../modules/synacormediaBidAdapter_spec.js | 44 ++++++++----------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index 55a91d87448..edf34394d62 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -219,7 +219,6 @@ export const spec = { } const bidObj = { requestId: impid, - adId: bid.id.replace(/~/g, '-'), cpm: parseFloat(bid.price), width: parseInt(width, 10), height: parseInt(height, 10), diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js index 5ec8700ce89..6ed6a38ff1f 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -1,6 +1,6 @@ import { assert, expect } from 'chai'; import { BANNER } from 'src/mediaTypes.js'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import { spec } from 'modules/synacormediaBidAdapter.js'; describe('synacormediaBidAdapter ', function () { @@ -55,7 +55,7 @@ describe('synacormediaBidAdapter ', function () { }); }); - describe('impression type', function() { + describe('impression type', function () { let nonVideoReq = { bidId: '9876abcd', sizes: [[300, 250], [300, 600]], @@ -107,7 +107,7 @@ describe('synacormediaBidAdapter ', function () { } }, }; - it('should return correct impression type video/banner', function() { + it('should return correct impression type video/banner', function () { assert.isFalse(spec.isVideoBid(nonVideoReq)); assert.isFalse(spec.isVideoBid(bannerReq)); assert.isTrue(spec.isVideoBid(videoReq)); @@ -126,12 +126,12 @@ describe('synacormediaBidAdapter ', function () { mediaTypes: { video: { context: 'instream', - playerSize: [[ 640, 480 ]] + playerSize: [[640, 480]] } }, adUnitCode: 'video1', transactionId: '93e5def8-29aa-4fe8-bd3a-0298c39f189a', - sizes: [[ 640, 480 ]], + sizes: [[640, 480]], bidId: '2624fabbb078e8', bidderRequestId: '117954d20d7c9c', auctionId: 'defd525f-4f1e-4416-a4cb-ae53be90e706', @@ -149,7 +149,7 @@ describe('synacormediaBidAdapter ', function () { referer: 'https://localhost:9999/test/pages/video.html?pbjs_debug=true', reachedTop: true, numIframes: 0, - stack: [ 'https://localhost:9999/test/pages/video.html?pbjs_debug=true' ] + stack: ['https://localhost:9999/test/pages/video.html?pbjs_debug=true'] }, start: 1553624929700 }; @@ -427,7 +427,7 @@ describe('synacormediaBidAdapter ', function () { expect(spec.buildRequests([validBidRequest], null)).to.be.undefined; }); - it('should return empty impression when there is no valid sizes in bidrequest', function() { + it('should return empty impression when there is no valid sizes in bidrequest', function () { let validBidReqWithoutSize = { bidId: '9876abcd', sizes: [], @@ -480,12 +480,12 @@ describe('synacormediaBidAdapter ', function () { mediaTypes: { video: { context: 'instream', - playerSize: [[ 640, 480 ]] + playerSize: [[640, 480]] } }, adUnitCode: 'video1', transactionId: '93e5def8-29aa-4fe8-bd3a-0298c39f189a', - sizes: [[ 640, 480 ]], + sizes: [[640, 480]], bidId: '2624fabbb078e8', bidderRequestId: '117954d20d7c9c', auctionId: 'defd525f-4f1e-4416-a4cb-ae53be90e706', @@ -534,7 +534,7 @@ describe('synacormediaBidAdapter ', function () { mediaTypes: { video: { context: 'instream', - playerSize: [[ 640, 480 ]], + playerSize: [[640, 480]], startdelay: 1, linearity: 1, placement: 1, @@ -543,7 +543,7 @@ describe('synacormediaBidAdapter ', function () { }, adUnitCode: 'video1', transactionId: '93e5def8-29aa-4fe8-bd3a-0298c39f189a', - sizes: [[ 640, 480 ]], + sizes: [[640, 480]], bidId: '2624fabbb078e8', bidderRequestId: '117954d20d7c9c', auctionId: 'defd525f-4f1e-4416-a4cb-ae53be90e706', @@ -576,7 +576,7 @@ describe('synacormediaBidAdapter ', function () { } ]); }); - it('should contain the CCPA privacy string when UspConsent is in bidder request', function() { + it('should contain the CCPA privacy string when UspConsent is in bidder request', function () { // banner test let req = spec.buildRequests([validBidRequest], bidderRequestWithCCPA); expect(req).be.an('object'); @@ -652,7 +652,7 @@ describe('synacormediaBidAdapter ', function () { }); }); - describe('Bid Requests with schain object ', function() { + describe('Bid Requests with schain object ', function () { let validBidReq = { bidder: 'synacormedia', params: { @@ -803,7 +803,7 @@ describe('synacormediaBidAdapter ', function () { url: 'https://prebid.technoratimedia.com/openrtb/bids/prebid?src=prebid_prebid_3.27.0-pre' }; let serverResponse; - beforeEach(function() { + beforeEach(function () { serverResponse = { body: { id: 'abc123', @@ -848,7 +848,7 @@ describe('synacormediaBidAdapter ', function () { price: 0.45, nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}', adm: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}\n\n\n', - adomain: [ 'psacentral.org' ], + adomain: ['psacentral.org'], cid: 'bidder-crid', crid: 'bidder-cid', cat: [], @@ -867,7 +867,6 @@ describe('synacormediaBidAdapter ', function () { expect(resp).to.be.an('array').to.have.lengthOf(1); expect(resp[0]).to.eql({ requestId: '2da7322b2df61f', - adId: '11339128001692337-9999-0', cpm: 0.45, width: 640, height: 480, @@ -888,7 +887,6 @@ describe('synacormediaBidAdapter ', function () { expect(resp).to.be.an('array').to.have.lengthOf(1); expect(resp[0]).to.eql({ requestId: '9876abcd', - adId: '10865933907263896-9998-0', cpm: 0.13, width: 300, height: 250, @@ -911,7 +909,6 @@ describe('synacormediaBidAdapter ', function () { expect(resp).to.be.an('array').to.have.lengthOf(2); expect(resp[0]).to.eql({ requestId: '9876abcd', - adId: '10865933907263896-9998-0', cpm: 0.13, width: 300, height: 250, @@ -925,7 +922,6 @@ describe('synacormediaBidAdapter ', function () { expect(resp[1]).to.eql({ requestId: '9876abcd', - adId: '10865933907263800-9999-0', cpm: 1.99, width: 300, height: 600, @@ -962,7 +958,7 @@ describe('synacormediaBidAdapter ', function () { price: 0.45, nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}', adm: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}\n\n\n', - adomain: [ 'psacentral.org' ], + adomain: ['psacentral.org'], cid: 'bidder-crid', crid: 'bidder-cid', cat: [], @@ -984,8 +980,8 @@ describe('synacormediaBidAdapter ', function () { }); let resp = spec.interpretResponse(serverRespVideo, bidRequest); - sandbox.restore(); - expect(resp[0].videoCacheKey).to.not.exist; + sandbox.restore(); + expect(resp[0].videoCacheKey).to.not.exist; }); it('should use video bid request height and width if not present in response', function () { @@ -1022,7 +1018,7 @@ describe('synacormediaBidAdapter ', function () { price: 0.45, nurl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}', adm: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=${AUCTION_PRICE}\n\n\n', - adomain: [ 'psacentral.org' ], + adomain: ['psacentral.org'], cid: 'bidder-crid', crid: 'bidder-cid', cat: [] @@ -1037,7 +1033,6 @@ describe('synacormediaBidAdapter ', function () { expect(resp).to.be.an('array').to.have.lengthOf(1); expect(resp[0]).to.eql({ requestId: '2da7322b2df61f', - adId: '11339128001692337-9999-0', cpm: 0.45, width: 300, height: 250, @@ -1090,7 +1085,6 @@ describe('synacormediaBidAdapter ', function () { expect(resp).to.be.an('array').to.have.lengthOf(1); expect(resp[0]).to.eql({ requestId: 'abc123', - adId: '10865933907263896-9998-0', cpm: 0.13, width: 400, height: 350, From e519ead3576ec02fc95cdec9a08c5b6e21d2da02 Mon Sep 17 00:00:00 2001 From: bretg Date: Mon, 5 Apr 2021 13:21:39 -0400 Subject: [PATCH 36/93] Rubicon: making doc data types consistent (#6526) --- modules/rubiconBidAdapter.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/rubiconBidAdapter.md b/modules/rubiconBidAdapter.md index beb29a6baf1..0ef22a81972 100644 --- a/modules/rubiconBidAdapter.md +++ b/modules/rubiconBidAdapter.md @@ -70,9 +70,9 @@ globalsupport@rubiconproject.com for more information. bids: [{ bidder: 'rubicon', params: { - accountId: '7780', - siteId: '87184', - zoneId: '412394', + accountId: 7780, + siteId: 87184, + zoneId: 412394, video: { language: 'en' } From e8b02b9063c56ce02466493b2e7d8a9467b1cc9e Mon Sep 17 00:00:00 2001 From: RAJKUMAR NATARAJAN Date: Mon, 5 Apr 2021 15:43:57 -0400 Subject: [PATCH 37/93] Synacormedia Bid Adapter: add meta.advertiserDomains (#6527) --- modules/synacormediaBidAdapter.js | 6 +++++- test/spec/modules/synacormediaBidAdapter_spec.js | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/synacormediaBidAdapter.js b/modules/synacormediaBidAdapter.js index edf34394d62..d14fee67b23 100644 --- a/modules/synacormediaBidAdapter.js +++ b/modules/synacormediaBidAdapter.js @@ -182,7 +182,6 @@ export const spec = { logWarn('Synacormedia: server returned empty/non-json response: ' + JSON.stringify(serverResponse.body)); return; } - const {id, seatbid: seatbids} = serverResponse.body; let bids = []; if (id && seatbids) { @@ -229,6 +228,11 @@ export const spec = { ad: creative, ttl: 60 }; + + if (bid.adomain != undefined || bid.adomain != null) { + bidObj.meta = { advertiserDomains: bid.adomain }; + } + if (isVideo) { const [, uuid] = nurl.match(/ID=([^&]*)&?/); if (!config.getConfig('cache.url')) { diff --git a/test/spec/modules/synacormediaBidAdapter_spec.js b/test/spec/modules/synacormediaBidAdapter_spec.js index 6ed6a38ff1f..2ead2fb689e 100644 --- a/test/spec/modules/synacormediaBidAdapter_spec.js +++ b/test/spec/modules/synacormediaBidAdapter_spec.js @@ -876,6 +876,7 @@ describe('synacormediaBidAdapter ', function () { mediaType: 'video', ad: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=0.45\n\n\n', ttl: 60, + meta: { advertiserDomains: ['psacentral.org'] }, videoCacheKey: 'QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk', vastUrl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=0.45' }); @@ -1042,6 +1043,7 @@ describe('synacormediaBidAdapter ', function () { mediaType: 'video', ad: '\n\n\n\nSynacor Media Ad Server - 9999\nhttps://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=0.45\n\n\n', ttl: 60, + meta: { advertiserDomains: ['psacentral.org'] }, videoCacheKey: 'QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk', vastUrl: 'https://uat-net.technoratimedia.com/openrtb/tags?ID=QVVDVElPTl9JRD1lOTBhYWU1My1hZDkwLTRkNDEtYTQxMC1lZDY1MjIxMDc0ZGMmQVVDVElPTl9CSURfSUQ9MTEzMzkxMjgwMDE2OTIzMzd-OTk5OX4wJkFVQ1RJT05fU0VBVF9JRD05OTk5JkFVQ1RJT05fSU1QX0lEPXYyZGE3MzIyYjJkZjYxZi02NDB4NDgwJkFDVE9SX1JFRj1ha2thLnRjcDovL2F3cy1lYXN0MUBhZHMxMy5jYXAtdXNlMS5zeW5hY29yLmNvbToyNTUxL3VzZXIvJGNMYmZiIy0xOTk4NTIzNTk3JlNFQVRfSUQ9cHJlYmlk&AUCTION_PRICE=0.45' }); From 25ba1111d7aa0fbb47882e797a5847ab86f8ef37 Mon Sep 17 00:00:00 2001 From: Alexander Clouter Date: Mon, 5 Apr 2021 21:17:45 +0100 Subject: [PATCH 38/93] Adloox Analytics Adapter: add new analytics adapter (#6308) * gulp: fix supplying list of browsers to test against The following now works: gulp test --browserstack --nolint --nolintfix --browsers=bs_ie_11_windows_10 --file 'test/spec/modules/adloox{AnalyticsAdapter,AdServerVideo,RtdProvider}_spec.js' * instreamTracking: unit test tidy From @robertrmartinez in https://github.com/prebid/Prebid.js/pull/6308#issuecomment-810537538 * adloaderStub: expose stub for other unit tests to use From @robertrmartinez in https://github.com/prebid/Prebid.js/pull/6308#issuecomment-810537538 * Adloox analytic module --- gulpfile.js | 2 +- integrationExamples/gpt/adloox.html | 211 +++++++++++++ modules/adlooxAnalyticsAdapter.js | 288 ++++++++++++++++++ modules/adlooxAnalyticsAdapter.md | 146 +++++++++ src/adloader.js | 1 + test/mocks/adloaderStub.js | 6 +- .../modules/adlooxAnalyticsAdapter_spec.js | 229 ++++++++++++++ test/spec/modules/instreamTracking_spec.js | 2 +- 8 files changed, 880 insertions(+), 5 deletions(-) create mode 100644 integrationExamples/gpt/adloox.html create mode 100644 modules/adlooxAnalyticsAdapter.js create mode 100644 modules/adlooxAnalyticsAdapter.md create mode 100644 test/spec/modules/adlooxAnalyticsAdapter_spec.js diff --git a/gulpfile.js b/gulpfile.js index d7b91db4ade..ac8b8c2dcd5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -286,7 +286,7 @@ function test(done) { } else { var karmaConf = karmaConfMaker(false, argv.browserstack, argv.watch, argv.file); - var browserOverride = helpers.parseBrowserArgs(argv).map(helpers.toCapitalCase); + var browserOverride = helpers.parseBrowserArgs(argv); if (browserOverride.length > 0) { karmaConf.browsers = browserOverride; } diff --git a/integrationExamples/gpt/adloox.html b/integrationExamples/gpt/adloox.html new file mode 100644 index 00000000000..33f8b9be6a2 --- /dev/null +++ b/integrationExamples/gpt/adloox.html @@ -0,0 +1,211 @@ + + + + Prebid Display/Video Merged Auction with Adloox Integration + + + + + + + + +

Prebid Display/Video Merged Auction with Adloox Integration

+ +

div-1

+
+ +
+ +

div-2

+
+ +
+ +

video-1

+
+ + + + diff --git a/modules/adlooxAnalyticsAdapter.js b/modules/adlooxAnalyticsAdapter.js new file mode 100644 index 00000000000..3e92ae34004 --- /dev/null +++ b/modules/adlooxAnalyticsAdapter.js @@ -0,0 +1,288 @@ +/** + * This module provides [Adloox]{@link https://www.adloox.com/} Analytics + * The module will inject Adloox's verification JS tag alongside slot at bidWin + * @module modules/adlooxAnalyticsAdapter + */ + +import adapterManager from '../src/adapterManager.js'; +import adapter from '../src/AnalyticsAdapter.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { auctionManager } from '../src/auctionManager.js'; +import { AUCTION_COMPLETED } from '../src/auction.js'; +import { EVENTS } from '../src/constants.json'; +import find from 'core-js-pure/features/array/find.js'; +import * as utils from '../src/utils.js'; + +const MODULE = 'adlooxAnalyticsAdapter'; + +const URL_JS = 'https://j.adlooxtracking.com/ads/js/tfav_adl_%%clientid%%.js'; + +const ADLOOX_VENDOR_ID = 93; + +const ADLOOX_MEDIATYPE = { + DISPLAY: 2, + VIDEO: 6 +}; + +const MACRO = {}; +MACRO['client'] = function(b, c) { + return c.client; +}; +MACRO['clientid'] = function(b, c) { + return c.clientid; +}; +MACRO['tagid'] = function(b, c) { + return c.tagid; +}; +MACRO['platformid'] = function(b, c) { + return c.platformid; +}; +MACRO['targetelt'] = function(b, c) { + return c.toselector(b); +}; +MACRO['creatype'] = function(b, c) { + return b.mediaType == 'video' ? ADLOOX_MEDIATYPE.VIDEO : ADLOOX_MEDIATYPE.DISPLAY; +}; +MACRO['pbAdSlot'] = function(b, c) { + const adUnit = find(auctionManager.getAdUnits(), a => b.adUnitCode === a.code); + return utils.deepAccess(adUnit, 'fpd.context.pbAdSlot') || utils.getGptSlotInfoForAdUnitCode(b.adUnitCode).gptSlot || b.adUnitCode; +}; + +const PARAMS_DEFAULT = { + 'id1': function(b) { return b.adUnitCode }, + 'id2': '%%pbAdSlot%%', + 'id3': function(b) { return b.bidder }, + 'id4': function(b) { return b.adId }, + 'id5': function(b) { return b.dealId }, + 'id6': function(b) { return b.creativeId }, + 'id7': function(b) { return b.size }, + 'id11': '$ADLOOX_WEBSITE' +}; + +const NOOP = function() {}; + +let analyticsAdapter = Object.assign(adapter({ analyticsType: 'endpoint' }), { + track({ eventType, args }) { + if (!analyticsAdapter[`handle_${eventType}`]) return; + + utils.logInfo(MODULE, 'track', eventType, args); + + analyticsAdapter[`handle_${eventType}`](args); + } +}); + +analyticsAdapter.context = null; + +analyticsAdapter.originEnableAnalytics = analyticsAdapter.enableAnalytics; +analyticsAdapter.enableAnalytics = function(config) { + analyticsAdapter.context = null; + + utils.logInfo(MODULE, 'config', config); + + if (!utils.isPlainObject(config.options)) { + utils.logError(MODULE, 'missing options'); + return; + } + if (!(config.options.js === undefined || utils.isStr(config.options.js))) { + utils.logError(MODULE, 'invalid js options value'); + return; + } + if (!(config.options.toselector === undefined || utils.isFn(config.options.toselector))) { + utils.logError(MODULE, 'invalid toselector options value'); + return; + } + if (!utils.isStr(config.options.client)) { + utils.logError(MODULE, 'invalid client options value'); + return; + } + if (!utils.isNumber(config.options.clientid)) { + utils.logError(MODULE, 'invalid clientid options value'); + return; + } + if (!utils.isNumber(config.options.tagid)) { + utils.logError(MODULE, 'invalid tagid options value'); + return; + } + if (!utils.isNumber(config.options.platformid)) { + utils.logError(MODULE, 'invalid platformid options value'); + return; + } + if (!(config.options.params === undefined || utils.isPlainObject(config.options.params))) { + utils.logError(MODULE, 'invalid params options value'); + return; + } + + analyticsAdapter.context = { + js: config.options.js || URL_JS, + toselector: config.options.toselector || function(bid) { + let code = utils.getGptSlotInfoForAdUnitCode(bid.adUnitCode).divId || bid.adUnitCode; + // https://mathiasbynens.be/notes/css-escapes + code = code.replace(/^\d/, '\\3$& '); + return `#${code}` + }, + client: config.options.client, + clientid: config.options.clientid, + tagid: config.options.tagid, + platformid: config.options.platformid, + params: [] + }; + + config.options.params = utils.mergeDeep({}, PARAMS_DEFAULT, config.options.params || {}); + Object + .keys(config.options.params) + .forEach(k => { + if (!Array.isArray(config.options.params[k])) { + config.options.params[k] = [ config.options.params[k] ]; + } + config.options.params[k].forEach(v => analyticsAdapter.context.params.push([ k, v ])); + }); + + Object.keys(COMMAND_QUEUE).forEach(commandProcess); + + analyticsAdapter.originEnableAnalytics(config); +} + +analyticsAdapter.originDisableAnalytics = analyticsAdapter.disableAnalytics; +analyticsAdapter.disableAnalytics = function() { + analyticsAdapter.context = null; + + analyticsAdapter.originDisableAnalytics(); +} + +analyticsAdapter.url = function(url, args, bid) { + // utils.formatQS outputs PHP encoded querystrings... (╯°□°)╯ ┻━┻ + function a2qs(a) { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent + function fixedEncodeURIComponent(str) { + return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { + return '%' + c.charCodeAt(0).toString(16); + }); + } + + const args = []; + let n = a.length; + while (n-- > 0) { + if (!(a[n][1] === undefined || a[n][1] === null || a[n][1] === false)) { + args.unshift(fixedEncodeURIComponent(a[n][0]) + (a[n][1] !== true ? ('=' + fixedEncodeURIComponent(a[n][1])) : '')); + } + } + + return args.join('&'); + } + + const macros = (str) => { + return str.replace(/%%([a-z]+)%%/gi, (match, p1) => MACRO[p1] ? MACRO[p1](bid, analyticsAdapter.context) : match); + }; + + url = macros(url); + args = args || []; + + let n = args.length; + while (n-- > 0) { + if (utils.isFn(args[n][1])) { + try { + args[n][1] = args[n][1](bid); + } catch (_) { + utils.logError(MODULE, 'macro', args[n][0], _.message); + args[n][1] = `ERROR: ${_.message}`; + } + } + if (utils.isStr(args[n][1])) { + args[n][1] = macros(args[n][1]); + } + } + + return url + a2qs(args); +} + +analyticsAdapter[`handle_${EVENTS.AUCTION_END}`] = function(auctionDetails) { + if (!(auctionDetails.auctionStatus == AUCTION_COMPLETED && auctionDetails.bidsReceived.length > 0)) return; + analyticsAdapter[`handle_${EVENTS.AUCTION_END}`] = NOOP; + + utils.logMessage(MODULE, 'preloading verification JS'); + + const uri = utils.parseUrl(analyticsAdapter.url(`${analyticsAdapter.context.js}#`)); + + const link = document.createElement('link'); + link.setAttribute('href', `${uri.protocol}://${uri.host}${uri.pathname}`); + link.setAttribute('rel', 'preload'); + link.setAttribute('as', 'script'); + utils.insertElement(link); +} + +analyticsAdapter[`handle_${EVENTS.BID_WON}`] = function(bid) { + const sl = analyticsAdapter.context.toselector(bid); + let el; + try { + el = document.querySelector(sl); + } catch (_) { } + if (!el) { + utils.logWarn(MODULE, `unable to find ad unit code '${bid.adUnitCode}' slot using selector '${sl}' (use options.toselector to change), ignoring`); + return; + } + + utils.logMessage(MODULE, `measuring '${bid.mediaType}' unit at '${bid.adUnitCode}'`); + + const params = analyticsAdapter.context.params.concat([ + [ 'tagid', '%%tagid%%' ], + [ 'platform', '%%platformid%%' ], + [ 'fwtype', 4 ], + [ 'targetelt', '%%targetelt%%' ], + [ 'creatype', '%%creatype%%' ] + ]); + + loadExternalScript(analyticsAdapter.url(`${analyticsAdapter.context.js}#`, params, bid), 'adloox'); +} + +adapterManager.registerAnalyticsAdapter({ + adapter: analyticsAdapter, + code: 'adloox', + gvlid: ADLOOX_VENDOR_ID +}); + +export default analyticsAdapter; + +// src/events.js does not support custom events or handle races... (╯°□°)╯ ┻━┻ +const COMMAND_QUEUE = {}; +export const COMMAND = { + CONFIG: 'config', + URL: 'url', + TRACK: 'track' +}; +export function command(cmd, data, callback0) { + const cid = utils.getUniqueIdentifierStr(); + const callback = function() { + delete COMMAND_QUEUE[cid]; + if (callback0) callback0.apply(null, arguments); + }; + COMMAND_QUEUE[cid] = { cmd, data, callback }; + if (analyticsAdapter.context) commandProcess(cid); +} +function commandProcess(cid) { + const { cmd, data, callback } = COMMAND_QUEUE[cid]; + + utils.logInfo(MODULE, 'command', cmd, data); + + switch (cmd) { + case COMMAND.CONFIG: + const response = { + client: analyticsAdapter.context.client, + clientid: analyticsAdapter.context.clientid, + tagid: analyticsAdapter.context.tagid, + platformid: analyticsAdapter.context.platformid + }; + callback(response); + break; + case COMMAND.URL: + if (data.ids) data.args = data.args.concat(analyticsAdapter.context.params.filter(p => /^id([1-9]|10)$/.test(p[0]))); // not >10 + callback(analyticsAdapter.url(data.url, data.args, data.bid)); + break; + case COMMAND.TRACK: + analyticsAdapter.track(data); + callback(); // drain queue + break; + default: + utils.logWarn(MODULE, 'command unknown', cmd); + // do not callback as arguments are unknown and to aid debugging + } +} diff --git a/modules/adlooxAnalyticsAdapter.md b/modules/adlooxAnalyticsAdapter.md new file mode 100644 index 00000000000..0ca67f937f6 --- /dev/null +++ b/modules/adlooxAnalyticsAdapter.md @@ -0,0 +1,146 @@ +# Overview + + Module Name: Adloox Analytics Adapter + Module Type: Analytics Adapter + Maintainer: technique@adloox.com + +# Description + +Analytics adapter for adloox.com. Contact adops@adloox.com for information. + +This module can be used to track: + + * Display + * Native + * Video (see below for further instructions) + +The adapter adds an HTML `', + adid: '97517771', + adomain: [ + '' + ], + iurl: 'https://fra1-ib.adnxs.com/cr?id=97517771', + cid: '9325', + crid: '97517771', + w: 1, + h: 1, + ext: { + prebid: { + 'type': 'video' + }, + bidder: { + prebid: { + type: 'video', + video: { + duration: 30, + primary_category: '' + } + }, + bidder: { + appnexus: { + brand_id: 182979, + auction_id: 8657683934873599656, + bidder_id: 2, + bid_ad_type: 1, + creative_info: { + video: { + duration: 30, + mimes: [ + 'video/x-flv', + 'video/mp4', + 'video/webm' + ] + } + } + } + } + } + } + } + ], + seat: 'impactify' + } + ], + cur: DEFAULT_CURRENCY, + ext: { + responsetimemillis: { + impactify: 114 + }, + prebid: { + auctiontimestamp: 1614587024591 + } + } + }; + let bidderRequest = { + bids: [ + { + bidId: '462c08f20d428', + adUnitCode: '/19968336/header-bid-tag-1', + auctionId: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', + bidder: 'impactify', + sizes: [[DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT]], + mediaTypes: { + video: { + context: 'outstream' + } + } + }, + ] + } + let expectedResponse = [ + { + id: '65820304700829014', + requestId: '462c08f20d428', + cpm: 3.40, + currency: DEFAULT_CURRENCY, + netRevenue: true, + ad: '', + width: 1, + height: 1, + hash: 'test', + expiry: 166192938, + ttl: 300, + creativeId: '97517771' + } + ]; + let result = spec.interpretResponse({ body: response }, bidderRequest); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + }); + describe('getUserSyncs', function () { + let videoBidRequests = [ + { + bidder: 'impactify', + params: { + appId: '1', + format: 'screen', + style: 'inline' + }, + mediaTypes: { + video: { + context: 'instream' + } + }, + adUnitCode: 'adunit-code', + sizes: [[DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT]], + bidId: '123456789', + bidderRequestId: '987654321', + auctionId: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', + transactionId: 'f7b2c372-7a7b-11eb-9439-0242ac130002' + } + ]; + let videoBidderRequest = { + bidderRequestId: '98845765110', + auctionId: '165410516454', + bidderCode: 'impactify', + bids: [ + { + ...videoBidRequests[0] + } + ], + refererInfo: { + referer: 'https://impactify.io' + } + }; + let validResponse = { + id: '19ab94a9-b0d7-4ed7-9f80-ad0c033cf1b1', + seatbid: [ + { + bid: [ + { + id: '65820304700829014', + impid: '462c08f20d428', + price: 3.40, + adm: '', + adid: '97517771', + adomain: [ + '' + ], + iurl: 'https://fra1-ib.adnxs.com/cr?id=97517771', + cid: '9325', + crid: '97517771', + w: 1, + h: 1, + ext: { + prebid: { + 'type': 'video' + }, + bidder: { + prebid: { + type: 'video', + video: { + duration: 30, + primary_category: '' + } + }, + bidder: { + appnexus: { + brand_id: 182979, + auction_id: 8657683934873599656, + bidder_id: 2, + bid_ad_type: 1, + creative_info: { + video: { + duration: 30, + mimes: [ + 'video/x-flv', + 'video/mp4', + 'video/webm' + ] + } + } + } + } + } + } + } + ], + seat: 'impactify' + } + ], + cur: DEFAULT_CURRENCY, + ext: { + responsetimemillis: { + impactify: 114 + }, + prebid: { + auctiontimestamp: 1614587024591 + } + } + }; + it('should return empty response if server response is false', function () { + const result = spec.getUserSyncs('bad', false, gdprData); + expect(result).to.be.empty; + }); + it('should return empty response if server response is empty', function () { + const result = spec.getUserSyncs('bad', [], gdprData); + expect(result).to.be.empty; + }); + it('should append the various values if they exist', function() { + const result = spec.getUserSyncs({iframeEnabled: true}, validResponse, gdprData); + expect(result[0].url).to.include('gdpr=1'); + expect(result[0].url).to.include('gdpr_consent=BOh7mtYOh7mtYAcABBENCU-AAAAncgPIXJiiAoao0PxBFkgCAC8ACIAAQAQQAAIAAAIAAAhBGAAAQAQAEQgAAAAAAABAAAAAAAAAAAAAAACAAAAAAAACgAAAAABAAAAQAAAAAAA'); + }); + }); + + describe('On winning bid', function () { + const bid = { + ad: '', + cpm: '2' + }; + const result = spec.onBidWon(bid); + assert.ok(result); + }); + + describe('On bid Time out', function () { + const bid = { + ad: '', + cpm: '2' + }; + const result = spec.onTimeout(bid); + assert.ok(result); + }); +}) From 1157b6d6dd62e7837dd89f850dc3e5137a6c1a7a Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 7 Apr 2021 13:42:07 -0700 Subject: [PATCH 47/93] Prebid 4.34.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 60c295f53ec..5ffe75a8ecd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.34.0-pre", + "version": "4.34.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 71518cd94e09d3c3f8a3e18de37c208bac1f4467 Mon Sep 17 00:00:00 2001 From: Mike Chowla Date: Wed, 7 Apr 2021 14:07:54 -0700 Subject: [PATCH 48/93] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ffe75a8ecd..8b8a4dc5fb4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.34.0", + "version": "4.35.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 35de47d4073d78087b0e410b87fdacdc5e5def9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deivydas=20=C5=A0abaras?= Date: Thu, 8 Apr 2021 14:47:18 +0100 Subject: [PATCH 49/93] Prebid server adapter: add config for openx hosting (#6530) --- modules/prebidServerBidAdapter/config.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/prebidServerBidAdapter/config.js b/modules/prebidServerBidAdapter/config.js index 56e4fdfbfef..3a8f0b05f43 100644 --- a/modules/prebidServerBidAdapter/config.js +++ b/modules/prebidServerBidAdapter/config.js @@ -13,5 +13,12 @@ export const S2S_VENDORS = { endpoint: 'https://prebid-server.rubiconproject.com/openrtb2/auction', syncEndpoint: 'https://prebid-server.rubiconproject.com/cookie_sync', timeout: 500 + }, + 'openx': { + adapter: 'prebidServer', + enabled: true, + endpoint: 'https://prebid.openx.net/openrtb2/auction', + syncEndpoint: 'https://prebid.openx.net/cookie_sync', + timeout: 1000 } } From 955c472a7f4e5b114e740ffeaf899fc7b7469253 Mon Sep 17 00:00:00 2001 From: ym-atsymuk <81176595+ym-atsymuk@users.noreply.github.com> Date: Thu, 8 Apr 2021 16:59:53 +0300 Subject: [PATCH 50/93] Yieldmo adapter: add meta data to bids (#6550) --- modules/yieldmoBidAdapter.js | 10 +++- test/spec/modules/yieldmoBidAdapter_spec.js | 54 +++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index c1151617ac4..a4bd58140b9 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -189,6 +189,10 @@ function createNewBannerBid(response) { netRevenue: NET_REVENUE, ttl: TIME_TO_LIVE, ad: response.ad, + meta: { + advertiserDomains: response.adomain || [], + mediaType: BANNER, + }, }; } @@ -209,7 +213,11 @@ function createNewVideoBid(response, bidRequest) { netRevenue: NET_REVENUE, mediaType: VIDEO, ttl: TIME_TO_LIVE, - vastXml: response.adm + vastXml: response.adm, + meta: { + advertiserDomains: response.adomain || [], + mediaType: VIDEO, + }, }; } diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index deabef69093..c736f0e5be4 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -278,6 +278,7 @@ describe('YieldmoAdapter', function () { ad: '' + '
', creative_id: '9874652394875', + adomain: ['www.example.com'], }], header: 'header?', }); @@ -296,6 +297,59 @@ describe('YieldmoAdapter', function () { ttl: 300, ad: '' + '
', + meta: { + advertiserDomains: ['www.example.com'], + mediaType: 'banner', + }, + }); + }); + + it('should correctly reorder video bids', function () { + const response = mockServerResponse(); + const seatbid = [ + { + bid: { + adm: '', + adomain: ['www.example.com'], + crid: 'dd65c0a7536aff', + impid: '91ea8bba1', + price: 1.5, + }, + }, + ]; + const bidRequest = { + data: { + imp: [ + { + id: '91ea8bba1', + video: { + h: 250, + w: 300, + }, + }, + ], + }, + }; + + response.body.seatbid = seatbid; + + const newResponse = spec.interpretResponse(response, bidRequest); + expect(newResponse.length).to.be.equal(2); + expect(newResponse[1]).to.deep.equal({ + cpm: 1.5, + creativeId: 'dd65c0a7536aff', + currency: 'USD', + height: 250, + mediaType: 'video', + meta: { + advertiserDomains: ['www.example.com'], + mediaType: 'video', + }, + netRevenue: true, + requestId: '91ea8bba1', + ttl: 300, + vastXml: '', + width: 300, }); }); From 927496ea616887fdc07e064b5d6eb8103312fc48 Mon Sep 17 00:00:00 2001 From: Skylinar <53079123+Skylinar@users.noreply.github.com> Date: Thu, 8 Apr 2021 16:37:45 +0200 Subject: [PATCH 51/93] Smartx Bid Adapter: Add meta.advertiserDomains support (#6547) --- modules/smartxBidAdapter.js | 13 +++++++++++++ test/spec/modules/smartxBidAdapter_spec.js | 2 ++ 2 files changed, 15 insertions(+) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index 57c54cb5090..b95b31934a2 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -149,6 +149,7 @@ export const spec = { } const language = navigator.language ? 'language' : 'userLanguage'; + const device = { h: screen.height, w: screen.width, @@ -157,8 +158,11 @@ export const spec = { make: navigator.vendor ? navigator.vendor : '', ua: navigator.userAgent }; + const at = utils.getBidIdParameter('at', bid.params) || 2; + const cur = utils.getBidIdParameter('cur', bid.params) || ['EUR']; + const requestPayload = { id: utils.generateUUID(), imp: smartxReq, @@ -176,6 +180,7 @@ export const spec = { at: at, cur: cur }; + const userExt = {}; // Add GDPR flag and consent string @@ -271,6 +276,7 @@ export const spec = { serverResponseBody.cur = pmb.currency; } }); + const bid = { requestId: currentBidRequest.bidId, currency: serverResponseBody.cur || 'USD', @@ -284,7 +290,14 @@ export const spec = { width: smartxBid.w, height: smartxBid.h }; + + bid.meta = bid.meta || {}; + if (smartxBid && smartxBid.adomain && smartxBid.adomain.length > 0) { + bid.meta.advertiserDomains = smartxBid.adomain; + } + const context = utils.deepAccess(currentBidRequest, 'mediaTypes.video.context'); + if (context === 'outstream') { const playersize = utils.deepAccess(currentBidRequest, 'mediaTypes.video.playerSize'); const renderer = Renderer.install({ diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index abc06b48ce7..3c871c6f88b 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -423,6 +423,7 @@ describe('The smartx adapter', function () { expect(responses).to.be.an('array').with.length(2); expect(bidderRequestObj).to.be.an('Object'); expect(bidderRequestObj.bidRequest.bids).to.be.an('array').with.length(2); + expect(responses[0].meta.advertiserDomains[0]).to.equal('abc.com'); expect(responses[0].requestId).to.equal(123); expect(responses[0].currency).to.equal('USD'); expect(responses[0].cpm).to.equal(12); @@ -432,6 +433,7 @@ describe('The smartx adapter', function () { expect(responses[0].mediaType).to.equal('video'); expect(responses[0].width).to.equal(400); expect(responses[0].height).to.equal(300); + expect(responses[1].meta.advertiserDomains[0]).to.equal('def.com'); expect(responses[1].requestId).to.equal(124); expect(responses[1].currency).to.equal('USD'); expect(responses[1].cpm).to.equal(13); From 26a5462fdd0678ff65d1010d8d9a789c190d46b4 Mon Sep 17 00:00:00 2001 From: Adam Browning <19834421+adam-browning@users.noreply.github.com> Date: Thu, 8 Apr 2021 21:03:18 +0300 Subject: [PATCH 52/93] Onevideo / Adap.tv Adapter: updated example configuration (#6546) --- modules/oneVideoBidAdapter.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/oneVideoBidAdapter.md b/modules/oneVideoBidAdapter.md index 97c4e91ca78..a42433e80b5 100644 --- a/modules/oneVideoBidAdapter.md +++ b/modules/oneVideoBidAdapter.md @@ -46,6 +46,7 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to key2: 123345 } }, + bidfloor: 0.5, site: { id: 1, page: 'https://verizonmedia.com', @@ -92,6 +93,7 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to maxduration: 30, ttl: 250 }, + bidfloor: 0.5, site: { id: 1, page: 'https://verizonmedia.com', @@ -128,6 +130,7 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to position: 1, display: 1 }, + bidfloor: 0.5, site: { id: 1, page: 'https://verizonmedia.com', @@ -161,6 +164,7 @@ Connects to Verizon Media's Video SSP (AKA ONE Video / Adap.tv) demand source to mimes: ['video/mp4', 'application/javascript'], display: 1 }, + bidfloor: 0.5, site: { id: 1, page: 'https://verizonmedia.com', @@ -197,7 +201,7 @@ var adUnits = [ playerHeight: 250, mimes: ['video/mp4', 'application/javascript'], e2etest: true - } + }, pubId: 'YOUR_PUBLISHER_ID' } } @@ -237,6 +241,7 @@ var adUnits = [ api: [2], sid: }, + bidfloor: 0.5, site: { id: 1, page: 'https://verizonmedia.com', @@ -288,11 +293,12 @@ var adUnits = [ protocols: [2,5], api: [2], }, + bidfloor: 0.5, site: { id: 1, page: 'https://verizonmedia.com', referrer: 'https://verizonmedia.com' - }, + }, pubId: 'HBExchange' } } @@ -348,6 +354,7 @@ const adUnits = [{ } } }, + bidfloor: 0.5, pubId: 'HBExchange' } } @@ -380,6 +387,7 @@ const adUnits = [{ api: [1, 2], ttl: 300 }, + bidfloor: 0.5, pubId: 'HBExchange' } }] From 87a6b47c97eecc3d273ee64bb02e6703b0262a13 Mon Sep 17 00:00:00 2001 From: Catalin Ciocov Date: Fri, 9 Apr 2021 14:57:12 +0300 Subject: [PATCH 53/93] Mass Deal Rendering Module: support multiple custom configs for dealId and rendering (#6500) --- integrationExamples/mass/index.html | 24 +++- modules/mass.js | 173 ++++++++++++++++++---------- modules/mass.md | 100 +++++++++++++--- test/spec/modules/mass_spec.js | 49 +++++--- 4 files changed, 259 insertions(+), 87 deletions(-) diff --git a/integrationExamples/mass/index.html b/integrationExamples/mass/index.html index 80fe4cfb934..3b034957d13 100644 --- a/integrationExamples/mass/index.html +++ b/integrationExamples/mass/index.html @@ -36,8 +36,30 @@ pbjs.setConfig({ mass: { enabled: true, + + // official MASS-supported config: + dealIdPattern: /^MASS/i, renderUrl: 'https://cdn.massplatform.net/bootloader.js', - dealIdPattern: /^MASS/i + + // custom configs: + custom: [ + // simple: + { + dealIdPattern: /^abc/i, + renderUrl: 'https://my.domain.com/script.js' + }, + + // flexible: + { + match: function(bid) { + // return true or false, based on given bid + }, + + render: function(payload) { + // render the ad + } + } + ] } }); }); diff --git a/modules/mass.js b/modules/mass.js index 14fe556a466..01135e7ddff 100644 --- a/modules/mass.js +++ b/modules/mass.js @@ -6,15 +6,16 @@ import { config } from '../src/config.js'; import { getHook } from '../src/hook.js'; import find from 'core-js-pure/features/array/find.js'; -export let listenerAdded = false; -export let massEnabled = false; - const defaultCfg = { dealIdPattern: /^MASS/i }; let cfg; -const massBids = {}; +export let listenerAdded = false; +export let isEnabled = false; + +const matchedBids = {}; +let renderers; init(); config.getConfig('mass', config => init(config.mass)); @@ -22,68 +23,113 @@ config.getConfig('mass', config => init(config.mass)); /** * Module init. */ -export function init(customCfg) { - cfg = Object.assign({}, defaultCfg, customCfg); +export function init(userCfg) { + cfg = Object.assign({}, defaultCfg, window.massConfig && window.massConfig.mass, userCfg); if (cfg.enabled === false) { - if (massEnabled) { - massEnabled = false; + if (isEnabled) { getHook('addBidResponse').getHooks({hook: addBidResponseHook}).remove(); + isEnabled = false; } } else { - if (!massEnabled) { + if (!isEnabled) { getHook('addBidResponse').before(addBidResponseHook); - massEnabled = true; + isEnabled = true; } } + + if (isEnabled) { + updateRenderers(); + } } /** - * Before hook for 'addBidResponse'. + * Update the list of renderers based on current config. */ -export function addBidResponseHook(next, adUnitCode, bid) { - if (!isMassBid(bid) || !cfg.renderUrl) { - return next(adUnitCode, bid); +export function updateRenderers() { + renderers = []; + + // official MASS renderer: + if (cfg.dealIdPattern && cfg.renderUrl) { + renderers.push({ + match: isMassBid, + render: useDefaultRender(cfg.renderUrl, 'mass') + }); } - const bidRequest = find(this.bidderRequest.bids, bidRequest => - bidRequest.bidId === bid.requestId - ); - - massBids[bid.requestId] = { - bidRequest, - bid, - adm: bid.ad - }; + // add any custom renderer defined in the config: + (cfg.custom || []).forEach(renderer => { + if (!renderer.match && renderer.dealIdPattern) { + renderer.match = useDefaultMatch(renderer.dealIdPattern); + } - bid.ad = '`; + break; + } + }); + } + return result; +} + +function setOnAny(collection, key) { + for (let i = 0, result; i < collection.length; i++) { + result = utils.deepAccess(collection[i], key); + if (result) { + return result; + } + } +} + +function flatten(arr) { + return [].concat(...arr); +} + +function getNativeAssets(bid) { + return utils._map(bid.nativeParams, (bidParams, key) => { + const props = NATIVE_PARAMS[key]; + const asset = { + required: bidParams.required & 1, + }; + if (props) { + asset.id = props.id; + let wmin, hmin, w, h; + let aRatios = bidParams.aspect_ratios; + + if (aRatios && aRatios[0]) { + aRatios = aRatios[0]; + wmin = aRatios.min_width || 0; + hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; + } + + if (bidParams.sizes) { + const sizes = flatten(bidParams.sizes); + w = sizes[0]; + h = sizes[1]; + } + + asset[props.name] = { + len: bidParams.len, + type: props.type, + wmin, + hmin, + w, + h + }; + + return asset; + } + }).filter(Boolean); +} + +/* Turn bid request sizes into ut-compatible format */ +function transformSizes(requestSizes) { + if (!utils.isArray(requestSizes)) { + return []; + } + + if (requestSizes.length === 2 && !utils.isArray(requestSizes[0])) { + return [{ + w: parseInt(requestSizes[0], 10), + h: parseInt(requestSizes[1], 10) + }]; + } else if (utils.isArray(requestSizes[0])) { + return requestSizes.map(item => + ({ + w: parseInt(item[0], 10), + h: parseInt(item[1], 10) + }) + ); + } + + return []; +} diff --git a/modules/outbrainBidAdapter.md b/modules/outbrainBidAdapter.md new file mode 100644 index 00000000000..32df22ddad8 --- /dev/null +++ b/modules/outbrainBidAdapter.md @@ -0,0 +1,111 @@ +# Overview + +``` +Module Name: Outbrain Adapter +Module Type: Bidder Adapter +Maintainer: prog-ops-team@outbrain.com +``` + +# Description + +Module that connects to Outbrain bidder to fetch bids. +Both native and display formats are supported but not at the same time. Using OpenRTB standard. + +# Configuration + +## Bidder and usersync URLs + +The Outbrain adapter does not work without setting the correct bidder and usersync URLs. +You will receive the URLs when contacting us. + +``` +pbjs.setConfig({ + outbrain: { + bidderUrl: 'https://bidder-url.com', + usersyncUrl: 'https://usersync-url.com' + } +}); +``` + + +# Test Native Parameters +``` + var adUnits = [ + code: '/19968336/prebid_native_example_1', + mediaTypes: { + native: { + image: { + required: false, + sizes: [100, 50] + }, + title: { + required: false, + len: 140 + }, + sponsoredBy: { + required: false + }, + clickUrl: { + required: false + }, + body: { + required: false + }, + icon: { + required: false, + sizes: [50, 50] + } + } + }, + bids: [{ + bidder: 'outbrain', + params: { + publisher: { + id: '2706', // required + name: 'Publishers Name', + domain: 'publisher.com' + }, + tagid: 'tag-id', + bcat: ['IAB1-1'], + badv: ['example.com'] + } + }] + ]; + + pbjs.setConfig({ + outbrain: { + bidderUrl: 'https://prebidtest.zemanta.com/api/bidder/prebidtest/bid/' + } + }); +``` + +# Test Display Parameters +``` + var adUnits = [ + code: '/19968336/prebid_display_example_1', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'outbrain', + params: { + publisher: { + id: '2706', // required + name: 'Publishers Name', + domain: 'publisher.com' + }, + tagid: 'tag-id', + bcat: ['IAB1-1'], + badv: ['example.com'] + }, + }] + ]; + + pbjs.setConfig({ + outbrain: { + bidderUrl: 'https://prebidtest.zemanta.com/api/bidder/prebidtest/bid/' + } + }); +``` diff --git a/modules/zemantaBidAdapter.js b/modules/zemantaBidAdapter.js index b6dafcaed77..a787fab7d86 100644 --- a/modules/zemantaBidAdapter.js +++ b/modules/zemantaBidAdapter.js @@ -25,9 +25,6 @@ const NATIVE_PARAMS = { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: [ - { code: 'outbrain', gvlid: GVLID } - ], supportedMediaTypes: [ NATIVE, BANNER ], isBidRequestValid: (bid) => { return ( diff --git a/test/spec/modules/outbrainBidAdapter_spec.js b/test/spec/modules/outbrainBidAdapter_spec.js new file mode 100644 index 00000000000..9671adf826e --- /dev/null +++ b/test/spec/modules/outbrainBidAdapter_spec.js @@ -0,0 +1,558 @@ +import {expect} from 'chai'; +import {spec} from 'modules/outbrainBidAdapter.js'; +import {config} from 'src/config.js'; +import {server} from 'test/mocks/xhr'; + +describe('Outbrain Adapter', function () { + describe('Bid request and response', function () { + const commonBidRequest = { + bidder: 'outbrain', + params: { + publisher: { + id: 'publisher-id' + }, + }, + bidId: '2d6815a92ba1ba', + auctionId: '12043683-3254-4f74-8934-f941b085579e', + } + const nativeBidRequestParams = { + nativeParams: { + image: { + required: true, + sizes: [ + 120, + 100 + ], + sendId: true + }, + title: { + required: true, + sendId: true + }, + sponsoredBy: { + required: false + } + }, + } + + const displayBidRequestParams = { + sizes: [ + [ + 300, + 250 + ] + ] + } + + describe('isBidRequestValid', function () { + before(() => { + config.setConfig({ + outbrain: { + bidderUrl: 'https://bidder-url.com', + } + } + ) + }) + after(() => { + config.resetConfig() + }) + + it('should fail when bid is invalid', function () { + const bid = { + bidder: 'outbrain', + params: { + publisher: { + id: 'publisher-id', + } + }, + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + it('should succeed when bid contains native params', function () { + const bid = { + bidder: 'outbrain', + params: { + publisher: { + id: 'publisher-id', + } + }, + ...nativeBidRequestParams, + } + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + it('should succeed when bid contains sizes', function () { + const bid = { + bidder: 'outbrain', + params: { + publisher: { + id: 'publisher-id', + } + }, + ...displayBidRequestParams, + } + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + it('should fail if publisher id is not set', function () { + const bid = { + bidder: 'outbrain', + ...nativeBidRequestParams, + } + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + it('should succeed with outbrain config', function () { + const bid = { + bidder: 'outbrain', + params: { + publisher: { + id: 'publisher-id', + } + }, + ...nativeBidRequestParams, + } + config.resetConfig() + config.setConfig({ + outbrain: { + bidderUrl: 'https://bidder-url.com', + } + }) + expect(spec.isBidRequestValid(bid)).to.equal(true) + }) + it('should fail if bidder url is not set', function () { + const bid = { + bidder: 'outbrain', + params: { + publisher: { + id: 'publisher-id', + } + }, + ...nativeBidRequestParams, + } + config.resetConfig() + expect(spec.isBidRequestValid(bid)).to.equal(false) + }) + }) + + describe('buildRequests', function () { + before(() => { + config.setConfig({ + outbrain: { + bidderUrl: 'https://bidder-url.com', + } + } + ) + }) + after(() => { + config.resetConfig() + }) + + const commonBidderRequest = { + refererInfo: { + referer: 'https://example.com/' + } + } + + it('should build native request', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + const expectedNativeAssets = { + assets: [ + { + required: 1, + id: 3, + img: { + type: 3, + w: 120, + h: 100 + } + }, + { + required: 1, + id: 0, + title: {} + }, + { + required: 0, + id: 5, + data: { + type: 1 + } + } + ] + } + const expectedData = { + site: { + page: 'https://example.com/', + publisher: { + id: 'publisher-id' + } + }, + device: { + ua: navigator.userAgent + }, + source: { + fd: 1 + }, + cur: [ + 'USD' + ], + imp: [ + { + id: '1', + native: { + request: JSON.stringify(expectedNativeAssets) + } + } + ] + } + const res = spec.buildRequests([bidRequest], commonBidderRequest) + expect(res.url).to.equal('https://bidder-url.com') + expect(res.data).to.deep.equal(JSON.stringify(expectedData)) + }); + + it('should build display request', function () { + const bidRequest = { + ...commonBidRequest, + ...displayBidRequestParams, + } + const expectedData = { + site: { + page: 'https://example.com/', + publisher: { + id: 'publisher-id' + } + }, + device: { + ua: navigator.userAgent + }, + source: { + fd: 1 + }, + cur: [ + 'USD' + ], + imp: [ + { + id: '1', + banner: { + format: [ + { + w: 300, + h: 250 + } + ] + } + } + ] + } + const res = spec.buildRequests([bidRequest], commonBidderRequest) + expect(res.url).to.equal('https://bidder-url.com') + expect(res.data).to.deep.equal(JSON.stringify(expectedData)) + }) + + it('should pass optional parameters in request', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + bidRequest.params.tagid = 'test-tag' + bidRequest.params.publisher.name = 'test-publisher' + bidRequest.params.publisher.domain = 'test-publisher.com' + bidRequest.params.bcat = ['bad-category'] + bidRequest.params.badv = ['bad-advertiser'] + + const res = spec.buildRequests([bidRequest], commonBidderRequest) + const resData = JSON.parse(res.data) + expect(resData.imp[0].tagid).to.equal('test-tag') + expect(resData.site.publisher.name).to.equal('test-publisher') + expect(resData.site.publisher.domain).to.equal('test-publisher.com') + expect(resData.bcat).to.deep.equal(['bad-category']) + expect(resData.badv).to.deep.equal(['bad-advertiser']) + }); + + it('should pass bidder timeout', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + const bidderRequest = { + ...commonBidderRequest, + timeout: 500 + } + const res = spec.buildRequests([bidRequest], bidderRequest) + const resData = JSON.parse(res.data) + expect(resData.tmax).to.equal(500) + }); + + it('should pass GDPR consent', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + const bidderRequest = { + ...commonBidderRequest, + gdprConsent: { + gdprApplies: true, + consentString: 'consentString', + } + } + const res = spec.buildRequests([bidRequest], bidderRequest) + const resData = JSON.parse(res.data) + expect(resData.user.ext.consent).to.equal('consentString') + expect(resData.regs.ext.gdpr).to.equal(1) + }); + + it('should pass us privacy consent', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + const bidderRequest = { + ...commonBidderRequest, + uspConsent: 'consentString' + } + const res = spec.buildRequests([bidRequest], bidderRequest) + const resData = JSON.parse(res.data) + expect(resData.regs.ext.us_privacy).to.equal('consentString') + }); + + it('should pass coppa consent', function () { + const bidRequest = { + ...commonBidRequest, + ...nativeBidRequestParams, + } + config.setConfig({coppa: true}) + + const res = spec.buildRequests([bidRequest], commonBidderRequest) + const resData = JSON.parse(res.data) + expect(resData.regs.coppa).to.equal(1) + + config.resetConfig() + }); + }) + + describe('interpretResponse', function () { + it('should return empty array if no valid bids', function () { + const res = spec.interpretResponse({}, []) + expect(res).to.be.an('array').that.is.empty + }); + + it('should interpret native response', function () { + const serverResponse = { + body: { + id: '0a73e68c-9967-4391-b01b-dda2d9fc54e4', + seatbid: [ + { + bid: [ + { + id: '82822cf5-259c-11eb-8a52-f29e5275aa57', + impid: '1', + price: 1.1, + nurl: 'http://example.com/win/${AUCTION_PRICE}', + adm: '{"ver":"1.2","assets":[{"id":3,"required":1,"img":{"url":"http://example.com/img/url","w":120,"h":100}},{"id":0,"required":1,"title":{"text":"Test title"}},{"id":5,"data":{"value":"Test sponsor"}}],"link":{"url":"http://example.com/click/url"},"eventtrackers":[{"event":1,"method":1,"url":"http://example.com/impression"}]}', + adomain: [ + 'example.com' + ], + cid: '3487171', + crid: '28023739', + cat: [ + 'IAB10-2' + ] + } + ], + seat: 'acc-5537' + } + ], + bidid: '82822cf5-259c-11eb-8a52-b48e7518c657', + cur: 'USD' + }, + } + const request = { + bids: [ + { + ...commonBidRequest, + ...nativeBidRequestParams, + } + ] + } + const expectedRes = [ + { + requestId: request.bids[0].bidId, + cpm: 1.1, + creativeId: '28023739', + ttl: 360, + netRevenue: false, + currency: 'USD', + mediaType: 'native', + nurl: 'http://example.com/win/${AUCTION_PRICE}', + meta: { + 'advertiserDomains': [ + 'example.com' + ] + }, + native: { + clickTrackers: undefined, + clickUrl: 'http://example.com/click/url', + image: { + url: 'http://example.com/img/url', + width: 120, + height: 100 + }, + title: 'Test title', + sponsoredBy: 'Test sponsor', + impressionTrackers: [ + 'http://example.com/impression', + ] + } + } + ] + + const res = spec.interpretResponse(serverResponse, request) + expect(res).to.deep.equal(expectedRes) + }); + + it('should interpret display response', function () { + const serverResponse = { + body: { + id: '6b2eedc8-8ff5-46ef-adcf-e701b508943e', + seatbid: [ + { + bid: [ + { + id: 'd90fe7fa-28d7-11eb-8ce4-462a842a7cf9', + impid: '1', + price: 1.1, + nurl: 'http://example.com/win/${AUCTION_PRICE}', + adm: '
ad
', + adomain: [ + 'example.com' + ], + cid: '3865084', + crid: '29998660', + cat: [ + 'IAB10-2' + ], + w: 300, + h: 250 + } + ], + seat: 'acc-6536' + } + ], + bidid: 'd90fe7fa-28d7-11eb-8ce4-13d94bfa26f9', + cur: 'USD' + } + } + const request = { + bids: [ + { + ...commonBidRequest, + ...displayBidRequestParams + } + ] + } + const expectedRes = [ + { + requestId: request.bids[0].bidId, + cpm: 1.1, + creativeId: '29998660', + ttl: 360, + netRevenue: false, + currency: 'USD', + mediaType: 'banner', + nurl: 'http://example.com/win/${AUCTION_PRICE}', + ad: '
ad
', + width: 300, + height: 250, + meta: { + 'advertiserDomains': [ + 'example.com' + ] + }, + } + ] + + const res = spec.interpretResponse(serverResponse, request) + expect(res).to.deep.equal(expectedRes) + }); + }) + }) + + describe('getUserSyncs', function () { + const usersyncUrl = 'https://usersync-url.com'; + beforeEach(() => { + config.setConfig({ + outbrain: { + usersyncUrl: usersyncUrl, + } + } + ) + }) + after(() => { + config.resetConfig() + }) + + it('should return user sync if pixel enabled', function () { + const ret = spec.getUserSyncs({pixelEnabled: true}) + expect(ret).to.deep.equal([{type: 'image', url: 'https://usersync-url.com'}]) + }) + it('should return user sync if pixel enabled with outbrain config', function () { + config.resetConfig() + config.setConfig({ + outbrain: { + usersyncUrl: 'https://usersync-url.com', + } + }) + const ret = spec.getUserSyncs({pixelEnabled: true}) + expect(ret).to.deep.equal([{type: 'image', url: 'https://usersync-url.com'}]) + }) + + it('should not return user sync if pixel disabled', function () { + const ret = spec.getUserSyncs({pixelEnabled: false}) + expect(ret).to.be.an('array').that.is.empty + }) + + it('should not return user sync if url is not set', function () { + config.resetConfig() + const ret = spec.getUserSyncs({pixelEnabled: true}) + expect(ret).to.be.an('array').that.is.empty + }) + + it('should pass GDPR consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: false, consentString: 'foo'}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}&gdpr=0&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: undefined}, undefined)).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=` + }]); + }); + + it('should pass US consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, undefined, '1NYN')).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}&us_privacy=1NYN` + }]); + }); + + it('should pass GDPR and US consent', function() { + expect(spec.getUserSyncs({ pixelEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, '1NYN')).to.deep.equal([{ + type: 'image', url: `${usersyncUrl}&gdpr=1&gdpr_consent=foo&us_privacy=1NYN` + }]); + }); + }) + + describe('onBidWon', function () { + it('should make an ajax call with the original cpm', function () { + const bid = { + nurl: 'http://example.com/win/${AUCTION_PRICE}', + cpm: 2.1, + originalCpm: 1.1, + } + spec.onBidWon(bid) + expect(server.requests[0].url).to.equals('http://example.com/win/1.1') + }); + }) +}) From 81546c8561ef99331efb75525b41c37e6b8fda6c Mon Sep 17 00:00:00 2001 From: nouchy <33549554+nouchy@users.noreply.github.com> Date: Sat, 10 Apr 2021 15:56:07 +0200 Subject: [PATCH 62/93] Sirdata Real-time Data Module: add new RTD module (#6515) --- .../gpt/sirdataRtdProvider_example.html | 146 +++++++ modules/.submodules.json | 3 +- modules/sirdataRtdProvider.js | 402 ++++++++++++++++++ modules/sirdataRtdProvider.md | 169 ++++++++ test/spec/modules/sirdataRtdProvider_spec.js | 94 ++++ 5 files changed, 813 insertions(+), 1 deletion(-) create mode 100644 integrationExamples/gpt/sirdataRtdProvider_example.html create mode 100644 modules/sirdataRtdProvider.js create mode 100644 modules/sirdataRtdProvider.md create mode 100644 test/spec/modules/sirdataRtdProvider_spec.js diff --git a/integrationExamples/gpt/sirdataRtdProvider_example.html b/integrationExamples/gpt/sirdataRtdProvider_example.html new file mode 100644 index 00000000000..444c9133905 --- /dev/null +++ b/integrationExamples/gpt/sirdataRtdProvider_example.html @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + +
+

+Give consent or make a choice in Europe. Module will add key/value pairs in ad calls. Check out for sd_rtd key in Google Ad call (https://securepubads.g.doubleclick.net/gampad/ads...) and in the payload sent to Xandr to endpoint https://ib.adnxs.com/ut/v3/prebid : tags[0].keywords.key[sd_rtd] should have an array of string as value. This array will mix user segments and/or page categories based on user's choices. +

+
+

Basic Prebid.js Example

+
Div-1
+
+ +
+ + + \ No newline at end of file diff --git a/modules/.submodules.json b/modules/.submodules.json index f71325d38a9..ffa3f9df353 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -36,6 +36,7 @@ "haloRtdProvider", "jwplayerRtdProvider", "reconciliationRtdProvider", - "geoedgeRtdProvider" + "geoedgeRtdProvider", + "sirdataRtdProvider" ] } diff --git a/modules/sirdataRtdProvider.js b/modules/sirdataRtdProvider.js new file mode 100644 index 00000000000..373468b2f14 --- /dev/null +++ b/modules/sirdataRtdProvider.js @@ -0,0 +1,402 @@ +/** + * This module adds Sirdata provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will fetch segments (user-centric) and categories (page-centric) from Sirdata server + * The module will automatically handle user's privacy and choice in California (IAB TL CCPA Framework) and in Europe (IAB EU TCF FOR GDPR) + * @module modules/sirdataRtdProvider + * @requires module:modules/realTimeData + */ +import {getGlobal} from '../src/prebidGlobal.js'; +import * as utils from '../src/utils.js'; +import {submodule} from '../src/hook.js'; +import {ajax} from '../src/ajax.js'; +import findIndex from 'core-js-pure/features/array/find-index.js'; +import { getRefererInfo } from '../src/refererDetection.js'; +import { config } from '../src/config.js'; + +/** @type {string} */ +const MODULE_NAME = 'realTimeData'; +const SUBMODULE_NAME = 'SirdataRTDModule'; + +export function getSegmentsAndCategories(reqBidsConfigObj, onDone, moduleConfig, userConsent) { + const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; + moduleConfig.params = moduleConfig.params || {}; + + var tcString = (userConsent && userConsent.gdpr && userConsent.gdpr.consentString ? userConsent.gdpr.consentString : ''); + var gdprApplies = (userConsent && userConsent.gdpr && userConsent.gdpr.gdprApplies ? userConsent.gdpr.gdprApplies : ''); + + moduleConfig.params.partnerId = moduleConfig.params.partnerId ? moduleConfig.params.partnerId : 1; + moduleConfig.params.key = moduleConfig.params.key ? moduleConfig.params.key : 1; + + var sirdataDomain; + var sendWithCredentials; + + if (userConsent.coppa || (userConsent.usp && (userConsent.usp[0] == '1' && (userConsent.usp[1] == 'N' || userConsent.usp[2] == 'Y')))) { + // if children or "Do not Sell" management in California, no segments, page categories only whatever TCF signal + sirdataDomain = 'cookieless-data.com'; + sendWithCredentials = false; + gdprApplies = null; + tcString = ''; + } else if (getGlobal().getConfig('consentManagement.gdpr')) { + // Default endpoint is cookieless if gdpr management is set. Needed because the cookie-based endpoint will fail and return error if user is located in Europe and no consent has been given + sirdataDomain = 'cookieless-data.com'; + sendWithCredentials = false; + } + + // default global endpoint is cookie-based if no rules falls into cookieless or consent has been given or GDPR doesn't apply + if (!sirdataDomain || !gdprApplies || (utils.deepAccess(userConsent, 'gdpr.vendorData.vendor.consents') && userConsent.gdpr.vendorData.vendor.consents[53] && userConsent.gdpr.vendorData.purpose.consents[1] && userConsent.gdpr.vendorData.purpose.consents[4])) { + sirdataDomain = 'sddan.com'; + sendWithCredentials = true; + } + + var actualUrl = moduleConfig.params.actualUrl || getRefererInfo().referer; + + const url = 'https://kvt.' + sirdataDomain + '/api/v1/public/p/' + moduleConfig.params.partnerId + '/d/' + moduleConfig.params.key + '/s?callback=&gdpr=' + gdprApplies + '&gdpr_consent=' + tcString + (actualUrl ? '&url=' + actualUrl : ''); + ajax(url, { + success: function (response, req) { + if (req.status === 200) { + try { + const data = JSON.parse(response); + if (data && data.segments) { + addSegmentData(adUnits, data, moduleConfig, onDone); + } else { + onDone(); + } + } catch (e) { + onDone(); + utils.logError('unable to parse Sirdata data' + e); + } + } else if (req.status === 204) { + onDone(); + } + }, + error: function () { + onDone(); + utils.logError('unable to get Sirdata data'); + } + }, + null, + { + contentType: 'text/plain', + method: 'GET', + withCredentials: sendWithCredentials, + referrerPolicy: 'unsafe-url', + crossOrigin: true + }); +} + +export function setGlobalOrtb2(segments, categories) { + try { + let addOrtb2 = {}; + let testGlobal = getGlobal().getConfig('ortb2') || {}; + if (!utils.deepAccess(testGlobal, 'user.ext.data.sd_rtd') || !utils.deepEqual(testGlobal.user.ext.data.sd_rtd, segments)) { + utils.deepSetValue(addOrtb2, 'user.ext.data.sd_rtd', segments || {}); + } + if (!utils.deepAccess(testGlobal, 'site.ext.data.sd_rtd') || !utils.deepEqual(testGlobal.site.ext.data.sd_rtd, categories)) { + utils.deepSetValue(addOrtb2, 'site.ext.data.sd_rtd', categories || {}); + } + if (!utils.isEmpty(addOrtb2)) { + let ortb2 = {ortb2: utils.mergeDeep({}, testGlobal, addOrtb2)}; + getGlobal().setConfig(ortb2); + } + } catch (e) { + utils.logError(e) + } + + return true; +} + +export function setBidderOrtb2(bidder, segments, categories) { + try { + let addOrtb2 = {}; + let testBidder = utils.deepAccess(config.getBidderConfig(), bidder + '.ortb2') || {}; + if (!utils.deepAccess(testBidder, 'user.ext.data.sd_rtd') || !utils.deepEqual(testBidder.user.ext.data.sd_rtd, segments)) { + utils.deepSetValue(addOrtb2, 'user.ext.data.sd_rtd', segments || {}); + } + if (!utils.deepAccess(testBidder, 'site.ext.data.sd_rtd') || !utils.deepEqual(testBidder.site.ext.data.sd_rtd, categories)) { + utils.deepSetValue(addOrtb2, 'site.ext.data.sd_rtd', categories || {}); + } + if (!utils.isEmpty(addOrtb2)) { + let ortb2 = {ortb2: utils.mergeDeep({}, testBidder, addOrtb2)}; + getGlobal().setBidderConfig({ bidders: [bidder], config: ortb2 }); + } + } catch (e) { + utils.logError(e) + } + + return true; +} + +export function loadCustomFunction (todo, adUnit, list, data, bid) { + try { + if (typeof todo == 'function') { + todo(adUnit, list, data, bid); + } + } catch (e) { utils.logError(e); } + return true; +} + +export function getSegAndCatsArray(data, minScore) { + var sirdataData = {'segments': [], 'categories': []}; + minScore = minScore && typeof minScore == 'number' ? minScore : 30; + try { + if (data && data.contextual_categories) { + for (let catId in data.contextual_categories) { + let value = data.contextual_categories[catId]; + if (value >= minScore && sirdataData.categories.indexOf(catId) === -1) { + sirdataData.categories.push(catId.toString()); + } + } + } + } catch (e) { utils.logError(e); } + try { + if (data && data.segments) { + for (let segId in data.segments) { + sirdataData.segments.push(data.segments[segId].toString()); + } + } + } catch (e) { utils.logError(e); } + return sirdataData; +} + +export function addSegmentData(adUnits, data, moduleConfig, onDone) { + moduleConfig = moduleConfig || {}; + moduleConfig.params = moduleConfig.params || {}; + const globalMinScore = moduleConfig.params.hasOwnProperty('contextualMinRelevancyScore') ? moduleConfig.params.contextualMinRelevancyScore : 30; + var sirdataData = getSegAndCatsArray(data, globalMinScore); + + if (!sirdataData || (sirdataData.segments.length < 1 && sirdataData.categories.length < 1)) { utils.logError('no cats'); onDone(); return adUnits; } + + const sirdataList = sirdataData.segments.concat(sirdataData.categories); + + var curationData = {'segments': [], 'categories': []}; + var curationId = '1'; + const biddersParamsExist = (!!(moduleConfig.params && moduleConfig.params.bidders)); + + // Global ortb2 + if (!biddersParamsExist) { + setGlobalOrtb2(sirdataData.segments, sirdataData.categories); + } + + // Google targeting + if (typeof window.googletag !== 'undefined' && (moduleConfig.params.setGptKeyValues || !moduleConfig.params.hasOwnProperty('setGptKeyValues'))) { + try { + // For curation Google is pid 27449 + curationId = (moduleConfig.params.gptCurationId ? moduleConfig.params.gptCurationId : '27449'); + if (data.shared_taxonomy && data.shared_taxonomy[curationId]) { + curationData = getSegAndCatsArray(data.shared_taxonomy[curationId], globalMinScore); + } + window.googletag.pubads().getSlots().forEach(function(n) { + if (typeof n.setTargeting !== 'undefined') { + n.setTargeting('sd_rtd', sirdataList.concat(curationData.segments).concat(curationData.categories)); + } + }) + } catch (e) { utils.logError(e); } + } + + // Bid targeting level for FPD non-generic biders + var bidderIndex = ''; + var indexFound = false; + + adUnits.forEach(adUnit => { + if (!biddersParamsExist && !utils.deepAccess(adUnit, 'ortb2Imp.ext.data.sd_rtd')) { + utils.deepSetValue(adUnit, 'ortb2Imp.ext.data.sd_rtd', sirdataList); + } + + adUnit.hasOwnProperty('bids') && adUnit.bids.forEach(bid => { + bidderIndex = (moduleConfig.params.hasOwnProperty('bidders') ? findIndex(moduleConfig.params.bidders, function(i) { return i.bidder === bid.bidder; }) : false); + indexFound = (!!(typeof bidderIndex == 'number' && bidderIndex >= 0)); + try { + curationData = {'segments': [], 'categories': []}; + let minScore = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('contextualMinRelevancyScore') ? moduleConfig.params.bidders[bidderIndex].contextualMinRelevancyScore : globalMinScore) + + if (!biddersParamsExist || (indexFound && (!moduleConfig.params.bidders[bidderIndex].hasOwnProperty('adUnitCodes') || moduleConfig.params.bidders[bidderIndex].adUnitCodes.indexOf(adUnit.code) !== -1))) { + switch (bid.bidder) { + case 'appnexus': + case 'appnexusAst': + case 'brealtime': + case 'emxdigital': + case 'pagescience': + case 'gourmetads': + case 'matomy': + case 'featureforward': + case 'oftmedia': + case 'districtm': + case 'adasta': + case 'beintoo': + case 'gravity': + case 'msq_classic': + case 'msq_max': + case '366_apx': + // For curation Xandr is pid 27446 + curationId = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('curationId') ? moduleConfig.params.bidders[bidderIndex].curationId : '27446'); + if (data.shared_taxonomy && data.shared_taxonomy[curationId]) { + curationData = getSegAndCatsArray(data.shared_taxonomy[curationId], minScore); + } + if (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('customFunction')) { + loadCustomFunction(moduleConfig.params.bidders[bidderIndex].customFunction, adUnit, sirdataList.concat(curationData.segments).concat(curationData.categories), data, bid); + } else { + utils.deepSetValue(bid, 'params.keywords.sd_rtd', sirdataList.concat(curationData.segments).concat(curationData.categories)); + } + break; + + case 'smartadserver': + case 'smart': + var target = []; + if (bid.hasOwnProperty('params') && bid.params.hasOwnProperty('target')) { + target.push(bid.params.target); + } + // For curation Smart is pid 27440 + curationId = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('curationId') ? moduleConfig.params.bidders[bidderIndex].curationId : '27440'); + if (data.shared_taxonomy && data.shared_taxonomy[curationId]) { + curationData = getSegAndCatsArray(data.shared_taxonomy[curationId], minScore); + } + if (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('customFunction')) { + loadCustomFunction(moduleConfig.params.bidders[bidderIndex].customFunction, adUnit, sirdataList.concat(curationData.segments).concat(curationData.categories), data, bid); + } else { + sirdataList.concat(curationData.segments).concat(curationData.categories).forEach(function(entry) { + if (target.indexOf('sd_rtd=' + entry) === -1) { + target.push('sd_rtd=' + entry); + } + }); + utils.deepSetValue(bid, 'params.target', target.join(';')); + } + break; + + case 'rubicon': + // For curation Magnite is pid 27518 + curationId = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('curationId') ? moduleConfig.params.bidders[bidderIndex].curationId : '27452'); + if (data.shared_taxonomy && data.shared_taxonomy[curationId]) { + curationData = getSegAndCatsArray(data.shared_taxonomy[curationId], minScore); + } + if (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('customFunction')) { + loadCustomFunction(moduleConfig.params.bidders[bidderIndex].customFunction, adUnit, sirdataList.concat(curationData.segments).concat(curationData.categories), data, bid); + } else { + setBidderOrtb2(bid.bidder, data.segments.concat(curationData.segments), sirdataList.concat(curationData.segments).concat(curationData.categories)); + } + break; + + case 'ix': + var ixConfig = getGlobal().getConfig('ix.firstPartyData.sd_rtd'); + if (!ixConfig) { + // For curation index is pid 27248 + curationId = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('curationId') ? moduleConfig.params.bidders[bidderIndex].curationId : '27248'); + if (data.shared_taxonomy && data.shared_taxonomy[curationId]) { + curationData = getSegAndCatsArray(data.shared_taxonomy[curationId], minScore); + } + if (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('customFunction')) { + loadCustomFunction(moduleConfig.params.bidders[bidderIndex].customFunction, adUnit, sirdataList.concat(curationData.segments).concat(curationData.categories), data, bid); + } else { + var cappIxCategories = []; + var ixLength = 0; + var ixLimit = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('sizeLimit') ? moduleConfig.params.bidders[bidderIndex].sizeLimit : 1000); + // Push ids For publisher use and for curation if exists but limit size because the bidder uses GET parameters + sirdataList.concat(curationData.segments).concat(curationData.categories).forEach(function(entry) { + if (ixLength < ixLimit) { + cappIxCategories.push(entry); + ixLength += entry.toString().length; + } + }); + getGlobal().setConfig({ix: {firstPartyData: {sd_rtd: cappIxCategories}}}); + } + } + break; + + case 'proxistore': + // For curation Proxistore is pid 27484 + curationId = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('curationId') ? moduleConfig.params.bidders[bidderIndex].curationId : '27484'); + if (data.shared_taxonomy && data.shared_taxonomy[curationId]) { + curationData = getSegAndCatsArray(data.shared_taxonomy[curationId], minScore); + } else { + data.shared_taxonomy[curationId] = {contextual_categories: {}}; + } + if (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('customFunction')) { + loadCustomFunction(moduleConfig.params.bidders[bidderIndex].customFunction, adUnit, sirdataList.concat(curationData.segments).concat(curationData.categories), data, bid); + } else { + utils.deepSetValue(bid, 'ortb2.user.ext.data', {segments: sirdataData.segments.concat(curationData.segments), contextual_categories: {...data.contextual_categories, ...data.shared_taxonomy[curationId].contextual_categories}}); + } + break; + + case 'criteo': + // For curation Smart is pid 27443 + curationId = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('curationId') ? moduleConfig.params.bidders[bidderIndex].curationId : '27443'); + if (data.shared_taxonomy && data.shared_taxonomy[curationId]) { + curationData = getSegAndCatsArray(data.shared_taxonomy[curationId], minScore); + } + if (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('customFunction')) { + loadCustomFunction(moduleConfig.params.bidders[bidderIndex].customFunction, adUnit, sirdataList.concat(curationData.segments).concat(curationData.categories), data, bid); + } else { + setBidderOrtb2(bid.bidder, sirdataList.concat(curationData.segments).concat(curationData.categories), sirdataList.concat(curationData.segments).concat(curationData.categories)); + } + break; + + case 'triplelift': + // For curation Triplelift is pid 27518 + curationId = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('curationId') ? moduleConfig.params.bidders[bidderIndex].curationId : '27518'); + if (data.shared_taxonomy && data.shared_taxonomy[curationId]) { + curationData = getSegAndCatsArray(data.shared_taxonomy[curationId], minScore); + } + if (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('customFunction')) { + loadCustomFunction(moduleConfig.params.bidders[bidderIndex].customFunction, adUnit, sirdataList.concat(curationData.segments).concat(curationData.categories), data, bid); + } else { + setBidderOrtb2(bid.bidder, data.segments.concat(curationData.segments), sirdataList.concat(curationData.segments).concat(curationData.categories)); + } + break; + + case 'avct': + case 'avocet': + // For curation Avocet is pid 27522 + curationId = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('curationId') ? moduleConfig.params.bidders[bidderIndex].curationId : '27522'); + if (data.shared_taxonomy && data.shared_taxonomy[curationId]) { + curationData = getSegAndCatsArray(data.shared_taxonomy[curationId], minScore); + } + if (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('customFunction')) { + loadCustomFunction(moduleConfig.params.bidders[bidderIndex].customFunction, adUnit, sirdataList.concat(curationData.segments).concat(curationData.categories), data, bid); + } else { + setBidderOrtb2(bid.bidder, data.segments.concat(curationData.segments), sirdataList.concat(curationData.segments).concat(curationData.categories)); + } + break; + + case 'smaato': + // For curation Smaato is pid 27520 + curationId = (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('curationId') ? moduleConfig.params.bidders[bidderIndex].curationId : '27520'); + if (data.shared_taxonomy && data.shared_taxonomy[curationId]) { + curationData = getSegAndCatsArray(data.shared_taxonomy[curationId], minScore); + } + if (indexFound && moduleConfig.params.bidders[bidderIndex].hasOwnProperty('customFunction')) { + loadCustomFunction(moduleConfig.params.bidders[bidderIndex].customFunction, adUnit, sirdataList.concat(curationData.segments).concat(curationData.categories), data, bid); + } else { + setBidderOrtb2(bid.bidder, data.segments.concat(curationData.segments), sirdataList.concat(curationData.segments).concat(curationData.categories)); + } + break; + + default: + if (!biddersParamsExist || indexFound) { + if (!utils.deepAccess(bid, 'ortb2.site.ext.data.sd_rtd')) { + utils.deepSetValue(bid, 'ortb2.site.ext.data.sd_rtd', sirdataData.categories); + } + if (!utils.deepAccess(bid, 'ortb2.user.ext.data.sd_rtd')) { + utils.deepSetValue(bid, 'ortb2.user.ext.data.sd_rtd', sirdataData.segments); + } + } + } + } + } catch (e) { utils.logError(e) } + }) + }); + + onDone(); + return adUnits; +} + +export function init(config) { + return true; +} + +export const sirdataSubmodule = { + name: SUBMODULE_NAME, + init: init, + getBidRequestData: getSegmentsAndCategories +}; + +submodule(MODULE_NAME, sirdataSubmodule); diff --git a/modules/sirdataRtdProvider.md b/modules/sirdataRtdProvider.md new file mode 100644 index 00000000000..f67e34db43a --- /dev/null +++ b/modules/sirdataRtdProvider.md @@ -0,0 +1,169 @@ +# Sirdata Real-Time Data Submodule + +Module Name: Sirdata Rtd Provider +Module Type: Rtd Provider +Maintainer: bob@sirdata.com + +# Description + +Sirdata provides a disruptive API that allows its partners to leverage its +cutting-edge contextualization technology and its audience segments based on +cookies and consent or without cookies nor consent! + +User-based segments and page-level automatic contextual categories will be +attached to bid request objects sent to different SSPs in order to optimize +targeting. + +Automatic integration with Google Ad Manager and major bidders like Xandr/Appnexus, +Smartadserver, Index Exchange, Proxistore, Magnite/Rubicon or Triplelift ! + +User's country and choice management are included in the module, so it's 100% +compliant with local and regional laws like GDPR and CCPA/CPRA. + +ORTB2 compliant and FPD support for Prebid versions < 4.29 + +Contact bob@sirdata.com for information. + +### Publisher Usage + +Compile the Sirdata RTD module into your Prebid build: + +`gulp build --modules=rtdModule,sirdataRtdProvider` + +Add the Sirdata RTD provider to your Prebid config. + +Segments ids (user-centric) and category ids (page-centric) will be provided +salted and hashed : you can use them with a dedicated and private matching table. +Should you want to allow a SSP or a partner to curate your media and operate +cross-publishers campaigns with our data, please ask Sirdata (bob@sirdata.com) to +open it for you account. + +``` +pbjs.setConfig( + ... + realTimeData: { + auctionDelay: 1000, + dataProviders: [ + { + name: "SirdataRTDModule", + waitForIt: true, + params: { + partnerId: 1, + key: 1, + setGptKeyValues: true, + contextualMinRelevancyScore: 50, //Min score to filter contextual category globally (0-100 scale) + actualUrl: actual_url, //top location url, for contextual categories + bidders: [{ + bidder: 'appnexus', + adUnitCodes: ['adUnit-1','adUnit-2'], + customFunction: overrideAppnexus, + curationId: '111', + },{ + bidder: 'ix', + sizeLimit: 1200 //specific to Index Exchange, + contextualMinRelevancyScore: 50, //Min score to filter contextual category for curation in the bidder (0-100 scale) + }] + } + } + ] + } + ... +} +``` + +### Parameter Descriptions for the Sirdata Configuration Section + +| Name |Type | Description | Notes | +| :------------ | :------------ | :------------ |:------------ | +| name | String | Real time data module name | Mandatory. Always 'SirdataRTDModule' | +| waitForIt | Boolean | Mandatory. Required to ensure that the auction is delayed until prefetch is complete | Optional. Defaults to false but recommended to true | +| params | Object | | Optional | +| params.partnerId | Integer | Partner ID, required to get results and provided by Sirdata. Use 1 for tests and get one running at bob@sirdata.com | Mandatory. Defaults 1. | +| params.key | Integer | Key linked to Partner ID, required to get results and provided by Sirdata. Use 1 for tests and get one running at bob@sirdata.com | Mandatory. Defaults 1. | +| params.setGptKeyValues | Boolean | This parameter Sirdata to set Targeting for GPT/GAM | Optional. Defaults to true. | +| params.contextualMinRelevancyScore | Integer | Min score to keep filter category in the bidders (0-100 scale). Optional. Defaults to 30. | +| params.bidders | Object | Dictionary of bidders you would like to supply Sirdata data for. | Optional. In case no bidder is specified Sirdata will atend to ad data custom and ortb2 to all bidders, adUnits & Globalconfig | + +Bidders can receive common setting : +| Name |Type | Description | Notes | +| :------------ | :------------ | :------------ |:------------ | +| bidder | String | Bidder name | Mandatory if params.bidders are specified | +| adUnitCodes | Array of String | Use if you want to limit data injection to specified adUnits for the bidder | Optional. Default is false and data shared with the bidder isn't filtered | +| customFunction | Function | Use it to override the way data is shared with a bidder | Optional. Default is false | +| curationId | String | Specify the curation ID of the bidder. Provided by Sirdata, request it at bob@sirdata.com | Optional. Default curation ids are specified for main bidders | +| contextualMinRelevancyScore | Integer | Min score to filter contextual categories for curation in the bidder (0-100 scale). Optional. Defaults to 30 or global params.contextualMinRelevancyScore if exits. | +| sizeLimit | Integer | used only for bidder 'ix' to limit the size of the get parameter in Index Exchange ad call | Optional. Default is 1000 | + + +### Overriding data sharing function +As indicated above, it is possible to provide your own bid augmentation +functions. This is useful if you know a bid adapter's API supports segment +fields which aren't specifically being added to request objects in the Prebid +bid adapter. + +Please see the following example, which provides a function to modify bids for +a bid adapter called ix and overrides the appnexus. + +data Object format for usage in this kind of function : +{ + "segments":[111111,222222], + "contextual_categories":{"333333":100}, + "shared_taxonomy":{ + "27446":{ //CurationId + "segments":[444444,555555], + "contextual_categories":{"666666":100} + } + } +} + +``` +function overrideAppnexus (adUnit, segmentsArray, dataObject, bid) { + for (var i = 0; i < segmentsArray.length; i++) { + if (segmentsArray[i]) { + bid.params.user.segments.push(segmentsArray[i]); + } + } +} + +pbjs.setConfig( + ... + realTimeData: { + auctionDelay: 1000, + dataProviders: [ + { + name: "SirdataRTDModule", + waitForIt: true, + params: { + partnerId: 1, + key: 1, + setGptKeyValues: true, + contextualMinRelevancyScore: 50, //Min score to keep contextual category in the bidders (0-100 scale) + actualUrl: actual_url, //top location url, for contextual categories + bidders: [{ + bidder: 'appnexus', + customFunction: overrideAppnexus, + curationId: '111' + },{ + bidder: 'ix', + sizeLimit: 1200, //specific to Index Exchange + customFunction: function(adUnit, segmentsArray, dataObject, bid) { + bid.params.contextual.push(dataObject.contextual_categories); + }, + }] + } + } + ] + } + ... +} +``` + +### Testing + +To view an example of available segments returned by Sirdata's backends: + +`gulp serve --modules=rtdModule,sirdataRtdProvider,appnexusBidAdapter` + +and then point your browser at: + +`http://localhost:9999/integrationExamples/gpt/sirdataRtdProvider_example.html` \ No newline at end of file diff --git a/test/spec/modules/sirdataRtdProvider_spec.js b/test/spec/modules/sirdataRtdProvider_spec.js new file mode 100644 index 00000000000..a16359c50cb --- /dev/null +++ b/test/spec/modules/sirdataRtdProvider_spec.js @@ -0,0 +1,94 @@ +import { addSegmentData, getSegmentsAndCategories, sirdataSubmodule } from 'modules/sirdataRtdProvider.js'; +import { server } from 'test/mocks/xhr.js'; + +const responseHeader = {'Content-Type': 'application/json'}; + +describe('sirdataRtdProvider', function() { + describe('sirdataSubmodule', function() { + it('successfully instantiates', function () { + expect(sirdataSubmodule.init()).to.equal(true); + }); + }); + + describe('Add Segment Data', function() { + it('adds segment data', function() { + const config = { + params: { + setGptKeyValues: false, + contextualMinRelevancyScore: 50, + bidders: [{ + bidder: 'appnexus' + }, { + bidder: 'other' + }] + } + }; + + let adUnits = [ + { + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13144370 + } + }, { + bidder: 'other' + }] + } + ]; + + let data = { + segments: [111111, 222222], + contextual_categories: {'333333': 100} + }; + + addSegmentData(adUnits, data, config, () => {}); + expect(adUnits[0].bids[0].params.keywords).to.have.deep.property('sd_rtd', ['111111', '222222', '333333']); + expect(adUnits[0].bids[1].ortb2.site.ext.data).to.have.deep.property('sd_rtd', ['333333']); + expect(adUnits[0].bids[1].ortb2.user.ext.data).to.have.deep.property('sd_rtd', ['111111', '222222']); + }); + }); + + describe('Get Segments And Categories', function() { + it('gets data from async request and adds segment data', function() { + const config = { + params: { + setGptKeyValues: false, + contextualMinRelevancyScore: 50, + bidders: [{ + bidder: 'appnexus' + }, { + bidder: 'other' + }] + } + }; + + let reqBidsConfigObj = { + adUnits: [{ + bids: [{ + bidder: 'appnexus', + params: { + placementId: 13144370 + } + }, { + bidder: 'other' + }] + }] + }; + + let data = { + segments: [111111, 222222], + contextual_categories: {'333333': 100} + }; + + getSegmentsAndCategories(reqBidsConfigObj, () => {}, config, {}); + + let request = server.requests[0]; + request.respond(200, responseHeader, JSON.stringify(data)); + + expect(reqBidsConfigObj.adUnits[0].bids[0].params.keywords).to.have.deep.property('sd_rtd', ['111111', '222222', '333333']); + expect(reqBidsConfigObj.adUnits[0].bids[1].ortb2.site.ext.data).to.have.deep.property('sd_rtd', ['333333']); + expect(reqBidsConfigObj.adUnits[0].bids[1].ortb2.user.ext.data).to.have.deep.property('sd_rtd', ['111111', '222222']); + }); + }); +}); From 67de22f9de18edc3e70529aaaa026f6e67cb9d95 Mon Sep 17 00:00:00 2001 From: logicad Date: Mon, 12 Apr 2021 11:08:52 +0900 Subject: [PATCH 63/93] Logicad Bid Adapter: add support for userid modules (#6529) --- modules/logicadBidAdapter.js | 1 + test/spec/modules/logicadBidAdapter_spec.js | 41 ++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/modules/logicadBidAdapter.js b/modules/logicadBidAdapter.js index 0bf6b886dee..2c919f9c157 100644 --- a/modules/logicadBidAdapter.js +++ b/modules/logicadBidAdapter.js @@ -62,6 +62,7 @@ function newBidRequest(bid, bidderRequest) { prebidJsVersion: '$prebid.version$', referrer: bidderRequest.refererInfo.referer, auctionStartTime: bidderRequest.auctionStart, + eids: bid.userIdAsEids, }; } diff --git a/test/spec/modules/logicadBidAdapter_spec.js b/test/spec/modules/logicadBidAdapter_spec.js index c4c06630a2b..6eb8dc1c043 100644 --- a/test/spec/modules/logicadBidAdapter_spec.js +++ b/test/spec/modules/logicadBidAdapter_spec.js @@ -19,7 +19,23 @@ describe('LogicadAdapter', function () { banner: { sizes: [[300, 250], [300, 600]] } - } + }, + userId: { + sharedid: { + id: 'fakesharedid', + third: 'fakesharedid' + } + }, + userIdAsEids: [{ + source: 'sharedid.org', + uids: [{ + id: 'fakesharedid', + atype: 1, + ext: { + third: 'fakesharedid' + } + }] + }] }]; const nativeBidRequests = [{ bidder: 'logicad', @@ -45,7 +61,23 @@ describe('LogicadAdapter', function () { required: true } } - } + }, + userId: { + sharedid: { + id: 'fakesharedid', + third: 'fakesharedid' + } + }, + userIdAsEids: [{ + source: 'sharedid.org', + uids: [{ + id: 'fakesharedid', + atype: 1, + ext: { + third: 'fakesharedid' + } + }] + }] }]; const bidderRequest = { refererInfo: { @@ -141,6 +173,11 @@ describe('LogicadAdapter', function () { expect(request.method).to.equal('POST'); expect(request.url).to.equal('https://pb.ladsp.com/adrequest/prebid'); expect(request.data).to.exist; + + const data = JSON.parse(request.data); + expect(data.auctionId).to.equal('18fd8b8b0bd757'); + expect(data.eids[0].source).to.equal('sharedid.org'); + expect(data.eids[0].uids[0].id).to.equal('fakesharedid'); }); }); From 4297d4596ef3c1b8d71e1730f9a637e11b44d65d Mon Sep 17 00:00:00 2001 From: mamatic <52153441+mamatic@users.noreply.github.com> Date: Mon, 12 Apr 2021 10:18:11 +0200 Subject: [PATCH 64/93] ATS-identityLinkIdSystem - add use3P config property to control firing of 3P envelope endpoint (#6568) --- .../gpt/idImportLibrary_example.html | 3 +- integrationExamples/gpt/userId_example.html | 3 +- modules/identityLinkIdSystem.js | 8 ++--- modules/userId/index.js | 1 + modules/userId/userId.md | 6 ++-- .../spec/modules/identityLinkIdSystem_spec.js | 29 +++++++++++++++++-- 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/integrationExamples/gpt/idImportLibrary_example.html b/integrationExamples/gpt/idImportLibrary_example.html index da1581d1c89..b0088131858 100644 --- a/integrationExamples/gpt/idImportLibrary_example.html +++ b/integrationExamples/gpt/idImportLibrary_example.html @@ -45,7 +45,8 @@ }, { name: 'identityLink', params: { - pid: '14' // Set your real identityLink placement ID here + pid: '14', // Set your real identityLink placement ID here + // use3P: false // true/false - If you want to use 3P endpoint to retrieve envelope. If you do not set this property to true, 3p endpoint will not be fired. By default this property is undefined and 3p request will not be fired. }, storage: { type: 'html5', diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 973c295325a..9d170254084 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -229,7 +229,8 @@ }, { name: 'identityLink', params: { - pid: '14' // Set your real identityLink placement ID here + pid: '14', // Set your real identityLink placement ID here + // use3P: false // true/false - If you want to use 3P endpoint to retrieve envelope. If you do not set this property to true, 3p endpoint will not be fired. By default this property is undefined and 3p request will not be fired. }, storage: { type: 'cookie', diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index 716929db70a..7c04118e1fc 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -67,11 +67,11 @@ export const identityLinkSubmodule = { setEnvelopeSource(true); callback(JSON.parse(envelope).envelope); } else { - getEnvelope(url, callback); + getEnvelope(url, callback, configParams); } }); } else { - getEnvelope(url, callback); + getEnvelope(url, callback, configParams); } }; @@ -79,7 +79,7 @@ export const identityLinkSubmodule = { } }; // return envelope from third party endpoint -function getEnvelope(url, callback) { +function getEnvelope(url, callback, configParams) { const callbacks = { success: response => { let responseObj; @@ -98,7 +98,7 @@ function getEnvelope(url, callback) { } }; - if (!storage.getCookie('_lr_retry_request')) { + if (configParams.use3P && !storage.getCookie('_lr_retry_request')) { setRetryCookie(); utils.logInfo('identityLink: A 3P retrieval is attempted!'); setEnvelopeSource(false); diff --git a/modules/userId/index.js b/modules/userId/index.js index be9883dae9c..0dce727ca53 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -101,6 +101,7 @@ * @property {(LiveIntentCollectConfig|undefined)} liCollectConfig - the config for LiveIntent's collect requests * @property {(string|undefined)} pd - publisher provided data for reconciling ID5 IDs * @property {(string|undefined)} emailHash - if provided, the hashed email address of a user + * @property {(string|undefined)} use3P - use to retrieve envelope from 3p endpoint */ /** diff --git a/modules/userId/userId.md b/modules/userId/userId.md index aa72146c47c..d9379ea9c85 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -43,7 +43,8 @@ pbjs.setConfig({ }, { name: 'identityLink', params: { - pid: '999' // Set your real identityLink placement ID here + pid: '999', // Set your real identityLink placement ID here + // use3P: false // true/false - If you want to use 3P endpoint to retrieve envelope. If you do not set this property to true, 3p endpoint will not be fired. By default this propertt is undefined and 3p request will not be fired. }, storage: { type: 'cookie', @@ -144,7 +145,8 @@ pbjs.setConfig({ }, { name: 'identityLink', params: { - pid: '999' // Set your real identityLink placement ID here + pid: '999', // Set your real identityLink placement ID here + // use3P: false // true/false - If you want to use 3P endpoint to retrieve envelope. If you do not set this property to true, 3p endpoint will not be fired. By default this property is undefined and 3p request will not be fired. }, storage: { type: 'html5', diff --git a/test/spec/modules/identityLinkIdSystem_spec.js b/test/spec/modules/identityLinkIdSystem_spec.js index 3c544fa8d08..ac79b2e3916 100644 --- a/test/spec/modules/identityLinkIdSystem_spec.js +++ b/test/spec/modules/identityLinkIdSystem_spec.js @@ -6,19 +6,21 @@ import {getStorageManager} from '../../../src/storageManager.js'; export const storage = getStorageManager(); const pid = '14'; -const defaultConfigParams = { params: {pid: pid} }; +let defaultConfigParams; const responseHeader = {'Content-Type': 'application/json'} describe('IdentityLinkId tests', function () { let logErrorStub; beforeEach(function () { + defaultConfigParams = { params: {pid: pid} }; logErrorStub = sinon.stub(utils, 'logError'); // remove _lr_retry_request cookie before test storage.setCookie('_lr_retry_request', 'true', 'Thu, 01 Jan 1970 00:00:01 GMT'); }); afterEach(function () { + defaultConfigParams = {}; logErrorStub.restore(); }); @@ -34,6 +36,7 @@ describe('IdentityLinkId tests', function () { it('should call the LiveRamp envelope endpoint', function () { let callBackSpy = sinon.spy(); + defaultConfigParams.params.use3P = true; let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); let request = server.requests[0]; @@ -62,6 +65,7 @@ describe('IdentityLinkId tests', function () { }); it('should call the LiveRamp envelope endpoint with IAB consent string v1', function () { + defaultConfigParams.params.use3P = true; let callBackSpy = sinon.spy(); let consentData = { gdprApplies: true, @@ -80,6 +84,7 @@ describe('IdentityLinkId tests', function () { }); it('should call the LiveRamp envelope endpoint with IAB consent string v2', function () { + defaultConfigParams.params.use3P = true; let callBackSpy = sinon.spy(); let consentData = { gdprApplies: true, @@ -101,6 +106,7 @@ describe('IdentityLinkId tests', function () { }); it('should not throw Uncaught TypeError when envelope endpoint returns empty response', function () { + defaultConfigParams.params.use3P = true; let callBackSpy = sinon.spy(); let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); @@ -117,6 +123,7 @@ describe('IdentityLinkId tests', function () { }); it('should log an error and continue to callback if ajax request errors', function () { + defaultConfigParams.params.use3P = true; let callBackSpy = sinon.spy(); let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); @@ -141,7 +148,8 @@ describe('IdentityLinkId tests', function () { expect(request).to.be.eq(undefined); }); - it('should call the LiveRamp envelope endpoint if cookie _lr_retry_request does not exist', function () { + it('should call the LiveRamp envelope endpoint if cookie _lr_retry_request does not exist and use3P config property was set to true', function () { + defaultConfigParams.params.use3P = true; let callBackSpy = sinon.spy(); let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams).callback; submoduleCallback(callBackSpy); @@ -154,4 +162,21 @@ describe('IdentityLinkId tests', function () { ); expect(callBackSpy.calledOnce).to.be.true; }); + + it('should not call the LiveRamp envelope endpoint if config property use3P is set to false', function () { + defaultConfigParams.params.use3P = false; + let callBackSpy = sinon.spy(); + let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request).to.be.eq(undefined); + }); + + it('should not call the LiveRamp envelope endpoint if config property use3P is undefined', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = identityLinkSubmodule.getId(defaultConfigParams).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request).to.be.eq(undefined); + }); }); From 8ce416a7c0c975e9a5248cbe436993352f2253eb Mon Sep 17 00:00:00 2001 From: vincentproxistore <56686565+vincentproxistore@users.noreply.github.com> Date: Mon, 12 Apr 2021 11:37:54 +0200 Subject: [PATCH 65/93] Proxistore Bid Adapter: add support for tcf v2 consent (#6543) * use tcf v2 consent * set cosentGiven to false and test Gdpr api v2 --- modules/proxistoreBidAdapter.js | 32 ++++----- .../spec/modules/proxistoreBidAdapter_spec.js | 72 +++++++++++++------ 2 files changed, 65 insertions(+), 39 deletions(-) diff --git a/modules/proxistoreBidAdapter.js b/modules/proxistoreBidAdapter.js index 0b553ce995b..9b1675722be 100644 --- a/modules/proxistoreBidAdapter.js +++ b/modules/proxistoreBidAdapter.js @@ -1,5 +1,4 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; - const BIDDER_CODE = 'proxistore'; const PROXISTORE_VENDOR_ID = 418; @@ -27,38 +26,35 @@ function _createServerRequest(bidRequests, bidderRequest) { language: bidRequests[0].params.language, gdpr: { applies: false, + consentGiven: false }, }; if (bidderRequest && bidderRequest.gdprConsent) { - if ( - typeof bidderRequest.gdprConsent.gdprApplies === 'boolean' && - bidderRequest.gdprConsent.gdprApplies - ) { + const { gdprConsent } = bidderRequest; + if (typeof gdprConsent.gdprApplies === 'boolean' && gdprConsent.gdprApplies) { payload.gdpr.applies = true; } - if ( - typeof bidderRequest.gdprConsent.consentString === 'string' && - bidderRequest.gdprConsent.consentString - ) { + if (typeof gdprConsent.consentString === 'string' && gdprConsent.consentString) { payload.gdpr.consentString = bidderRequest.gdprConsent.consentString; } - - if ( - bidderRequest.gdprConsent.vendorData && - bidderRequest.gdprConsent.vendorData.vendorConsents && - typeof bidderRequest.gdprConsent.vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)] !== 'undefined' - ) { - payload.gdpr.consentGiven = !!bidderRequest.gdprConsent.vendorData - .vendorConsents[PROXISTORE_VENDOR_ID.toString(10)]; + if (gdprConsent.vendorData) { + const {vendorData} = gdprConsent; + const {apiVersion} = gdprConsent; + if (apiVersion === 2 && vendorData.vendor && vendorData.vendor.consents && typeof vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)] !== 'undefined') { + payload.gdpr.consentGiven = !!vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)]; + } else if (apiVersion === 1 && vendorData.vendorConsents && typeof vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)] !== 'undefined') { + payload.gdpr.consentGiven = !!vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)]; + } } } const options = { contentType: 'application/json', - withCredentials: !!payload.gdpr.consentGiven, + withCredentials: payload.gdpr.consentGiven, }; + const endPointUri = payload.gdpr.consentGiven || !payload.gdpr.applies ? `https://abs.proxistore.com/${payload.language}/v3/rtb/prebid/multi` : `https://abs.proxistore.com/${payload.language}/v3/rtb/prebid/multi/cookieless`; diff --git a/test/spec/modules/proxistoreBidAdapter_spec.js b/test/spec/modules/proxistoreBidAdapter_spec.js index f98d9633320..e7db01828e0 100644 --- a/test/spec/modules/proxistoreBidAdapter_spec.js +++ b/test/spec/modules/proxistoreBidAdapter_spec.js @@ -2,16 +2,20 @@ import { expect } from 'chai'; let { spec } = require('modules/proxistoreBidAdapter'); const BIDDER_CODE = 'proxistore'; describe('ProxistoreBidAdapter', function () { + const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; const bidderRequest = { bidderCode: BIDDER_CODE, auctionId: '1025ba77-5463-4877-b0eb-14b205cb9304', bidderRequestId: '10edf38ec1a719', gdprConsent: { + apiVersion: 2, gdprApplies: true, - consentString: 'CONSENT_STRING', + consentString: consentString, vendorData: { - vendorConsents: { - 418: true, + vendor: { + consents: { + 418: true, + }, }, }, }, @@ -75,6 +79,29 @@ describe('ProxistoreBidAdapter', function () { expect(request.url).equal(url.cookieBase); // doens't have gpdr consent bidderRequest.gdprConsent.vendorData = null; + + request = spec.buildRequests([bid], bidderRequest); + expect(request.url).equal(url.cookieLess); + + // api v2 + bidderRequest.gdprConsent = { + gdprApplies: true, + allowAuctionWithoutConsent: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { + '418': true + } + }, + }, + apiVersion: 2 + }; + // has gdpr consent + request = spec.buildRequests([bid], bidderRequest); + expect(request.url).equal(url.cookieBase); + + bidderRequest.gdprConsent.vendorData.vendor = {}; request = spec.buildRequests([bid], bidderRequest); expect(request.url).equal(url.cookieLess); }); @@ -105,26 +132,29 @@ describe('ProxistoreBidAdapter', function () { expect(data.bids[0].floor).to.be.null; }); }); - describe('interpretResponse', function() { - const emptyResponseParam = {body: []}; - const fakeResponseParam = {body: [ - { ad: '', - cpm: 6.25, - creativeId: '22c3290b-8cd5-4cd6-8e8c-28a2de180ccd', - currency: 'EUR', - dealId: '2021-03_a63ec55e-b9bb-4ca4-b2c9-f456be67e656', - height: 600, - netRevenue: true, - requestId: '3543724f2a033c9', - segments: [], - ttl: 10, - vastUrl: null, - vastXml: null, - width: 300} - ] + describe('interpretResponse', function () { + const emptyResponseParam = { body: [] }; + const fakeResponseParam = { + body: [ + { + ad: '', + cpm: 6.25, + creativeId: '22c3290b-8cd5-4cd6-8e8c-28a2de180ccd', + currency: 'EUR', + dealId: '2021-03_a63ec55e-b9bb-4ca4-b2c9-f456be67e656', + height: 600, + netRevenue: true, + requestId: '3543724f2a033c9', + segments: [], + ttl: 10, + vastUrl: null, + vastXml: null, + width: 300, + }, + ], }; - it('should always return an array', function() { + it('should always return an array', function () { let response = spec.interpretResponse(emptyResponseParam, bid); expect(response).to.be.an('array'); expect(response.length).equal(0); From df0d4ddb9ee4e2dce740f3dcefbdbc0482b4cae2 Mon Sep 17 00:00:00 2001 From: Klaas-Jan Boon Date: Mon, 12 Apr 2021 12:52:27 +0200 Subject: [PATCH 66/93] BlueBillyWig Bid Adapter: add renderer customization options (#6540) * add Blue Billywig adapter * Blue Billywig Adapter - update according to review feedback * Blue Billywig Adapter - update to try and pass CircleCI * Remove the last for .. of in bluebillywigBidAdapter.js, hopefully... * Update bluebillywigBidAdapter test parameters to match renderer to rendererCode rename * Blue Billywig - Also pass through site config with OpenRTB request * add Blue Billywig adapter * Blue Billywig Adapter - update according to review feedback * Blue Billywig Adapter - update to try and pass CircleCI * Remove the last for .. of in bluebillywigBidAdapter.js, hopefully... * Code quality update, always hit user syncs, improved video params * Remove unnecessary export * Add rendererSettings param to bluebillywig adapter * Kick off CircleCi tests manually Co-authored-by: Klaas-Jan Boon Co-authored-by: Chris Huie --- modules/bluebillywigBidAdapter.js | 10 ++++++++-- .../modules/bluebillywigBidAdapter_spec.js | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/modules/bluebillywigBidAdapter.js b/modules/bluebillywigBidAdapter.js index afacc48aa8e..cac993b0f8c 100644 --- a/modules/bluebillywigBidAdapter.js +++ b/modules/bluebillywigBidAdapter.js @@ -28,7 +28,7 @@ const BB_CONSTANTS = { const getConfig = config.getConfig; // Helper Functions -export const BB_HELPERS = { +const BB_HELPERS = { addSiteAppDevice: function(request, pageUrl) { if (typeof getConfig('app') === 'object') request.app = getConfig('app'); else { @@ -151,7 +151,7 @@ const BB_RENDERER = { const ele = document.getElementById(bid.adUnitCode); // NB convention const renderer = find(window.bluebillywig.renderers, r => r._id === rendererId); - if (renderer) renderer.bootstrap(config, ele); + if (renderer) renderer.bootstrap(config, ele, bid.rendererSettings || {}); else utils.logWarn(`${BB_CONSTANTS.BIDDER_CODE}: Couldn't find a renderer with ${rendererId}`); }, newRenderer: function(rendererUrl, adUnitCode) { @@ -235,6 +235,11 @@ export const spec = { return false; } + if (bid.params.hasOwnProperty('rendererSettings') && (bid.params.rendererSettings === null || typeof bid.params.rendererSettings !== 'object')) { + utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: params.rendererSettings must be of type object. Rejecting bid: `, bid); + return false; + } + if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { utils.logError(`${BB_CONSTANTS.BIDDER_CODE}: no context specified in bid. Rejecting bid: `, bid); @@ -330,6 +335,7 @@ export const spec = { bid.publicationName = bidParams.publicationName; bid.rendererCode = bidParams.rendererCode; bid.accountId = bidParams.accountId; + bid.rendererSettings = bidParams.rendererSettings; const rendererUrl = BB_HELPERS.getRendererUrl(bid.publicationName, bid.rendererCode); bid.renderer = BB_RENDERER.newRenderer(rendererUrl, bid.adUnitCode); diff --git a/test/spec/modules/bluebillywigBidAdapter_spec.js b/test/spec/modules/bluebillywigBidAdapter_spec.js index 56ea29d6d73..3aee6c69438 100644 --- a/test/spec/modules/bluebillywigBidAdapter_spec.js +++ b/test/spec/modules/bluebillywigBidAdapter_spec.js @@ -210,6 +210,25 @@ describe('BlueBillywigAdapter', () => { bid.params.video = void (0); expect(spec.isBidRequestValid(bid)).to.equal(false); }); + + it('should fail if rendererSettings is specified but is not an object', () => { + const bid = deepClone(baseValidBid); + + bid.params.rendererSettings = null; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.rendererSettings = 'string'; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.rendererSettings = 123; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.rendererSettings = false; + expect(spec.isBidRequestValid(bid)).to.equal(false); + + bid.params.rendererSettings = void (0); + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); }); describe('buildRequests', () => { From d911c0fdcc53fc861a7fd77812dad97a25bd6794 Mon Sep 17 00:00:00 2001 From: Kenan Gillet <1706856+kenan-gillet@users.noreply.github.com> Date: Mon, 12 Apr 2021 11:33:14 -0700 Subject: [PATCH 67/93] OpenX Bid Adapter: Set Deal ID for video requests (#6573) --- modules/openxBidAdapter.js | 3 +++ test/spec/modules/openxBidAdapter_spec.js | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/modules/openxBidAdapter.js b/modules/openxBidAdapter.js index fa65abdc56b..460a721cb8b 100644 --- a/modules/openxBidAdapter.js +++ b/modules/openxBidAdapter.js @@ -440,6 +440,9 @@ function createVideoBidResponses(response, {bid, startTime}) { let vastQueryParams = utils.parseUrl(response.vastUrl).search || {}; let bidResponse = {}; bidResponse.requestId = bid.bidId; + if (response.deal_id) { + bidResponse.dealId = response.deal_id; + } // default 5 mins bidResponse.ttl = 300; // true is net, false is gross diff --git a/test/spec/modules/openxBidAdapter_spec.js b/test/spec/modules/openxBidAdapter_spec.js index c8c92392ff2..580fd3dec93 100644 --- a/test/spec/modules/openxBidAdapter_spec.js +++ b/test/spec/modules/openxBidAdapter_spec.js @@ -1854,6 +1854,13 @@ describe('OpenxAdapter', function () { expect(JSON.stringify(Object.keys(result[0]).sort())).to.eql(JSON.stringify(Object.keys(expectedResponse[0]).sort())); }); + it('should return correct bid response with MediaType and deal_id', function () { + const bidResponseOverride = { 'deal_id': 'OX-mydeal' }; + const bidResponseWithDealId = Object.assign({}, bidResponse, bidResponseOverride); + const result = spec.interpretResponse({body: bidResponseWithDealId}, bidRequestsWithMediaType); + expect(result[0].dealId).to.equal(bidResponseOverride.deal_id); + }); + it('should handle nobid responses for bidRequests with MediaTypes', function () { const bidResponse = {'vastUrl': '', 'pub_rev': '', 'width': '', 'height': '', 'adid': '', 'pixels': ''}; const result = spec.interpretResponse({body: bidResponse}, bidRequestsWithMediaTypes); From 87a333838a7b9dabf5af75602873ea0eddefbc80 Mon Sep 17 00:00:00 2001 From: Aparna Rao Date: Mon, 12 Apr 2021 17:08:02 -0400 Subject: [PATCH 68/93] 33Across Bid Adapter: add support for User ID modules (#6554) --- modules/33acrossBidAdapter.js | 61 +++-- test/spec/modules/33acrossBidAdapter_spec.js | 260 ++++++++++++++----- 2 files changed, 242 insertions(+), 79 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 65df8baad2e..e5a6d148808 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -194,18 +194,36 @@ function _createServerRequest({bidRequest, gdprConsent = {}, uspConsent, pageUrl // therefore in ad targetting process ttxRequest.id = bidRequest.bidId; - // Set GDPR related fields - ttxRequest.user = { - ext: { - consent: gdprConsent.consentString - } - }; - ttxRequest.regs = { - ext: { - gdpr: (gdprConsent.gdprApplies === true) ? 1 : 0, - us_privacy: uspConsent || null - } - }; + if (gdprConsent.consentString) { + ttxRequest.user = setExtension( + ttxRequest.user, + 'consent', + gdprConsent.consentString + ) + } + + if (Array.isArray(bidRequest.userIdAsEids) && bidRequest.userIdAsEids.length > 0) { + ttxRequest.user = setExtension( + ttxRequest.user, + 'eids', + bidRequest.userIdAsEids + ) + } + + ttxRequest.regs = setExtension( + ttxRequest.regs, + 'gdpr', + Number(gdprConsent.gdprApplies) + ); + + if (uspConsent) { + ttxRequest.regs = setExtension( + ttxRequest.regs, + 'us_privacy', + uspConsent + ) + } + ttxRequest.ext = { ttx: { prebidStartedAt: Date.now(), @@ -217,11 +235,11 @@ function _createServerRequest({bidRequest, gdprConsent = {}, uspConsent, pageUrl }; if (bidRequest.schain) { - ttxRequest.source = { - ext: { - schain: bidRequest.schain - } - } + ttxRequest.source = setExtension( + ttxRequest.source, + 'schain', + bidRequest.schain + ) } // Finally, set the openRTB 'test' param if this is to be a test bid @@ -250,6 +268,15 @@ function _createServerRequest({bidRequest, gdprConsent = {}, uspConsent, pageUrl } } +// BUILD REQUESTS: SET EXTENSIONS +function setExtension(obj = {}, key, value) { + return Object.assign({}, obj, { + ext: Object.assign({}, obj.ext, { + [key]: value + }) + }); +} + // BUILD REQUESTS: SIZE INFERENCE function _transformSizes(sizes) { if (utils.isArray(sizes) && sizes.length === 2 && !utils.isArray(sizes[0])) { diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index edc7b7a2767..c16e3fffd03 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -5,6 +5,14 @@ import { config } from 'src/config.js'; import { spec } from 'modules/33acrossBidAdapter.js'; +function validateBuiltServerRequest(builtReq, expectedReq) { + expect(builtReq.url).to.equal(expectedReq.url); + expect(builtReq.options).to.deep.equal(expectedReq.options); + expect(JSON.parse(builtReq.data)).to.deep.equal( + JSON.parse(expectedReq.data) + ) +} + describe('33acrossBidAdapter:', function () { const BIDDER_CODE = '33across'; const SITE_ID = 'sample33xGUID123456789'; @@ -22,14 +30,9 @@ describe('33acrossBidAdapter:', function () { id: SITE_ID }, id: 'b1', - user: { - ext: { - } - }, regs: { ext: { - gdpr: 0, - us_privacy: null + gdpr: 0 } }, ext: { @@ -194,6 +197,18 @@ describe('33acrossBidAdapter:', function () { return this; }; + this.withUserIds = (eids) => { + Object.assign(ttxRequest, { + user: { + ext: { + eids + } + } + }); + + return this; + } + this.build = () => ttxRequest; } @@ -270,6 +285,12 @@ describe('33acrossBidAdapter:', function () { return this; } + this.withUserIds = (eids) => { + bidRequests[0].userIdAsEids = eids; + + return this; + }; + this.build = () => bidRequests; } @@ -568,7 +589,8 @@ describe('33acrossBidAdapter:', function () { Object.assign(element, { width: 600, height: 400 }); - expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); + const [ buildRequest ] = spec.buildRequests(bidRequests); + validateBuiltServerRequest(buildRequest, serverRequest); }); }); @@ -585,7 +607,8 @@ describe('33acrossBidAdapter:', function () { Object.assign(element, { x: -300, y: 0, width: 207, height: 320 }); - expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); + const [ buildRequest ] = spec.buildRequests(bidRequests); + validateBuiltServerRequest(buildRequest, serverRequest); }); }); @@ -602,7 +625,8 @@ describe('33acrossBidAdapter:', function () { Object.assign(element, { width: 800, height: 800 }); - expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); + const [ buildRequest ] = spec.buildRequests(bidRequests); + validateBuiltServerRequest(buildRequest, serverRequest); }); }); @@ -621,7 +645,8 @@ describe('33acrossBidAdapter:', function () { Object.assign(element, { width: 0, height: 0 }); bidRequests[0].mediaTypes.banner.sizes = [[800, 2400]]; - expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); + const [ buildRequest ] = spec.buildRequests(bidRequests); + validateBuiltServerRequest(buildRequest, serverRequest); }); }); @@ -643,7 +668,8 @@ describe('33acrossBidAdapter:', function () { sandbox.stub(utils, 'getWindowTop').returns({}); sandbox.stub(utils, 'getWindowSelf').returns(win); - expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); + const [ buildRequest ] = spec.buildRequests(bidRequests); + validateBuiltServerRequest(buildRequest, serverRequest); }); }); @@ -664,7 +690,8 @@ describe('33acrossBidAdapter:', function () { win.document.visibilityState = 'hidden'; sandbox.stub(utils, 'getWindowTop').returns(win); - expect(spec.buildRequests(bidRequests)).to.deep.equal([ serverRequest ]); + const [ buildRequest ] = spec.buildRequests(bidRequests); + validateBuiltServerRequest(buildRequest, serverRequest); }); }); @@ -689,9 +716,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); it('returns corresponding test server requests with gdpr consent data', function() { @@ -710,9 +737,9 @@ describe('33acrossBidAdapter:', function () { .withData(ttxRequest) .withUrl('https://foo.com/hb/') .build(); - const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -731,9 +758,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); it('returns corresponding test server requests with default gdpr consent data', function() { @@ -751,9 +778,9 @@ describe('33acrossBidAdapter:', function () { .withData(ttxRequest) .withUrl('https://foo.com/hb/') .build(); - const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -775,9 +802,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); it('returns corresponding test server requests with us_privacy consent data', function() { @@ -796,9 +823,9 @@ describe('33acrossBidAdapter:', function () { .withData(ttxRequest) .withUrl('https://foo.com/hb/') .build(); - const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -817,9 +844,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); it('returns corresponding test server requests with default us_privacy consent data', function() { @@ -837,9 +864,9 @@ describe('33acrossBidAdapter:', function () { .withData(ttxRequest) .withUrl('https://foo.com/hb/') .build(); - const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -860,9 +887,9 @@ describe('33acrossBidAdapter:', function () { .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -880,9 +907,9 @@ describe('33acrossBidAdapter:', function () { .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, bidderRequest); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -934,9 +961,9 @@ describe('33acrossBidAdapter:', function () { .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); }); @@ -952,9 +979,9 @@ describe('33acrossBidAdapter:', function () { .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -967,9 +994,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -984,9 +1011,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); it('sets bidfloors in ttxRequest if there is a floor', function() { @@ -1009,9 +1036,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -1034,9 +1061,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); it('builds instream request with params passed', function() { @@ -1051,9 +1078,9 @@ describe('33acrossBidAdapter:', function () { .withProduct('instream') .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(JSON.parse(builtServerRequests[0].data)).to.deep.equal(ttxRequest); + expect(JSON.parse(builtServerRequest.data)).to.deep.equal(ttxRequest); }); }); @@ -1075,9 +1102,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); it('builds siab request with video params passed', function() { @@ -1095,9 +1122,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -1117,9 +1144,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); it('builds default inview request when product is set as such', function() { @@ -1138,9 +1165,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -1162,9 +1189,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); it('builds siab request with banner and outstream video even when context is instream', function() { @@ -1186,9 +1213,9 @@ describe('33acrossBidAdapter:', function () { const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(builtServerRequests).to.deep.equal([serverRequest]); + validateBuiltServerRequest(builtServerRequest, serverRequest); }); }); @@ -1207,9 +1234,9 @@ describe('33acrossBidAdapter:', function () { .withProduct() .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); - expect(JSON.parse(builtServerRequests[0].data)).to.deep.equal(ttxRequest); + expect(JSON.parse(builtServerRequest.data)).to.deep.equal(ttxRequest); }); it('sets bidfloors in video if there is a floor', function() { @@ -1235,9 +1262,118 @@ describe('33acrossBidAdapter:', function () { .withFloors('video', [ 1.0 ]) .build(); - const builtServerRequests = spec.buildRequests(bidRequests, {}); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); + + expect(JSON.parse(builtServerRequest.data)).to.deep.equal(ttxRequest); + }); + }); + + context('when user ID data exists as userIdAsEids Array in bidRequest', function() { + it('passes userIds in eids field in ORTB request', function() { + const eids = [ + { + 'source': 'x-device-vendor-x.com', + 'uids': [ + { + 'id': 'yyy', + 'atype': 1 + }, + { + 'id': 'zzz', + 'atype': 1 + }, + { + 'id': 'DB700403-9A24-4A4B-A8D5-8A0B4BE777D2', + 'atype': 2 + } + ], + 'ext': { + 'foo': 'bar' + } + } + ]; + + const bidRequests = ( + new BidRequestsBuilder() + .withUserIds(eids) + .build() + ); + + const ttxRequest = new TtxRequestBuilder() + .withUserIds(eids) + .withProduct() + .build(); + + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); + + expect(JSON.parse(builtServerRequest.data)).to.deep.equal(ttxRequest); + }); + + it('does not validate eids ORTB', function() { + const eids = [1, 2, 3]; + + const bidRequests = ( + new BidRequestsBuilder() + .withUserIds(eids) + .build() + ); + + const ttxRequest = new TtxRequestBuilder() + .withUserIds(eids) + .withProduct() + .build(); + + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); + + expect(JSON.parse(builtServerRequest.data)).to.deep.equal(ttxRequest); + }); + }); + + context('when user IDs do not exist under the userIdAsEids field in bidRequest as a non-empty Array', function() { + it('does not pass user IDs in the bidRequest ORTB', function() { + const eidsScenarios = [ + 'foo', + [], + {foo: 1} + ]; - expect(JSON.parse(builtServerRequests[0].data)).to.deep.equal(ttxRequest); + eidsScenarios.forEach((eids) => { + const bidRequests = ( + new BidRequestsBuilder() + .withUserIds(eids) + .build() + ); + bidRequests.userId = { + 'vendorx': { + 'source': 'x-device-vendor-x.com', + 'uids': [ + { + 'id': 'yyy', + 'atype': 1 + }, + { + 'id': 'zzz', + 'atype': 1 + }, + { + 'id': 'DB700403-9A24-4A4B-A8D5-8A0B4BE777D2', + 'atype': 2 + } + ], + 'ext': { + 'foo': 'bar' + } + } + }; + + const ttxRequest = new TtxRequestBuilder() + .withProduct() + .build(); + + const [ builtServerRequest ] = spec.buildRequests(bidRequests, {}); + + expect(JSON.parse(builtServerRequest.data)).to.deep.equal(ttxRequest); + }); }); }); }); From 62ea47c6690ad5e902fa1e47a79fd675399ad3dc Mon Sep 17 00:00:00 2001 From: Meng <5110935+edmonl@users.noreply.github.com> Date: Mon, 12 Apr 2021 17:28:19 -0400 Subject: [PATCH 69/93] pubGENIUS bid adapter: support floor module (#6555) --- modules/pubgeniusBidAdapter.js | 15 +++++++++++---- modules/pubgeniusBidAdapter.md | 4 ---- test/spec/modules/pubgeniusBidAdapter_spec.js | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/modules/pubgeniusBidAdapter.js b/modules/pubgeniusBidAdapter.js index 5c750e66c25..2df2d25f627 100644 --- a/modules/pubgeniusBidAdapter.js +++ b/modules/pubgeniusBidAdapter.js @@ -7,8 +7,8 @@ import { deepSetValue, inIframe, isArrayOfNums, + isFn, isInteger, - isNumber, isStr, logError, parseQueryStringParameters, @@ -185,9 +185,16 @@ function buildImp(bid) { imp.video = buildVideoParams(bid.mediaTypes.video, bid.params.video); } - const bidFloor = bid.params.bidFloor; - if (isNumber(bidFloor)) { - imp.bidfloor = bidFloor; + if (isFn(bid.getFloor)) { + const { floor } = bid.getFloor({ + mediaType: bid.mediaTypes.banner ? 'banner' : 'video', + size: '*', + currency: 'USD', + }); + + if (floor) { + imp.bidfloor = floor; + } } const pos = bid.params.position; diff --git a/modules/pubgeniusBidAdapter.md b/modules/pubgeniusBidAdapter.md index ff23a433331..66e0c382285 100644 --- a/modules/pubgeniusBidAdapter.md +++ b/modules/pubgeniusBidAdapter.md @@ -12,8 +12,6 @@ Module that connects to pubGENIUS's demand sources # Test Parameters -Test bids have $0.01 CPM by default. Use `bidFloor` in bidder params to control CPM for testing purposes. - ``` var adUnits = [ { @@ -45,7 +43,6 @@ var adUnits = [ bidder: 'pubgenius', params: { adUnitId: '1000', - bidFloor: 0.5, test: true } } @@ -66,7 +63,6 @@ var adUnits = [ bidder: 'pubgenius', params: { adUnitId: '1001', - bidFloor: 1, test: true, // other video parameters as in OpenRTB v2.5 spec diff --git a/test/spec/modules/pubgeniusBidAdapter_spec.js b/test/spec/modules/pubgeniusBidAdapter_spec.js index 382199dcffc..57b83fced06 100644 --- a/test/spec/modules/pubgeniusBidAdapter_spec.js +++ b/test/spec/modules/pubgeniusBidAdapter_spec.js @@ -225,8 +225,8 @@ describe('pubGENIUS adapter', () => { expect(buildRequests([bidRequest, bidRequest1], bidderRequest)).to.deep.equal(expectedRequest); }); - it('should take bid floor in bidder params', () => { - bidRequest.params.bidFloor = 0.5; + it('should take bid floor from getFloor interface', () => { + bidRequest.getFloor = () => ({ floor: 0.5, currency: 'USD' }); expectedRequest.data.imp[0].bidfloor = 0.5; expect(buildRequests([bidRequest], bidderRequest)).to.deep.equal(expectedRequest); From 36d211a91cb42429f13b7f4f7cab743bc00e1bf1 Mon Sep 17 00:00:00 2001 From: Nick Duitz <42961155+nduitz@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:39:54 +0200 Subject: [PATCH 70/93] Welect Bid Adapter: update url of API (#6570) * update api url * update api url in tests --- modules/welectBidAdapter.js | 2 +- test/spec/modules/welectBidAdapter_spec.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/welectBidAdapter.js b/modules/welectBidAdapter.js index f9fc57a4834..467b628c944 100644 --- a/modules/welectBidAdapter.js +++ b/modules/welectBidAdapter.js @@ -38,7 +38,7 @@ export const spec = { let domain = bidRequest.params.domain || DEFAULT_DOMAIN; - let url = `https://${domain}/api/v2/preflight/by_alias/${bidRequest.params.placementId}`; + let url = `https://${domain}/api/v2/preflight/${bidRequest.params.placementId}`; let gdprConsent = null; diff --git a/test/spec/modules/welectBidAdapter_spec.js b/test/spec/modules/welectBidAdapter_spec.js index 5f047904c76..571f68038ac 100644 --- a/test/spec/modules/welectBidAdapter_spec.js +++ b/test/spec/modules/welectBidAdapter_spec.js @@ -103,7 +103,7 @@ describe('WelectAdapter', function () { // Formatted requets let request1 = { method: 'POST', - url: 'https://www.welect.de/api/v2/preflight/by_alias/exampleAlias', + url: 'https://www.welect.de/api/v2/preflight/exampleAlias', data: data1, options: { contentType: 'application/json', @@ -114,7 +114,7 @@ describe('WelectAdapter', function () { let request2 = { method: 'POST', - url: 'https://www.welect2.de/api/v2/preflight/by_alias/exampleAlias', + url: 'https://www.welect2.de/api/v2/preflight/exampleAlias', data: data2, options: { contentType: 'application/json', @@ -171,7 +171,7 @@ describe('WelectAdapter', function () { } }, method: 'POST', - url: 'https://www.welect.de/api/v2/preflight/by_alias/exampleAlias', + url: 'https://www.welect.de/api/v2/preflight/exampleAlias', options: { contentType: 'application/json', withCredentials: false, From 2794ce9335e4a938c0cec839b931b9efb1449eed Mon Sep 17 00:00:00 2001 From: BrightMountainMedia <69471268+BrightMountainMediaInc@users.noreply.github.com> Date: Tue, 13 Apr 2021 17:19:05 +0530 Subject: [PATCH 71/93] Bright Mountain Media Bid Adapter: change bidder code to bmtm; alias old name (#6574) --- modules/brightMountainMediaBidAdapter.js | 3 ++- modules/brightMountainMediaBidAdapter.md | 2 +- test/spec/modules/brightMountainMediaBidAdapter_spec.js | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index aa1076e798a..6434d8b2b81 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -2,11 +2,12 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; -const BIDDER_CODE = 'brightmountainmedia'; +const BIDDER_CODE = 'bmtm'; const AD_URL = 'https://console.brightmountainmedia.com/hb/bid'; export const spec = { code: BIDDER_CODE, + aliases: ['brightmountainmedia'], supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: (bid) => { diff --git a/modules/brightMountainMediaBidAdapter.md b/modules/brightMountainMediaBidAdapter.md index a9900b5264d..8dba3a4e6e5 100644 --- a/modules/brightMountainMediaBidAdapter.md +++ b/modules/brightMountainMediaBidAdapter.md @@ -23,7 +23,7 @@ Bright Mountain Media bid adapter currently supports Banner. }, bids: [ { - bidder: 'brightmountainmedia', + bidder: 'bmtm', params: { placement_id: '5f21784949be82079d08c', traffic: 'banner' diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/brightMountainMediaBidAdapter_spec.js index f6312253722..7511b5bf239 100644 --- a/test/spec/modules/brightMountainMediaBidAdapter_spec.js +++ b/test/spec/modules/brightMountainMediaBidAdapter_spec.js @@ -4,7 +4,7 @@ import { spec } from '../../../modules/brightMountainMediaBidAdapter.js'; describe('brightMountainMediaBidAdapter_spec', function () { let bid = { bidId: '2dd581a2b6281d', - bidder: 'brightmountainmedia', + bidder: 'bmtm', bidderRequestId: '145e1d6a7837c9', params: { placement_id: '123qwerty' @@ -19,7 +19,7 @@ describe('brightMountainMediaBidAdapter_spec', function () { transactionId: '3bb2f6da-87a6-4029-aeb0-bfe951372e62', }; let bidderRequest = { - bidderCode: 'brightmountainmedia', + bidderCode: 'bmtm', auctionId: 'fffffff-ffff-ffff-ffff-ffffffffffff', bidderRequestId: 'ffffffffffffff', start: 1472239426002, From df6cf9a4bca25f7988a35f56a8e7f430ac9d1b55 Mon Sep 17 00:00:00 2001 From: Gena Date: Tue, 13 Apr 2021 15:15:12 +0300 Subject: [PATCH 72/93] Adtelligent Bid Adapter: add adUrl support & new alias (#6559) * add adUrl support * add adUrl test --- modules/adtelligentBidAdapter.js | 9 +++++++-- test/spec/modules/adtelligentBidAdapter_spec.js | 3 +++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 669feeedb04..9ca4b95dfd2 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -19,6 +19,7 @@ const HOST_GETTERS = { appaloosa: () => 'ghb.hb.appaloosa.media', onefiftytwomedia: () => 'ghb.ads.152media.com', mediafuse: () => 'ghb.hbmp.mediafuse.com', + bidsxchange: () => 'ghb.hbd.bidsxchange.com', } const getUri = function (bidderCode) { let bidderWithoutSuffix = bidderCode.split('_')[0]; @@ -34,7 +35,7 @@ const syncsCache = {}; export const spec = { code: BIDDER_CODE, gvlid: 410, - aliases: ['onefiftytwomedia', 'selectmedia', 'appaloosa', + aliases: ['onefiftytwomedia', 'selectmedia', 'appaloosa', 'bidsxchange', { code: 'navelix', gvlid: 380 }, { code: 'mediafuse', @@ -205,6 +206,9 @@ function prepareBidRequests(bidReq) { }; bidReqParams.PlacementId = bidReq.adUnitCode; + if (bidReq.params.iframe) { + bidReqParams.AdmType = 'iframe'; + } if (bidReq.params.vpb_placement_id) { bidReqParams.PlacementId = bidReq.params.vpb_placement_id; } @@ -249,7 +253,8 @@ function createBid(bidResponse, bidRequest) { if (mediaType === BANNER) { return Object.assign(bid, { - ad: bidResponse.ad + ad: bidResponse.ad, + adUrl: bidResponse.adUrl, }); } if (context === ADPOD) { diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index 67372216e96..e6916997133 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -16,6 +16,7 @@ const aliasEP = { onefiftytwomedia: 'https://ghb.ads.152media.com/v2/auction/', mediafuse: 'https://ghb.hbmp.mediafuse.com/v2/auction/', navelix: 'https://ghb.hb.navelix.com/v2/auction/', + bidsxchange: 'https://ghb.hbd.bidsxchange.com/v2/auction/', }; const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; @@ -88,6 +89,7 @@ const SERVER_DISPLAY_RESPONSE = { 'source': { 'aid': 12345, 'pubId': 54321 }, 'bids': [{ 'ad': '', + 'adUrl': 'adUrl', 'requestId': '2e41f65424c87c', 'creative_id': 342516, 'cmpId': 342516, @@ -171,6 +173,7 @@ const displayEqResponse = [{ netRevenue: true, currency: 'USD', ad: '', + adUrl: 'adUrl', height: 250, width: 300, ttl: 300, From 28cd35a0635dd83e1d8797b8b4b59da115ba65a1 Mon Sep 17 00:00:00 2001 From: BrightMountainMedia <69471268+BrightMountainMediaInc@users.noreply.github.com> Date: Tue, 13 Apr 2021 18:38:18 +0530 Subject: [PATCH 73/93] Bright Mountain Media Bid Adapter: Change Endpoint URL (#6576) --- modules/brightMountainMediaBidAdapter.js | 2 +- test/spec/modules/brightMountainMediaBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index 6434d8b2b81..b57e696148f 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -3,7 +3,7 @@ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; import * as utils from '../src/utils.js'; const BIDDER_CODE = 'bmtm'; -const AD_URL = 'https://console.brightmountainmedia.com/hb/bid'; +const AD_URL = 'https://one.elitebidder.com/api/hb'; export const spec = { code: BIDDER_CODE, diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/brightMountainMediaBidAdapter_spec.js index 7511b5bf239..bc7f69f0baf 100644 --- a/test/spec/modules/brightMountainMediaBidAdapter_spec.js +++ b/test/spec/modules/brightMountainMediaBidAdapter_spec.js @@ -55,7 +55,7 @@ describe('brightMountainMediaBidAdapter_spec', function () { expect(serverRequest.method).to.equal('POST'); }); it('Returns valid URL', function () { - expect(serverRequest.url).to.equal('https://console.brightmountainmedia.com/hb/bid'); + expect(serverRequest.url).to.equal('https://one.elitebidder.com/api/hb'); }); it('Returns valid data if array of bids is valid', function () { From 587a5f6c92d6ae6486e464a8b42f39c4390c4f03 Mon Sep 17 00:00:00 2001 From: marc_tappx Date: Wed, 24 Mar 2021 12:27:26 +0100 Subject: [PATCH 74/93] tappxBidAdapter: update --- modules/tappxBidAdapter.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 3677c3ce4c9..80a05a0de41 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -286,6 +286,10 @@ function buildOneRequest(validBidRequests, bidderRequest) { payload.regs = regs; // < Payload + utils.logMessage('-------- PAYLOAD ------------') + utils.logMessage(JSON.stringify(payload)) + utils.logMessage('-------- PAYLOAD ------------') + return { method: 'POST', url: `https://${HOST}/${ENDPOINT}?type_cnn=${TYPE_CNN}&v=${TAPPX_BIDDER_VERSION}`, From dee1db605042de7b36f443505a8fad01e54701a7 Mon Sep 17 00:00:00 2001 From: marc_tappx Date: Fri, 9 Apr 2021 08:32:11 +0200 Subject: [PATCH 75/93] tasppxBidAdapter: add video --- modules/tappxBidAdapter.js | 42 ++- test/spec/modules/tappxBidAdapter_spec.js | 332 +++++++++++----------- 2 files changed, 198 insertions(+), 176 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 80a05a0de41..861d6c9663b 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.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'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; @@ -15,7 +15,7 @@ var hostDomain; export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, VIDEO], /** * Determines whether or not the given bid request is valid. @@ -113,18 +113,31 @@ export const spec = { * @returns {object} Prebid banner bidObject */ function interpretBannerBid(serverBid, request) { - return { + let bidReturned = { requestId: request.bids.bidId, cpm: serverBid.price, currency: serverBid.cur ? serverBid.cur : CUR, width: serverBid.w, height: serverBid.h, - ad: serverBid.adm, ttl: TTL, creativeId: serverBid.crid, netRevenue: true, - mediaType: BANNER, } + + if (serverBid.video) { + utils.logMessage('--- VIDEO'); + bidReturned.vastXml = serverBid.adm; + bidReturned.vastUrl = serverBid.lurl; + bidReturned.ad = serverBid.adm; + bidReturned.mediaType = VIDEO; + } + if (serverBid.banner) { + utils.logMessage('--- BANNER'); + bidReturned.ad = serverBid.adm; + bidReturned.mediaType = BANNER; + } + + return bidReturned; } /** @@ -136,14 +149,14 @@ function interpretBannerBid(serverBid, request) { */ function buildOneRequest(validBidRequests, bidderRequest) { HOST = utils.deepAccess(validBidRequests, 'params.host'); - let hostInfo = getHostInfo(HOST) - // hostDomain = HOST.split('/', 1)[0]; + let hostInfo = getHostInfo(HOST); hostDomain = hostInfo.domain; const ENDPOINT = utils.deepAccess(validBidRequests, 'params.endpoint'); const TAPPXKEY = utils.deepAccess(validBidRequests, 'params.tappxkey'); const BIDFLOOR = utils.deepAccess(validBidRequests, 'params.bidfloor'); const bannerMediaType = utils.deepAccess(validBidRequests, 'mediaTypes.banner'); + const videoMediaType = utils.deepAccess(validBidRequests, 'mediaTypes.video'); const { refererInfo } = bidderRequest; // let requests = []; @@ -205,6 +218,18 @@ function buildOneRequest(validBidRequests, bidderRequest) { imp.banner = banner; } + if (videoMediaType) { + let video = {}; + w = videoMediaType.playerSize[0][0]; + h = videoMediaType.playerSize[0][1]; + video.w = w; + video.h = h; + + video.mimes = videoMediaType.mimes; + + imp.video = video; + } + imp.id = validBidRequests.bidId; imp.tagid = tagid; imp.secure = 1; @@ -286,9 +311,6 @@ function buildOneRequest(validBidRequests, bidderRequest) { payload.regs = regs; // < Payload - utils.logMessage('-------- PAYLOAD ------------') - utils.logMessage(JSON.stringify(payload)) - utils.logMessage('-------- PAYLOAD ------------') return { method: 'POST', diff --git a/test/spec/modules/tappxBidAdapter_spec.js b/test/spec/modules/tappxBidAdapter_spec.js index 1d3f9676d09..2212d93825c 100644 --- a/test/spec/modules/tappxBidAdapter_spec.js +++ b/test/spec/modules/tappxBidAdapter_spec.js @@ -1,181 +1,181 @@ -import { assert } from 'chai'; -import { spec } from 'modules/tappxBidAdapter'; +// import { assert } from 'chai'; +// import { spec } from 'modules/tappxBidAdapter'; -const c_BIDREQUEST = { - data: { - }, - bids: [ - { - bidder: 'tappx', - params: { - host: 'testing.ssp.tappx.com\/rtb\/v2\/', - tappxkey: 'pub-1234-android-1234', - endpoint: 'ZZ1234PBJS', - bidfloor: 0.05 - }, - crumbs: { - pubcid: 'df2144f7-673f-4440-83f5-cd4a73642d99' - }, - fpd: { - context: { - adServer: { - name: 'gam', - adSlot: '/19968336/header-bid-tag-0' - }, - pbAdSlot: '/19968336/header-bid-tag-0', - }, - }, - mediaTypes: { - banner: { - sizes: [ - [ - 320, - 480 - ] - ] - } - }, - adUnitCode: 'div-1', - transactionId: '47dd44e8-e7db-417c-a8f1-621a2e1a117d', - sizes: [ - [ - 320, - 480 - ] - ], - bidId: '2170932097e505', - bidderRequestId: '140ba7a1ab7aeb', - auctionId: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0, - } - ] -}; -const c_SERVERRESPONSE = { - body: { - id: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', - bidid: 'bid3811165568213389257', - seatbid: [ - { - seat: '1', - group: 0, - bid: [ - { - id: '3811165568213389257', - impid: 1, - price: 0.05, - adm: "\t", - w: 320, - h: 480, - lurl: 'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', - cid: '01744fbb521e9fb10ffea926190effea', - crid: 'a13cf884e66e7c660afec059c89d98b6', - adomain: [ - ], - }, - ], - }, - ], - cur: 'USD', - }, - headers: {} -}; -const c_CONSENTSTRING = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; -const c_VALIDBIDREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000x179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6eFJ7otPYix179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6e', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_rEXbz6UYtYEJelYrDaZOLkh8WcF9J0ZHmEHFKZEBlLXsgP6xqXU3BCj4Ay0Z6fw_jSOaHxMHwd-voRHqFA4Q9NwAxFcVLyPWnNGZ9VbcSAPos1wupq7Xu3MIm-Bw_0vxjhZdWNy4chM9x3i', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': 'xTtLUY7GwqX2MMqSHo9RQ2YUOIBFhlASOR43I9KjvgtcrxIys3RxME96M02LTjWR', 'parrableId': {'eid': '02.YoqC9lWZh8.C8QTSiJTNgI6Pp0KCM5zZgEgwVMSsVP5W51X8cmiUHQESq9WRKB4nreqZJwsWIcNKlORhG4u25Wm6lmDOBmQ0B8hv0KP6uVQ97aouuH52zaz2ctVQTORUKkErPRPcaCJ7dKFcrNoF2i6WOR0S5Nk'}, 'pubcid': 'b1254-152f-12F5-5698-dI1eljK6C7WA', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; -const c_VALIDBIDAPPREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; -const c_BIDDERREQUEST = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': c_CONSENTSTRING, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; +// const c_BIDREQUEST = { +// data: { +// }, +// bids: [ +// { +// bidder: 'tappx', +// params: { +// host: 'testing.ssp.tappx.com\/rtb\/v2\/', +// tappxkey: 'pub-1234-android-1234', +// endpoint: 'ZZ1234PBJS', +// bidfloor: 0.05 +// }, +// crumbs: { +// pubcid: 'df2144f7-673f-4440-83f5-cd4a73642d99' +// }, +// fpd: { +// context: { +// adServer: { +// name: 'gam', +// adSlot: '/19968336/header-bid-tag-0' +// }, +// pbAdSlot: '/19968336/header-bid-tag-0', +// }, +// }, +// mediaTypes: { +// banner: { +// sizes: [ +// [ +// 320, +// 480 +// ] +// ] +// } +// }, +// adUnitCode: 'div-1', +// transactionId: '47dd44e8-e7db-417c-a8f1-621a2e1a117d', +// sizes: [ +// [ +// 320, +// 480 +// ] +// ], +// bidId: '2170932097e505', +// bidderRequestId: '140ba7a1ab7aeb', +// auctionId: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', +// src: 'client', +// bidRequestsCount: 1, +// bidderRequestsCount: 1, +// bidderWinsCount: 0, +// } +// ] +// }; +// const c_SERVERRESPONSE = { +// body: { +// id: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', +// bidid: 'bid3811165568213389257', +// seatbid: [ +// { +// seat: '1', +// group: 0, +// bid: [ +// { +// id: '3811165568213389257', +// impid: 1, +// price: 0.05, +// adm: "\t", +// w: 320, +// h: 480, +// lurl: 'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', +// cid: '01744fbb521e9fb10ffea926190effea', +// crid: 'a13cf884e66e7c660afec059c89d98b6', +// adomain: [ +// ], +// }, +// ], +// }, +// ], +// cur: 'USD', +// }, +// headers: {} +// }; +// const c_CONSENTSTRING = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; +// const c_VALIDBIDREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000x179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6eFJ7otPYix179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6e', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_rEXbz6UYtYEJelYrDaZOLkh8WcF9J0ZHmEHFKZEBlLXsgP6xqXU3BCj4Ay0Z6fw_jSOaHxMHwd-voRHqFA4Q9NwAxFcVLyPWnNGZ9VbcSAPos1wupq7Xu3MIm-Bw_0vxjhZdWNy4chM9x3i', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': 'xTtLUY7GwqX2MMqSHo9RQ2YUOIBFhlASOR43I9KjvgtcrxIys3RxME96M02LTjWR', 'parrableId': {'eid': '02.YoqC9lWZh8.C8QTSiJTNgI6Pp0KCM5zZgEgwVMSsVP5W51X8cmiUHQESq9WRKB4nreqZJwsWIcNKlORhG4u25Wm6lmDOBmQ0B8hv0KP6uVQ97aouuH52zaz2ctVQTORUKkErPRPcaCJ7dKFcrNoF2i6WOR0S5Nk'}, 'pubcid': 'b1254-152f-12F5-5698-dI1eljK6C7WA', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; +// const c_VALIDBIDAPPREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; +// const c_BIDDERREQUEST = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': c_CONSENTSTRING, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; -describe('Tappx bid adapter', function () { - /** - * IS REQUEST VALID - */ - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { - assert.isTrue(spec.isBidRequestValid(c_BIDREQUEST.bids[0]), JSON.stringify(c_BIDREQUEST)); - }); +// describe('Tappx bid adapter', function () { +// /** +// * IS REQUEST VALID +// */ +// describe('isBidRequestValid', function () { +// it('should return true when required params found', function () { +// assert.isTrue(spec.isBidRequestValid(c_BIDREQUEST.bids[0]), JSON.stringify(c_BIDREQUEST)); +// }); - it('should return false when required params are missing', function () { - let badBidRequest = c_BIDREQUEST; - delete badBidRequest.bids[0].params.tappxkey; - delete badBidRequest.bids[0].params.endpoint; - assert.isFalse(spec.isBidRequestValid(badBidRequest.bids[0])); - }); - }); +// it('should return false when required params are missing', function () { +// let badBidRequest = c_BIDREQUEST; +// delete badBidRequest.bids[0].params.tappxkey; +// delete badBidRequest.bids[0].params.endpoint; +// assert.isFalse(spec.isBidRequestValid(badBidRequest.bids[0])); +// }); +// }); - /** - * BUILD REQUEST TEST - */ - describe('buildRequest', function () { - // Web Test - let validBidRequests = c_VALIDBIDREQUESTS; - // App Test - let validAppBidRequests = c_VALIDBIDAPPREQUESTS; +// /** +// * BUILD REQUEST TEST +// */ +// describe('buildRequest', function () { +// // Web Test +// let validBidRequests = c_VALIDBIDREQUESTS; +// // App Test +// let validAppBidRequests = c_VALIDBIDAPPREQUESTS; - let bidderRequest = c_BIDDERREQUEST; +// let bidderRequest = c_BIDDERREQUEST; - it('should add gdpr/usp consent information to the request', function () { - const request = spec.buildRequests(validBidRequests, bidderRequest); - const payload = JSON.parse(request[0].data); +// it('should add gdpr/usp consent information to the request', function () { +// const request = spec.buildRequests(validBidRequests, bidderRequest); +// const payload = JSON.parse(request[0].data); - expect(payload.regs.gdpr).to.exist.and.to.be.true; - expect(payload.regs.consent).to.exist.and.to.equal(c_CONSENTSTRING); - expect(payload.regs.ext.us_privacy).to.exist; - }); +// expect(payload.regs.gdpr).to.exist.and.to.be.true; +// expect(payload.regs.consent).to.exist.and.to.equal(c_CONSENTSTRING); +// expect(payload.regs.ext.us_privacy).to.exist; +// }); - it('should properly build a banner request', function () { - const request = spec.buildRequests(validBidRequests, bidderRequest); - expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); - expect(request[0].method).to.equal('POST'); +// it('should properly build a banner request', function () { +// const request = spec.buildRequests(validBidRequests, bidderRequest); +// expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); +// expect(request[0].method).to.equal('POST'); - const data = JSON.parse(request[0].data); - expect(data.site).to.not.equal(null); - expect(data.imp).to.have.lengthOf(1); - expect(data.imp[0].bidfloor, data).to.not.be.null; - expect(data.imp[0].banner).to.not.equal(null); - expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); - expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); - }); +// const data = JSON.parse(request[0].data); +// expect(data.site).to.not.equal(null); +// expect(data.imp).to.have.lengthOf(1); +// expect(data.imp[0].bidfloor, data).to.not.be.null; +// expect(data.imp[0].banner).to.not.equal(null); +// expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); +// expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); +// }); - it('should set user eids array', function () { - const request = spec.buildRequests(validBidRequests, bidderRequest); +// it('should set user eids array', function () { +// const request = spec.buildRequests(validBidRequests, bidderRequest); - const data = JSON.parse(request[0].data); - expect(data.user.ext.eids, data).to.not.be.null; - expect(data.user.ext.eids[0]).to.have.keys(['source', 'uids']); - }); +// const data = JSON.parse(request[0].data); +// expect(data.user.ext.eids, data).to.not.be.null; +// expect(data.user.ext.eids[0]).to.have.keys(['source', 'uids']); +// }); - it('should properly build a banner request with app params', function () { - const request = spec.buildRequests(validAppBidRequests, bidderRequest); - expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); - expect(request[0].method).to.equal('POST'); +// it('should properly build a banner request with app params', function () { +// const request = spec.buildRequests(validAppBidRequests, bidderRequest); +// expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); +// expect(request[0].method).to.equal('POST'); - const data = JSON.parse(request[0].data); - expect(data.site).to.not.equal(null); - expect(data.imp).to.have.lengthOf(1); - expect(data.imp[0].bidfloor, data).to.not.be.null; - expect(data.imp[0].banner).to.not.equal(null); - expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); - expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); - }); - }); +// const data = JSON.parse(request[0].data); +// expect(data.site).to.not.equal(null); +// expect(data.imp).to.have.lengthOf(1); +// expect(data.imp[0].bidfloor, data).to.not.be.null; +// expect(data.imp[0].banner).to.not.equal(null); +// expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); +// expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); +// }); +// }); - /** - * INTERPRET RESPONSE TESTS - */ - describe('interpretResponse', function () { - it('receive reponse with single placement', function () { - const bids = spec.interpretResponse(c_SERVERRESPONSE, c_BIDREQUEST); - const bid = bids[0]; - expect(bid.cpm).to.exist; - expect(bid.ad).to.match(/^\t", -// w: 320, -// h: 480, -// lurl: 'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', -// cid: '01744fbb521e9fb10ffea926190effea', -// crid: 'a13cf884e66e7c660afec059c89d98b6', -// adomain: [ -// ], -// }, -// ], -// }, -// ], -// cur: 'USD', -// }, -// headers: {} -// }; -// const c_CONSENTSTRING = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; -// const c_VALIDBIDREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000x179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6eFJ7otPYix179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6e', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_rEXbz6UYtYEJelYrDaZOLkh8WcF9J0ZHmEHFKZEBlLXsgP6xqXU3BCj4Ay0Z6fw_jSOaHxMHwd-voRHqFA4Q9NwAxFcVLyPWnNGZ9VbcSAPos1wupq7Xu3MIm-Bw_0vxjhZdWNy4chM9x3i', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': 'xTtLUY7GwqX2MMqSHo9RQ2YUOIBFhlASOR43I9KjvgtcrxIys3RxME96M02LTjWR', 'parrableId': {'eid': '02.YoqC9lWZh8.C8QTSiJTNgI6Pp0KCM5zZgEgwVMSsVP5W51X8cmiUHQESq9WRKB4nreqZJwsWIcNKlORhG4u25Wm6lmDOBmQ0B8hv0KP6uVQ97aouuH52zaz2ctVQTORUKkErPRPcaCJ7dKFcrNoF2i6WOR0S5Nk'}, 'pubcid': 'b1254-152f-12F5-5698-dI1eljK6C7WA', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; -// const c_VALIDBIDAPPREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; -// const c_BIDDERREQUEST = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': c_CONSENTSTRING, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; - -// describe('Tappx bid adapter', function () { -// /** -// * IS REQUEST VALID -// */ -// describe('isBidRequestValid', function () { -// it('should return true when required params found', function () { -// assert.isTrue(spec.isBidRequestValid(c_BIDREQUEST.bids[0]), JSON.stringify(c_BIDREQUEST)); -// }); - -// it('should return false when required params are missing', function () { -// let badBidRequest = c_BIDREQUEST; -// delete badBidRequest.bids[0].params.tappxkey; -// delete badBidRequest.bids[0].params.endpoint; -// assert.isFalse(spec.isBidRequestValid(badBidRequest.bids[0])); -// }); -// }); - -// /** -// * BUILD REQUEST TEST -// */ -// describe('buildRequest', function () { -// // Web Test -// let validBidRequests = c_VALIDBIDREQUESTS; -// // App Test -// let validAppBidRequests = c_VALIDBIDAPPREQUESTS; - -// let bidderRequest = c_BIDDERREQUEST; - -// it('should add gdpr/usp consent information to the request', function () { -// const request = spec.buildRequests(validBidRequests, bidderRequest); -// const payload = JSON.parse(request[0].data); - -// expect(payload.regs.gdpr).to.exist.and.to.be.true; -// expect(payload.regs.consent).to.exist.and.to.equal(c_CONSENTSTRING); -// expect(payload.regs.ext.us_privacy).to.exist; -// }); - -// it('should properly build a banner request', function () { -// const request = spec.buildRequests(validBidRequests, bidderRequest); -// expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); -// expect(request[0].method).to.equal('POST'); - -// const data = JSON.parse(request[0].data); -// expect(data.site).to.not.equal(null); -// expect(data.imp).to.have.lengthOf(1); -// expect(data.imp[0].bidfloor, data).to.not.be.null; -// expect(data.imp[0].banner).to.not.equal(null); -// expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); -// expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); -// }); - -// it('should set user eids array', function () { -// const request = spec.buildRequests(validBidRequests, bidderRequest); - -// const data = JSON.parse(request[0].data); -// expect(data.user.ext.eids, data).to.not.be.null; -// expect(data.user.ext.eids[0]).to.have.keys(['source', 'uids']); -// }); - -// it('should properly build a banner request with app params', function () { -// const request = spec.buildRequests(validAppBidRequests, bidderRequest); -// expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); -// expect(request[0].method).to.equal('POST'); - -// const data = JSON.parse(request[0].data); -// expect(data.site).to.not.equal(null); -// expect(data.imp).to.have.lengthOf(1); -// expect(data.imp[0].bidfloor, data).to.not.be.null; -// expect(data.imp[0].banner).to.not.equal(null); -// expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); -// expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); -// }); -// }); - -// /** -// * INTERPRET RESPONSE TESTS -// */ -// describe('interpretResponse', function () { -// it('receive reponse with single placement', function () { -// const bids = spec.interpretResponse(c_SERVERRESPONSE, c_BIDREQUEST); -// const bid = bids[0]; -// expect(bid.cpm).to.exist; -// expect(bid.ad).to.match(/^\t", + w: 320, + h: 480, + lurl: 'http://testing.ssp.tappx.com/rtb/RTBv2Loss?id=3811165568213389257&ep=ZZ1234PBJS&au=test&bu=localhost&sz=320x480&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', + cid: '01744fbb521e9fb10ffea926190effea', + crid: 'a13cf884e66e7c660afec059c89d98b6', + adomain: [ + ], + }, + ], + }, + ], + cur: 'USD', + }, + headers: {} +}; + +const c_SERVERRESPONSE_V = { + body: { + id: '1c54b4f1-645f-44e6-b8ae-5d43c923ef1c', + bidid: 'bid3811165568213389257', + seatbid: [ + { + seat: '1', + group: 0, + bid: [ + { + id: '3811165568213389257', + impid: 1, + price: 0.05, + adm: "Tappx<\/AdSystem>Tappx<\/AdTitle><\/Impression><\/Error>00:00:22<\/Duration><\/Tracking><\/Tracking><\/Tracking><\/Tracking><\/Tracking><\/Tracking><\/Tracking><\/TrackingEvents><\/ClickThrough><\/ClickTracking><\/VideoClicks><\/MediaFile><\/MediaFiles><\/Linear><\/Creative><\/Creatives><\/InLine><\/Ad><\/VAST>", + 'lurl': 'https:\/\/ssp.api.tappx.com\/rtb\/RTBv2Loss?id=5001829913749291152&ep=VZ12TESTCTV&au=test&bu=localhost&sz=6x6&pu=0.005&pt=0.01&cid=&crid=&adv=&aid=${AUCTION_ID}&bidid=${AUCTION_BID_ID}&impid=${AUCTION_IMP_ID}&sid=${AUCTION_SEAT_ID}&adid=${AUCTION_AD_ID}&ap=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}&mbr=${AUCTION_MBR}&l=${AUCTION_LOSS}', + cid: '01744fbb521e9fb10ffea926190effea', + crid: 'a13cf884e66e7c660afec059c89d98b6', + adomain: [ + ], + }, + ], + }, + ], + cur: 'USD', + }, + headers: {} +}; + +const c_CONSENTSTRING = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; +const c_VALIDBIDREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000x179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6eFJ7otPYix179MZAzMqUWsFonu7Drm3eDDBMYtj5SPoWQnl89Upk3WTlCvEnKI9SshX0p6e', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_rEXbz6UYtYEJelYrDaZOLkh8WcF9J0ZHmEHFKZEBlLXsgP6xqXU3BCj4Ay0Z6fw_jSOaHxMHwd-voRHqFA4Q9NwAxFcVLyPWnNGZ9VbcSAPos1wupq7Xu3MIm-Bw_0vxjhZdWNy4chM9x3i', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': 'xTtLUY7GwqX2MMqSHo9RQ2YUOIBFhlASOR43I9KjvgtcrxIys3RxME96M02LTjWR', 'parrableId': {'eid': '02.YoqC9lWZh8.C8QTSiJTNgI6Pp0KCM5zZgEgwVMSsVP5W51X8cmiUHQESq9WRKB4nreqZJwsWIcNKlORhG4u25Wm6lmDOBmQ0B8hv0KP6uVQ97aouuH52zaz2ctVQTORUKkErPRPcaCJ7dKFcrNoF2i6WOR0S5Nk'}, 'pubcid': 'b1254-152f-12F5-5698-dI1eljK6C7WA', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; +const c_VALIDBIDAPPREQUESTS = [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1, 'app': {'name': 'Tappx Test', 'bundle': 'com.test.tappx', 'domain': 'tappx.com', 'publisher': { 'name': 'Tappx', 'domain': 'tappx.com' }}}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}, {'source': 'intentiq.com', 'uids': [{'id': 'GIF89a\u0001\u0000\u0001\u0000�\u0000\u0000���\u0000\u0000\u0000!�\u0004\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;', 'atype': 1}]}, {'source': 'crwdcntrl.net', 'uids': [{'id': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'atype': 1}]}, {'source': 'parrable.com', 'uids': [{'id': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0', 'atype': 1}]}, {'source': 'pubcid.org', 'uids': [{'id': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'atype': 1}]}, {'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}]; +const c_BIDDERREQUEST_B = {'bidderCode': 'tappx', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'bidderRequestId': '1c674c14a3889c', 'bids': [{'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com\/rtb\/v2\/', 'tappxkey': 'pub-1234-android-1234', 'endpoint': 'ZZ1234PBJS', 'bidfloor': 0.005, 'test': 1}, 'userId': {'haloId': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'id5id': {'uid': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'ext': {'linkType': 0}}, 'intentIqId': 'GIF89a\u0000\u0000\u0000\u0000�\u0000\u0000���\u0000\u0000\u0000?�\u0000\u0000\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000A\u0000\u0000;', 'lotamePanoramaId': '8003916b61a95b185690ec103bdf4945a70213e01818a5e5d8690b542730755a', 'parrableId': {'eid': '01.1617088921.7faa68d9570a50ea8e4f359e9b99ca4b7509e948a6175b3e5b0b8cbaf5b62424104ccfb0191ca79366de8368ed267b89a68e236df5f41f96f238e4301659e9023fec05e46399fb1ad0a0'}, 'pubcid': 'b7143795-852f-42f0-8864-5ecbea1ade4e', 'pubProvidedId': [{'source': 'domain.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 1, 'ext': {'stype': 'ppuid'}}]}, {'source': '3rdpartyprovided.com', 'uids': [{'id': 'value read from cookie or local storage', 'atype': 3, 'ext': {'stype': 'sha256email'}}]}]}, 'userIdAsEids': [{'source': 'audigent.com', 'uids': [{'id': '0000fgclxw05ycn0608xiyi90bwpa0c0evvlif0hv1x0i0ku88il0ntek0o0qskvir0trr70u0wqxiix0zq3u1012pa5j315ogh1618nmsj91bmt41c1elzfjf1hl5r1i1kkc2jl', 'atype': 1}]}, {'source': 'id5-sync.com', 'uids': [{'id': 'ID5@iu-PJX_OQ0d6FJjKS8kYfUpHriD_qpoXJUngedfpNva812If1fHEqHHkamLC89txVxk1i9WGqeQrTX97HFCgv9QDa1M_bkHUBsAWFm-D5r1rYrsfMFFiyqwCAEzqNbvsUZXOYCAQSjPcLxR4of22w-U9_JDRThCGRDV3Fmvc38E', 'atype': 1, 'ext': {'linkType': 0}}]}], 'ortb2Imp': {'ext': {'data': {'adserver': {'name': 'gam', 'adslot': '/19968336/header-bid-tag-0'}, 'pbadslot': '/19968336/header-bid-tag-0'}}}, 'mediaTypes': {'banner': {'sizes': [[320, 480], [320, 50]]}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '71c0d86b-4b47-4aff-a6da-1af0b1712439', 'sizes': [[320, 480], [320, 50]], 'bidId': '264d7969b125a5', 'bidderRequestId': '1c674c14a3889c', 'auctionId': '13a8a3a9-ed3a-4101-9435-4699ee77bb62', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}], 'auctionStart': 1617088922120, 'timeout': 700, 'refererInfo': {'referer': 'http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true', 'reachedTop': true, 'isAmp': false, 'numIframes': 0, 'stack': ['http://localhost:9999/integrationExamples/gpt/gdpr_hello_world.html?pbjs_debug=true'], 'canonicalUrl': null}, 'gdprConsent': {'consentString': c_CONSENTSTRING, 'vendorData': {'metadata': 'BO-JeiTPABAOkAAABAENABA', 'gdprApplies': true, 'hasGlobalScope': false, 'cookieVersion': 1, 'created': '2020-12-09T09:22:09.900Z', 'lastUpdated': '2021-01-14T15:44:03.600Z', 'cmpId': 0, 'cmpVersion': 1, 'consentScreen': 0, 'consentLanguage': 'EN', 'vendorListVersion': 1, 'maxVendorId': 0, 'purposeConsents': {}, 'vendorConsents': {}}, 'gdprApplies': true, 'apiVersion': 1}, 'uspConsent': '1YCC', 'start': 1611308859099}; +const c_BIDDERREQUEST_V = {'method': 'POST', 'url': 'https://testing.ssp.tappx.com/rtb/v2//VZ12TESTCTV?type_cnn=prebidjs&v=0.1.10329', 'data': '{"site":{"name":"localhost","bundle":"localhost","domain":"localhost"},"user":{"ext":{}},"id":"e807363f-3095-43a8-a4a6-f44196cb7318","test":1,"at":1,"tmax":1000,"bidder":"tappx","imp":[{"video":{"w":320,"h":250,"mimes":["video/mp4","application/javascript"]},"id":"28f49c71b13f2f","tagid":"localhost_typeAdBanVid_windows","secure":1,"bidfloor":0.005,"ext":{"bidder":{"tappxkey":"pub-1234-desktop-1234","endpoint":"VZ12TESTCTV","host":"testing.ssp.tappx.com/rtb/v2/"}}}],"device":{"os":"windows","ip":"peer","ua":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36","h":864,"w":1536,"dnt":0,"language":"en","make":"Google Inc."},"params":{"host":"tappx.com","bidfloor":0.005},"regs":{"gdpr":0,"ext":{}}}', 'bids': {'bidder': 'tappx', 'params': {'host': 'testing.ssp.tappx.com/rtb/v2/', 'tappxkey': 'pub-1234-desktop-1234', 'endpoint': 'VZ12TESTCTV', 'bidfloor': 0.005, 'test': true}, 'crumbs': {'pubcid': 'dccfe922-3823-4676-b7b2-e5ed8743154e'}, 'ortb2Imp': {'ext': {'data': {'pbadslot': 'video-ad-div'}}}, 'renderer': {'options': {'text': 'Tappx Outstream Video'}}, 'mediaTypes': {'video': {'context': 'instream', 'mimes': ['video/mp4', 'application/javascript'], 'playerSize': [[320, 250]]}}, 'adUnitCode': 'video-ad-div', 'transactionId': 'ed41c805-d14c-49c3-954d-26b98b2aa2c2', 'sizes': [[320, 250]], 'bidId': '28f49c71b13f2f', 'bidderRequestId': '1401710496dc7', 'auctionId': 'e807363f-3095-43a8-a4a6-f44196cb7318', 'src': 'client', 'bidRequestsCount': 1, 'bidderRequestsCount': 1, 'bidderWinsCount': 0}} + +describe('Tappx bid adapter', function () { + /** + * IS REQUEST VALID + */ + describe('isBidRequestValid', function () { + it('should return true when required params found', function () { + assert.isTrue(spec.isBidRequestValid(c_BIDREQUEST.bids[0]), JSON.stringify(c_BIDREQUEST)); + }); + + it('should return false when required params are missing', function () { + let badBidRequest = c_BIDREQUEST; + delete badBidRequest.bids[0].params.tappxkey; + delete badBidRequest.bids[0].params.endpoint; + assert.isFalse(spec.isBidRequestValid(badBidRequest.bids[0])); + }); + }); + + /** + * BUILD REQUEST TEST + */ + describe('buildRequest', function () { + // Web Test + let validBidRequests = c_VALIDBIDREQUESTS; + // App Test + let validAppBidRequests = c_VALIDBIDAPPREQUESTS; + + let bidderRequest = c_BIDDERREQUEST_B; + + it('should add gdpr/usp consent information to the request', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request[0].data); + + expect(payload.regs.gdpr).to.exist.and.to.be.true; + expect(payload.regs.consent).to.exist.and.to.equal(c_CONSENTSTRING); + expect(payload.regs.ext.us_privacy).to.exist; + }); + + it('should properly build a banner request', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); + expect(request[0].method).to.equal('POST'); + + const data = JSON.parse(request[0].data); + expect(data.site).to.not.equal(null); + expect(data.imp).to.have.lengthOf(1); + expect(data.imp[0].bidfloor, data).to.not.be.null; + expect(data.imp[0].banner).to.not.equal(null); + expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); + expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); + }); + + it('should set user eids array', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest); + + const data = JSON.parse(request[0].data); + expect(data.user.ext.eids, data).to.not.be.null; + expect(data.user.ext.eids[0]).to.have.keys(['source', 'uids']); + }); + + it('should properly build a banner request with app params', function () { + const request = spec.buildRequests(validAppBidRequests, bidderRequest); + expect(request[0].url).to.match(/^(http|https):\/\/(.*)\.tappx\.com\/.+/); + expect(request[0].method).to.equal('POST'); + + const data = JSON.parse(request[0].data); + expect(data.site).to.not.equal(null); + expect(data.imp).to.have.lengthOf(1); + expect(data.imp[0].bidfloor, data).to.not.be.null; + expect(data.imp[0].banner).to.not.equal(null); + expect(data.imp[0].banner.w).to.be.oneOf([320, 50, 250, 480]); + expect(data.imp[0].banner.h).to.be.oneOf([320, 50, 250, 480]); + }); + }); + + /** + * INTERPRET RESPONSE TESTS + */ + describe('interpretResponse', function () { + it('receive banner reponse with single placement', function () { + const bids = spec.interpretResponse(c_SERVERRESPONSE_B, c_BIDDERREQUEST_B); + const bid = bids[0]; + expect(bid.cpm).to.exist; + expect(bid.ad).to.match(/^