From 667bbda82a49fcf83e81cfb8c22e07fa6b9c4cc0 Mon Sep 17 00:00:00 2001 From: Michele Nasti Date: Tue, 16 May 2023 17:13:58 +0200 Subject: [PATCH 01/26] RubiconBidAdapter: sync parseSize algorithm for isBidRequestValid and ortb conversion (#9957) * sync parseSize algorithm for isBidRequestValid and ortb conversion * use same sizes of parseSizes in video outstream * general test refactor to be less flacky --------- Co-authored-by: Michele Nasti --- modules/rubiconBidAdapter.js | 6 +- test/spec/modules/rubiconBidAdapter_spec.js | 142 +++++++++++--------- 2 files changed, 79 insertions(+), 69 deletions(-) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index 62ec61b3dcf..5c30b413780 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -208,8 +208,10 @@ export const converter = ortbConverter({ bidResponse.meta.mediaType = deepAccess(bid, 'ext.prebid.type'); const {bidRequest} = context; - bidResponse.width = bid.w || deepAccess(bidRequest, 'mediaTypes.video.w') || deepAccess(bidRequest, 'params.video.playerWidth') || bidResponse.playerWidth; - bidResponse.height = bid.h || deepAccess(bidRequest, 'mediaTypes.video.h') || deepAccess(bidRequest, 'params.video.playerHeight') || bidResponse.playerHeight; + let [parseSizeWidth, parseSizeHeight] = bidRequest.mediaTypes.video?.context === 'outstream' ? parseSizes(bidRequest, VIDEO) : [undefined, undefined]; + + bidResponse.width = bid.w || parseSizeWidth || bidResponse.playerWidth; + bidResponse.height = bid.h || parseSizeHeight || bidResponse.playerHeight; if (bidResponse.mediaType === VIDEO && bidRequest.mediaTypes.video.context === 'outstream') { bidResponse.renderer = outstreamRenderer(bidResponse); diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 4f1c0dd606a..0c8477a0fcb 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -192,6 +192,7 @@ describe('the rubicon adapter', function () { * @param {boolean} [gdprApplies] */ function createGdprBidderRequest(gdprApplies) { + const bidderRequest = getBidderRequest(); if (typeof gdprApplies === 'boolean') { bidderRequest.gdprConsent = { 'consentString': 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==', @@ -202,15 +203,16 @@ describe('the rubicon adapter', function () { 'consentString': 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==' }; } + return bidderRequest; } - function createUspBidderRequest() { + function addUspToBidderRequest(bidderRequest) { bidderRequest.uspConsent = '1NYN'; } function createVideoBidderRequest() { - createGdprBidderRequest(true); - createUspBidderRequest(); + const bidderRequest = createGdprBidderRequest(true); + addUspToBidderRequest(bidderRequest); let bid = bidderRequest.bids[0]; bid.mediaTypes = { @@ -260,9 +262,10 @@ describe('the rubicon adapter', function () { criteoId: '1111', }; bid.userIdAsEids = createEidsArray(bid.userId); + return bidderRequest; } - function createVideoBidderRequestNoVideo() { + function removeVideoParamFromBidderRequest(bidderRequest) { let bid = bidderRequest.bids[0]; bid.mediaTypes = { video: { @@ -273,7 +276,9 @@ describe('the rubicon adapter', function () { } function createVideoBidderRequestOutstream() { + const bidderRequest = createGdprBidderRequest(false); let bid = bidderRequest.bids[0]; + delete bid.sizes; bid.mediaTypes = { video: { context: 'outstream', @@ -291,17 +296,20 @@ describe('the rubicon adapter', function () { protocols: [1, 2, 3, 4, 5, 6] }, }; - bid.params.accountId = 14062; - bid.params.siteId = 70608; - bid.params.zoneId = 335918; - bid.params.video = { - 'language': 'en', - 'skip': 1, - 'skipafter': 15, - 'playerHeight': 320, - 'playerWidth': 640, - 'size_id': 203 - }; + bid.params = { + accountId: 14062, + siteId: 70608, + zoneId: 335918, + video: { + 'language': 'en', + 'skip': 1, + 'skipafter': 15, + 'playerHeight': 320, + 'playerWidth': 640, + 'size_id': 203 + } + } + return bidderRequest; } beforeEach(function () { @@ -744,7 +752,7 @@ describe('the rubicon adapter', function () { describe('GDPR consent config', function () { it('should send "gdpr" and "gdpr_consent", when gdprConsent defines consentString and gdprApplies', function () { - createGdprBidderRequest(true); + const bidderRequest = createGdprBidderRequest(true); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -753,7 +761,7 @@ describe('the rubicon adapter', function () { }); it('should send only "gdpr_consent", when gdprConsent defines only consentString', function () { - createGdprBidderRequest(); + const bidderRequest = createGdprBidderRequest(); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -770,12 +778,12 @@ describe('the rubicon adapter', function () { }); it('should set "gdpr" value as 1 or 0, using "gdprApplies" value of either true/false', function () { - createGdprBidderRequest(true); + let bidderRequest = createGdprBidderRequest(true); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); expect(data['gdpr']).to.equal('1'); - createGdprBidderRequest(false); + bidderRequest = createGdprBidderRequest(false); [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); data = parseQuery(request.data); expect(data['gdpr']).to.equal('0'); @@ -784,7 +792,7 @@ describe('the rubicon adapter', function () { describe('USP Consent', function () { it('should send us_privacy if bidderRequest has a value for uspConsent', function () { - createUspBidderRequest(); + addUspToBidderRequest(bidderRequest); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let data = parseQuery(request.data); @@ -1542,7 +1550,7 @@ describe('the rubicon adapter', function () { if (FEATURES.VIDEO) { describe('for video requests', function () { it('should make a well-formed video request', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 @@ -1628,7 +1636,7 @@ describe('the rubicon adapter', function () { }); it('should add ortb values to video requests', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 @@ -1661,7 +1669,7 @@ describe('the rubicon adapter', function () { }); it('should correctly set bidfloor on imp when getfloor in scope', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); // default getFloor response is empty object so should not break and not send hard_floor bidderRequest.bids[0].getFloor = () => getFloorResponse; sinon.spy(bidderRequest.bids[0], 'getFloor'); @@ -1706,7 +1714,7 @@ describe('the rubicon adapter', function () { }); it('should continue with auction if getFloor throws error', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); // default getFloor response is empty object so should not break and not send hard_floor bidderRequest.bids[0].getFloor = () => { throw new Error('An exception!'); @@ -1727,7 +1735,7 @@ describe('the rubicon adapter', function () { }); it('should add alias name to PBS Request', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); adapterManager.aliasRegistry['superRubicon'] = 'rubicon'; bidderRequest.bidderCode = 'superRubicon'; bidderRequest.bids[0].bidder = 'superRubicon'; @@ -1743,7 +1751,7 @@ describe('the rubicon adapter', function () { }); it('should add floors flag correctly to PBS Request', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); // should not pass if undefined @@ -1759,7 +1767,7 @@ describe('the rubicon adapter', function () { }); it('should add multibid configuration to PBS Request', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); const multibid = [{ bidder: 'bidderA', @@ -1786,7 +1794,7 @@ describe('the rubicon adapter', function () { }); it('should pass client analytics to PBS endpoint if all modules included', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); $$PREBID_GLOBAL$$.installedModules = []; let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let payload = request.data; @@ -1796,7 +1804,7 @@ describe('the rubicon adapter', function () { }); it('should pass client analytics to PBS endpoint if rubicon analytics adapter is included', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); $$PREBID_GLOBAL$$.installedModules = ['rubiconBidAdapter', 'rubiconAnalyticsAdapter']; let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let payload = request.data; @@ -1806,7 +1814,7 @@ describe('the rubicon adapter', function () { }); it('should not pass client analytics to PBS endpoint if rubicon analytics adapter is not included', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); $$PREBID_GLOBAL$$.installedModules = ['rubiconBidAdapter']; let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let payload = request.data; @@ -1815,7 +1823,7 @@ describe('the rubicon adapter', function () { }); it('should send video exp param correctly when set', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); config.setConfig({s2sConfig: {defaultTtl: 600}}); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let post = request.data; @@ -1826,7 +1834,7 @@ describe('the rubicon adapter', function () { }); it('should not send video exp at all if not set in s2sConfig config', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let post = request.data; @@ -1837,7 +1845,7 @@ describe('the rubicon adapter', function () { }); it('should send tmax as the bidderRequest timeout value', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); bidderRequest.timeout = 3333; let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); let post = request.data; @@ -1845,7 +1853,7 @@ describe('the rubicon adapter', function () { }); it('should send correct bidfloor to PBS', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); bidderRequest.bids[0].params.floor = 0.1; let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); @@ -1873,7 +1881,7 @@ describe('the rubicon adapter', function () { }); it('should send request with proper ad position', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); let positionBidderRequest = utils.deepClone(bidderRequest); positionBidderRequest.bids[0].mediaTypes.video.pos = 1; let [request] = spec.buildRequests(positionBidderRequest.bids, positionBidderRequest); @@ -1943,7 +1951,7 @@ describe('the rubicon adapter', function () { }); it('should enforce the new required mediaTypes.video params', function () { - createVideoBidderRequest(); + let bidderRequest = createVideoBidderRequest(); sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 @@ -1952,48 +1960,48 @@ describe('the rubicon adapter', function () { expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(true); // change mimes to a non array, no good - createVideoBidderRequest(); + bidderRequest = createVideoBidderRequest(); bidderRequest.bids[0].mediaTypes.video.mimes = 'video/mp4'; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); // delete mimes, no good - createVideoBidderRequest(); + bidderRequest = createVideoBidderRequest(); delete bidderRequest.bids[0].mediaTypes.video.mimes; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); // change protocols to an int not array of ints, no good - createVideoBidderRequest(); + bidderRequest = createVideoBidderRequest(); bidderRequest.bids[0].mediaTypes.video.protocols = 1; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); // delete protocols, no good - createVideoBidderRequest(); + bidderRequest = createVideoBidderRequest(); delete bidderRequest.bids[0].mediaTypes.video.protocols; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); // change linearity to an string, no good - createVideoBidderRequest(); + bidderRequest = createVideoBidderRequest(); bidderRequest.bids[0].mediaTypes.video.linearity = 'string'; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); // delete linearity, no good - createVideoBidderRequest(); + bidderRequest = createVideoBidderRequest(); delete bidderRequest.bids[0].mediaTypes.video.linearity; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); // change api to an string, no good - createVideoBidderRequest(); + bidderRequest = createVideoBidderRequest(); bidderRequest.bids[0].mediaTypes.video.api = 'string'; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); // delete api, no good - createVideoBidderRequest(); + bidderRequest = createVideoBidderRequest(); delete bidderRequest.bids[0].mediaTypes.video.api; expect(spec.isBidRequestValid(bidderRequest.bids[0])).to.equal(false); }); it('bid request is valid when video context is outstream', function () { - createVideoBidderRequestOutstream(); + const bidderRequest = createVideoBidderRequestOutstream(); sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 ); @@ -2035,7 +2043,7 @@ describe('the rubicon adapter', function () { }); it('should send request as banner when invalid video bid in multiple mediaType bidRequest', function () { - createVideoBidderRequestNoVideo(); + removeVideoParamFromBidderRequest(bidderRequest); let bid = bidderRequest.bids[0]; bid.mediaTypes.banner = { @@ -2054,7 +2062,7 @@ describe('the rubicon adapter', function () { }); it('should include coppa flag in video bid request', () => { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 @@ -2071,7 +2079,7 @@ describe('the rubicon adapter', function () { }); it('should include first party data', () => { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); const site = { ext: { @@ -2123,7 +2131,7 @@ describe('the rubicon adapter', function () { }); it('should include pbadslot in bid request', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); bidderRequest.bids[0].ortb2Imp = { ext: { data: { @@ -2141,7 +2149,7 @@ describe('the rubicon adapter', function () { }); it('should NOT include storedrequests in pbs payload', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); bidderRequest.bids[0].ortb2 = { ext: { prebid: { @@ -2168,7 +2176,7 @@ describe('the rubicon adapter', function () { }); it('should include GAM ad unit in bid request', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); bidderRequest.bids[0].ortb2Imp = { ext: { data: { @@ -2190,7 +2198,7 @@ describe('the rubicon adapter', function () { }); it('should use the integration type provided in the config instead of the default', () => { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); config.setConfig({rubicon: {int_type: 'testType'}}); const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest); expect(request.data.ext.prebid.bidders.rubicon.integration).to.equal('testType'); @@ -2198,7 +2206,7 @@ describe('the rubicon adapter', function () { it('should pass the user.id provided in the config', function () { config.setConfig({user: {id: '123'}}); - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); sandbox.stub(Date, 'now').callsFake(() => bidderRequest.auctionStart + 100 @@ -2321,13 +2329,13 @@ describe('the rubicon adapter', function () { describe('classifiedAsVideo', function () { it('should return true if mediaTypes is video', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); const bidClassifiedAsVideo = classifiedAsVideo(bidderRequest.bids[0]); expect(bidClassifiedAsVideo).is.equal(true); }); it('should return false if trying to use legacy mediaType with video', function () { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); delete bidderRequest.bids[0].mediaTypes; bidderRequest.bids[0].mediaType = 'video'; const legacyVideoTypeBidRequest = classifiedAsVideo(bidderRequest.bids[0]); @@ -2345,13 +2353,13 @@ describe('the rubicon adapter', function () { }); it('Should return false if both banner and video mediaTypes are set and params.video is not an object', function () { - createVideoBidderRequestNoVideo(); + removeVideoParamFromBidderRequest(bidderRequest); let bid = bidderRequest.bids[0]; bid.mediaTypes.banner = {flag: true}; expect(classifiedAsVideo(bid)).to.equal(false); }); it('Should return true if both banner and video mediaTypes are set and params.video is an object', function () { - createVideoBidderRequestNoVideo(); + removeVideoParamFromBidderRequest(bidderRequest); let bid = bidderRequest.bids[0]; bid.mediaTypes.banner = {flag: true}; bid.params.video = {}; @@ -2359,7 +2367,7 @@ describe('the rubicon adapter', function () { }); it('Should return true and create a params.video object if one is not already present', function () { - createVideoBidderRequestNoVideo(); + removeVideoParamFromBidderRequest(bidderRequest); let bid = bidderRequest.bids[0] expect(classifiedAsVideo(bid)).to.equal(true); expect(bid.params.video).to.not.be.undefined; @@ -3213,11 +3221,8 @@ describe('the rubicon adapter', function () { if (FEATURES.VIDEO) { describe('for video', function () { - beforeEach(function () { - createVideoBidderRequest(); - }); - it('should register a successful bid', function () { + const bidderRequest = createVideoBidderRequest(); let response = { cur: 'USD', seatbid: [{ @@ -3288,7 +3293,6 @@ describe('the rubicon adapter', function () { describe('for outstream video', function () { const sandbox = sinon.createSandbox(); beforeEach(function () { - createVideoBidderRequestOutstream(); config.setConfig({rubicon: { rendererConfig: { align: 'left', @@ -3309,6 +3313,7 @@ describe('the rubicon adapter', function () { }); it('should register a successful bid', function () { + const bidderRequest = createVideoBidderRequestOutstream(); let response = { cur: 'USD', seatbid: [{ @@ -3369,6 +3374,7 @@ describe('the rubicon adapter', function () { }); it('should render ad with Magnite renderer', function () { + const bidderRequest = createVideoBidderRequestOutstream(); let response = { cur: 'USD', seatbid: [{ @@ -3432,6 +3438,7 @@ describe('the rubicon adapter', function () { }); it('should render ad with Magnite renderer without video object', function () { + const bidderRequest = createVideoBidderRequestOutstream(); delete bidderRequest.bids[0].params.video; bidderRequest.bids[0].params.bidonmultiformat = true; bidderRequest.bids[0].mediaTypes.video.placement = 3; @@ -3749,7 +3756,7 @@ describe('the rubicon adapter', function () { }); it('should copy the schain JSON to to bid.source.ext.schain', () => { - createVideoBidderRequest(); + const bidderRequest = createVideoBidderRequest(); const schain = getSupplyChainConfig(); bidderRequest.bids[0].schain = schain; const request = spec.buildRequests(bidderRequest.bids, bidderRequest); @@ -3785,12 +3792,13 @@ describe('the rubicon adapter', function () { }); // banner - let [bannerRequest] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const bannerBidderRequest = createGdprBidderRequest(false); + let [bannerRequest] = spec.buildRequests(bannerBidderRequest.bids, bannerBidderRequest); expect(bannerRequest.url).to.equal('https://fastlane-qa.rubiconproject.com/a/api/fastlane.json'); // video and returnVast - createVideoBidderRequest(); - let [videoRequest] = spec.buildRequests(bidderRequest.bids, bidderRequest); + const videoBidderRequest = createVideoBidderRequest(); + let [videoRequest] = spec.buildRequests(videoBidderRequest.bids, videoBidderRequest); let post = videoRequest.data; expect(videoRequest.url).to.equal('https://prebid-server-qa.rubiconproject.com/openrtb2/auction'); expect(post.ext.prebid.cache.vastxml).to.have.property('returnCreative').that.is.an('boolean'); From 92daa81f277598cbed486cf8be01ce796aa80c8f Mon Sep 17 00:00:00 2001 From: radubarbos Date: Wed, 17 May 2023 08:37:55 +0300 Subject: [PATCH 02/26] Yahoo SSP adapter support for extra site publisher info. (#9921) * Yahoo SSP adapter support for extra site publisher info. * Yahoo SSP adapter support for extra site publisher info - docs. --------- Co-authored-by: dumitrubarbos --- modules/yahoosspBidAdapter.js | 6 ++ modules/yahoosspBidAdapter.md | 37 ++++++++++++ test/spec/modules/yahoosspBidAdapter_spec.js | 61 +++++++++++++++++++- 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js index 40677f90064..249a088ed1c 100644 --- a/modules/yahoosspBidAdapter.js +++ b/modules/yahoosspBidAdapter.js @@ -380,6 +380,7 @@ function appendFirstPartyData(outBoundBidRequest, bid) { const ortb2Object = bid.ortb2; const siteObject = deepAccess(ortb2Object, 'site') || undefined; const siteContentObject = deepAccess(siteObject, 'content') || undefined; + const sitePublisherObject = deepAccess(siteObject, 'publisher') || undefined; const siteContentDataArray = deepAccess(siteObject, 'content.data') || undefined; const appContentObject = deepAccess(ortb2Object, 'app.content') || undefined; const appContentDataArray = deepAccess(ortb2Object, 'app.content.data') || undefined; @@ -394,6 +395,11 @@ function appendFirstPartyData(outBoundBidRequest, bid) { outBoundBidRequest.site = validateAppendObject('object', allowedSiteObjectKeys, siteObject, outBoundBidRequest.site); }; + if (sitePublisherObject && isPlainObject(sitePublisherObject)) { + const allowedPublisherObjectKeys = ['ext']; + outBoundBidRequest.site.publisher = validateAppendObject('object', allowedPublisherObjectKeys, sitePublisherObject, outBoundBidRequest.site.publisher); + } + if (siteContentObject && isPlainObject(siteContentObject)) { const allowedContentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language']; const allowedContentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; diff --git a/modules/yahoosspBidAdapter.md b/modules/yahoosspBidAdapter.md index 7fb7a307192..e0d1b365acd 100644 --- a/modules/yahoosspBidAdapter.md +++ b/modules/yahoosspBidAdapter.md @@ -435,6 +435,43 @@ pbjs.setConfig({ } }); ``` + +Notes: The first party site info is filtered and only the following specific keys are allowed in the bidRequests: + +| Field | Type | +|-----------------------------|--------| +| site.name | String | +| site.domain | String | +| site.page | String | +| site.ref | String | +| site.keywords | String | +| site.search | String | +| site.cat | Array | +| site.sectioncat | Array | +| site.pagecat | Array | +| site.ext | Object | +| site.publisher.ext | Object | +| site.content.id | String | +| site.content.title | String | +| site.content.series | String | +| site.content.season | String | +| site.content.genre | String | +| site.content.contentrating | String | +| site.content.language | String | +| site.content.episode | Number | +| site.content.prodq | Number | +| site.content.context | Number | +| site.content.livestream | Number | +| site.content.len | Number | +| site.content.cat | Array | +| site.content.ext | Object | +| site.content.data | Array | +| site.content.data[].id | String | +| site.content.data[].name | String | +| site.content.data[].segment | Array | +| site.content.data[].ext | Object | + + ### Passing First Party "user" data: ```javascript pbjs.setConfig({ diff --git a/test/spec/modules/yahoosspBidAdapter_spec.js b/test/spec/modules/yahoosspBidAdapter_spec.js index a326b22ecb4..f558089b7b0 100644 --- a/test/spec/modules/yahoosspBidAdapter_spec.js +++ b/test/spec/modules/yahoosspBidAdapter_spec.js @@ -481,6 +481,23 @@ describe('YahooSSP Bid Adapter:', () => { }); }); + const VALID_PUBLISHER_OBJECTS = ['ext']; + VALID_PUBLISHER_OBJECTS.forEach(param => { + it(`should determine that the ortb2.site.publisher Object key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const ortb2 = { + site: { + publisher: { + [param]: {a: '123', b: '456'} + } + } + }; + const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.site.publisher[param]).to.be.a('object'); + expect(data.site.publisher[param]).to.be.equal(ortb2.site.publisher[param]); + }); + }); + const VALID_CONTENT_ARRAYS = ['cat']; VALID_CONTENT_ARRAYS.forEach(param => { it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { @@ -512,7 +529,6 @@ describe('YahooSSP Bid Adapter:', () => { const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.site.content[param]).to.be.a('object'); expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); - config.setConfig({ortb2: {}}); }); }); }); @@ -959,6 +975,49 @@ describe('YahooSSP Bid Adapter:', () => { expect(data.site.id).to.equal('1234567'); }); + it('should use site publisher ortb2 config in default integration mode', () => { + const ortb2 = { + site: { + publisher: { + ext: { + publisherblob: 'pblob', + bucket: 'bucket' + } + } + } + } + let { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.site.publisher).to.deep.equal({ + ext: { + publisherblob: 'pblob', + bucket: 'bucket' + } + }); + }); + + it('should use site publisher ortb2 config when using "pubId" integration mode', () => { + const ortb2 = { + site: { + publisher: { + ext: { + publisherblob: 'pblob', + bucket: 'bucket' + } + } + } + } + let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true, ortb2}); + const data = spec.buildRequests(validBidRequests, bidderRequest).data; + expect(data.site.publisher).to.deep.equal({ + id: DEFAULT_PUBID, + ext: { + publisherblob: 'pblob', + bucket: 'bucket' + } + }); + }); + it('should use placementId value as imp.tagid in the outbound bid-request when using "pubId" integration mode', () => { let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true}); validBidRequests[0].params.placementId = 'header-300x250'; From eeab092d7642cde5bf70f5620155452631ffa56f Mon Sep 17 00:00:00 2001 From: Pgb-Criteo <92986445+Pgb-Criteo@users.noreply.github.com> Date: Wed, 17 May 2023 23:22:51 +0200 Subject: [PATCH 03/26] Criteo Bid Adapter: add support for imp.rwdd (#9964) --- modules/criteoBidAdapter.js | 5 ++ test/spec/modules/criteoBidAdapter_spec.js | 67 ++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index edb718ec953..63c4482ab84 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -438,6 +438,11 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { if (deepAccess(bidRequest, 'ortb2Imp.ext')) { slot.ext = bidRequest.ortb2Imp.ext; } + + if (deepAccess(bidRequest, 'ortb2Imp.rwdd')) { + slot.rwdd = bidRequest.ortb2Imp.rwdd; + } + if (bidRequest.params.ext) { slot.ext = Object.assign({}, slot.ext, bidRequest.params.ext); } diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index d88e70a6cdc..17527eb42c7 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1579,6 +1579,73 @@ describe('The Criteo bidding adapter', function () { } }); }); + + it('should properly build a request when imp.rwdd is present', function () { + const bidderRequest = {}; + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: { + zoneId: 123, + ext: { + bidfloor: 0.75 + } + }, + ortb2Imp: { + rwdd: 1, + ext: { + data: { + someContextAttribute: 'abc' + } + } + } + }, + ]; + + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.slots[0].rwdd).to.be.not.null; + expect(request.data.slots[0].rwdd).to.equal(1); + }); + + it('should properly build a request when imp.rwdd is false', function () { + const bidderRequest = {}; + const bidRequests = [ + { + bidder: 'criteo', + adUnitCode: 'bid-123', + transactionId: 'transaction-123', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + params: { + zoneId: 123, + ext: { + bidfloor: 0.75 + } + }, + ortb2Imp: { + rwdd: 0, + ext: { + data: { + someContextAttribute: 'abc' + } + } + } + }, + ]; + + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.slots[0].rwdd).to.be.undefined; + }); }); describe('interpretResponse', function () { From 3cdbd1de50ae02012f3714fd9558365b3418aa36 Mon Sep 17 00:00:00 2001 From: denys-berzoy-confiant <50574764+denys-berzoy-confiant@users.noreply.github.com> Date: Thu, 18 May 2023 00:25:19 +0300 Subject: [PATCH 04/26] confiant Rtd Provider : add message type check (#9950) * confiantRtdProvider: add message type check - added check for message type and propertyId * rm unused variable * fix test --- modules/confiantRtdProvider.js | 27 ++++++++++--------- test/spec/modules/confiantRtdProvider_spec.js | 5 +++- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/modules/confiantRtdProvider.js b/modules/confiantRtdProvider.js index 2c13ad87d75..6b1066a20f1 100644 --- a/modules/confiantRtdProvider.js +++ b/modules/confiantRtdProvider.js @@ -44,7 +44,7 @@ function setupPage(config) { if (config?.params?.shouldEmitBillableEvent) { if (window.frames['cnftComm']) { - subscribeToConfiantCommFrame(window); + subscribeToConfiantCommFrame(window, propertyId); } else { setUpMutationObserver(); } @@ -58,8 +58,8 @@ function setupPage(config) { * Subscribe to window's message events to report Billable events * @param {Window} targetWindow window instance to subscribe to */ -function subscribeToConfiantCommFrame(targetWindow) { - targetWindow.addEventListener('message', reportBillableEvents); +function subscribeToConfiantCommFrame(targetWindow, propertyId) { + targetWindow.addEventListener('message', getEventHandlerFunction(propertyId)); } let mutationObserver; @@ -86,14 +86,18 @@ function setUpMutationObserver() { /** * Emit billable event when Confiant integration reports that it has monitored an impression */ -function reportBillableEvents (e) { - events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { - auctionId: e.data.auctionId, - billingId: generateUUID(), - transactionId: e.data.transactionId, - type: 'impression', - vendor: 'confiant' - }); +function getEventHandlerFunction(propertyId) { + return function reportBillableEvent(e) { + if (e.data.type.indexOf('cnft:reportBillableEvent:' + propertyId) > -1) { + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + auctionId: e.data.auctionId, + billingId: generateUUID(), + transactionId: e.data.transactionId, + type: 'impression', + vendor: 'confiant' + }); + } + } } /** @@ -123,6 +127,5 @@ export default { setupPage, subscribeToConfiantCommFrame, setUpMutationObserver, - reportBillableEvents, registerConfiantSubmodule }; diff --git a/test/spec/modules/confiantRtdProvider_spec.js b/test/spec/modules/confiantRtdProvider_spec.js index 987aca2e020..8f9fcd0ba98 100644 --- a/test/spec/modules/confiantRtdProvider_spec.js +++ b/test/spec/modules/confiantRtdProvider_spec.js @@ -46,6 +46,7 @@ describe('Confiant RTD module', function () { let listenerCallback; const mockWindow = { addEventListener: (a, cb) => (listenerCallback = cb) }; let billableEventsCounter = 0; + const propertyId = 'fff'; events.on(CONSTANTS.EVENTS.BILLABLE_EVENT, (e) => { if (e.vendor === 'confiant') { @@ -57,15 +58,17 @@ describe('Confiant RTD module', function () { } }); - subscribeToConfiantCommFrame(mockWindow); + subscribeToConfiantCommFrame(mockWindow, propertyId); listenerCallback({ data: { + type: 'cnft:reportBillableEvent:' + propertyId, auctionId: 'auctionId', transactionId: 'transactionId' } }); listenerCallback({ data: { + type: 'cnft:reportBillableEvent:' + propertyId, auctionId: 'auctionId', transactionId: 'transactionId' } From a205c6c893b3bd469377c1bb655a9ca2090901d2 Mon Sep 17 00:00:00 2001 From: anthonyjl92 Date: Wed, 17 May 2023 17:33:32 -0400 Subject: [PATCH 05/26] Add mobile client hint 33x adapter (#9958) Co-authored-by: Carlos Felix --- modules/33acrossBidAdapter.js | 2 +- test/spec/modules/33acrossBidAdapter_spec.js | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index e9901794ff9..606933e7b63 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -765,7 +765,7 @@ function _buildDeviceORTB(device = {}) { } if (device.sua) { - deviceProps.sua = pick(device.sua, [ 'browsers', 'platform', 'model' ]); + deviceProps.sua = pick(device.sua, [ 'browsers', 'platform', 'model', 'mobile' ]); } return deviceProps; diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index 3b3c05660df..3702a34fbb8 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -53,7 +53,8 @@ describe('33acrossBidAdapter:', function () { brand: 'macOS', version: ['11', '6', '8'] }, - model: '' + model: '', + mobile: 0 } }, id: 'r1', @@ -337,7 +338,7 @@ describe('33acrossBidAdapter:', function () { version: ['11', '6', '8'] }, model: '', - mobile: false + mobile: 0 } } } @@ -369,7 +370,7 @@ describe('33acrossBidAdapter:', function () { version: ['11', '6', '8'] }, model: '', - mobile: false + mobile: 0 } } }, From 4b62ff513ca5a70771783c5518c2f585438e77fa Mon Sep 17 00:00:00 2001 From: Aaron Price <67345931+AaronColbyPrice@users.noreply.github.com> Date: Wed, 17 May 2023 15:59:35 -0700 Subject: [PATCH 06/26] eps_aliasing - adding in aliases for epsilon (formerly conversant) for bidder and analytics (#9961) Co-authored-by: aarprice@publicisgroupe.net --- modules/conversantAnalyticsAdapter.js | 12 +++++++----- modules/conversantBidAdapter.js | 2 +- test/spec/modules/conversantBidAdapter_spec.js | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/modules/conversantAnalyticsAdapter.js b/modules/conversantAnalyticsAdapter.js index e934f1bb153..ce4597eecfe 100644 --- a/modules/conversantAnalyticsAdapter.js +++ b/modules/conversantAnalyticsAdapter.js @@ -18,6 +18,7 @@ const DOMAIN = 'https://web.hb.ad.cpe.dotomi.com/'; const ANALYTICS_URL = DOMAIN + 'cvx/event/prebidanalytics'; const ERROR_URL = DOMAIN + 'cvx/event/prebidanalyticerrors'; const ANALYTICS_CODE = 'conversant'; +const ANALYTICS_ALIASES = [ANALYTICS_CODE, 'epsilon', 'cnvr']; export const CNVR_CONSTANTS = { LOG_PREFIX: 'Conversant analytics adapter: ', @@ -688,11 +689,12 @@ conversantAnalytics.disableAnalytics = function () { conversantAnalyticsEnabled = false; conversantAnalytics.originDisableAnalytics(); }; - -adapterManager.registerAnalyticsAdapter({ - adapter: conversantAnalytics, - code: ANALYTICS_CODE, - gvlid: GVLID +ANALYTICS_ALIASES.forEach(alias => { + adapterManager.registerAnalyticsAdapter({ + adapter: conversantAnalytics, + code: alias, + gvlid: GVLID + }); }); export default conversantAnalytics; diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index e17a9fe4021..b5a1424dcba 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -12,7 +12,7 @@ const URL = 'https://web.hb.ad.cpe.dotomi.com/cvx/client/hb/ortb/25'; export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: ['cnvr'], // short code + aliases: ['cnvr', 'epsilon'], // short code supportedMediaTypes: [BANNER, VIDEO], /** diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index bda5d8daca7..bf02a2893f0 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -221,8 +221,9 @@ describe('Conversant adapter tests', function() { it('Verify basic properties', function() { expect(spec.code).to.equal('conversant'); - expect(spec.aliases).to.be.an('array').with.lengthOf(1); + expect(spec.aliases).to.be.an('array').with.lengthOf(2); expect(spec.aliases[0]).to.equal('cnvr'); + expect(spec.aliases[1]).to.equal('epsilon'); expect(spec.supportedMediaTypes).to.be.an('array').with.lengthOf(2); expect(spec.supportedMediaTypes[1]).to.equal('video'); }); From 4d998deee44fd9af56618816e928b32ec4a5a878 Mon Sep 17 00:00:00 2001 From: Kevin Park <109548315+kevpark02@users.noreply.github.com> Date: Wed, 17 May 2023 16:15:31 -0700 Subject: [PATCH 07/26] Underdog Media Bid Adapter: Update ttl & referer information (#9826) * Update ttl and referer info in underdogmediaBidAdapter * Restore hello world default * Fix the unit test for referer info * undo change to package lock * undo change to package lock * revert change to package-lock.json --------- Co-authored-by: Jake --- modules/underdogmediaBidAdapter.js | 4 ++-- test/spec/modules/underdogmediaBidAdapter_spec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/underdogmediaBidAdapter.js b/modules/underdogmediaBidAdapter.js index bdc5c04279f..e8494119b62 100644 --- a/modules/underdogmediaBidAdapter.js +++ b/modules/underdogmediaBidAdapter.js @@ -74,7 +74,7 @@ export const spec = { pbTimeout: config.getConfig('bidderTimeout'), pbjsVersion: prebidVersion, placements: [], - ref: deepAccess(bidderRequest, 'refererInfo.ref') ? bidderRequest.refererInfo.ref : undefined, + ref: deepAccess(bidderRequest, 'refererInfo.page') ? bidderRequest.refererInfo.page : undefined, usp: {}, userIds: { '33acrossId': deepAccess(validBidRequests[0], 'userId.33acrossId.envelope') ? validBidRequests[0].userId['33acrossId'].envelope : undefined, @@ -193,7 +193,7 @@ export const spec = { creativeId: mid.mid, currency: 'USD', netRevenue: false, - ttl: mid.ttl || 60, + ttl: mid.ttl || 300, meta: { advertiserDomains: mid.advertiser_domains || [] } diff --git a/test/spec/modules/underdogmediaBidAdapter_spec.js b/test/spec/modules/underdogmediaBidAdapter_spec.js index 4201ecc2329..040d85f9179 100644 --- a/test/spec/modules/underdogmediaBidAdapter_spec.js +++ b/test/spec/modules/underdogmediaBidAdapter_spec.js @@ -735,7 +735,7 @@ describe('UnderdogMedia adapter', function () { }, }, refererInfo: { - ref: 'www.example.com' + page: 'www.example.com' } } From 425cc5601ddfbab561a1991a37d84fc619bc95ec Mon Sep 17 00:00:00 2001 From: Viktor Dreiling <34981284+3link@users.noreply.github.com> Date: Thu, 18 May 2023 01:24:49 +0200 Subject: [PATCH 08/26] LiveIntent UserId module: add support for distributorId configuration parameter (#9963) * Allow to configure distributorId * Add test for did being present in track events * Compute source only once --------- Co-authored-by: Viktor Dreiling Co-authored-by: Viktor Dreiling --- modules/liveIntentIdSystem.js | 12 +++-- .../modules/liveIntentIdMinimalSystem_spec.js | 28 ++++++++++++ test/spec/modules/liveIntentIdSystem_spec.js | 45 +++++++++++++++++++ 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index 3ecd061085c..3157a4c155e 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -92,21 +92,25 @@ function initializeLiveConnect(configParams) { const publisherId = configParams.publisherId || 'any'; const identityResolutionConfig = { - source: 'prebid', publisherId: publisherId, requestedAttributes: parseRequestedAttributes(configParams.requestedAttributesOverrides) }; if (configParams.url) { identityResolutionConfig.url = configParams.url } - if (configParams.partner) { - identityResolutionConfig.source = configParams.partner - } if (configParams.ajaxTimeout) { identityResolutionConfig.ajaxTimeout = configParams.ajaxTimeout; } const liveConnectConfig = parseLiveIntentCollectorConfig(configParams.liCollectConfig); + + if (!liveConnectConfig.appId && configParams.distributorId) { + liveConnectConfig.distributorId = configParams.distributorId; + identityResolutionConfig.source = configParams.distributorId; + } else { + identityResolutionConfig.source = configParams.partner || 'prebid' + } + liveConnectConfig.wrapperName = 'prebid'; liveConnectConfig.identityResolutionConfig = identityResolutionConfig; liveConnectConfig.identifiersToResolve = configParams.identifiersToResolve || []; diff --git a/test/spec/modules/liveIntentIdMinimalSystem_spec.js b/test/spec/modules/liveIntentIdMinimalSystem_spec.js index 6a5afa58da2..5640a6d36cb 100644 --- a/test/spec/modules/liveIntentIdMinimalSystem_spec.js +++ b/test/spec/modules/liveIntentIdMinimalSystem_spec.js @@ -73,6 +73,34 @@ describe('LiveIntentMinimalId', function() { expect(callBackSpy.calledOnce).to.be.true; }); + it('should call the Identity Exchange endpoint with the privided distributorId', function() { + getCookieStub.returns(null); + let callBackSpy = sinon.spy(); + let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111' } }).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://idx.liadm.com/idex/did-1111/any?did=did-1111&resolve=nonId'); + request.respond( + 204, + responseHeader + ); + expect(callBackSpy.calledOnceWith({})).to.be.true; + }); + + it('should call the Identity Exchange endpoint without the privided distributorId when appId is provided', function() { + getCookieStub.returns(null); + let callBackSpy = sinon.spy(); + let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111', liCollectConfig: { appId: 'a-0001' } } }).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/any?resolve=nonId'); + request.respond( + 204, + responseHeader + ); + expect(callBackSpy.calledOnceWith({})).to.be.true; + }); + it('should call the default url of the LiveIntent Identity Exchange endpoint, with a partner', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); diff --git a/test/spec/modules/liveIntentIdSystem_spec.js b/test/spec/modules/liveIntentIdSystem_spec.js index afbd1566438..859db423eac 100644 --- a/test/spec/modules/liveIntentIdSystem_spec.js +++ b/test/spec/modules/liveIntentIdSystem_spec.js @@ -100,6 +100,23 @@ describe('LiveIntentId', function() { }, 200); }); + it('should fire an event with the provided distributorId', function (done) { + liveIntentIdSubmodule.decode({}, { params: { fireEventDelay: 1, distributorId: 'did-1111' } }); + setTimeout(() => { + expect(server.requests[0].url).to.match(/https:\/\/rp.liadm.com\/j\?.*did=did-1111.*&wpn=prebid.*/); + done(); + }, 200); + }); + + it('should fire an event without the provided distributorId when appId is provided', function (done) { + liveIntentIdSubmodule.decode({}, { params: { fireEventDelay: 1, distributorId: 'did-1111', liCollectConfig: { appId: 'a-0001' } } }); + setTimeout(() => { + expect(server.requests[0].url).to.match(/https:\/\/rp.liadm.com\/j\?.*aid=a-0001.*&wpn=prebid.*/); + expect(server.requests[0].url).to.not.match(/.*did=*/); + done(); + }, 200); + }); + it('should initialize LiveConnect and emit an event with a privacy string when decode', function(done) { uspConsentDataStub.returns('1YNY'); gdprConsentDataStub.returns({ @@ -162,6 +179,34 @@ describe('LiveIntentId', function() { expect(callBackSpy.calledOnceWith({})).to.be.true; }); + it('should call the Identity Exchange endpoint with the privided distributorId', function() { + getCookieStub.returns(null); + let callBackSpy = sinon.spy(); + let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111' } }).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://idx.liadm.com/idex/did-1111/any?did=did-1111&resolve=nonId'); + request.respond( + 204, + responseHeader + ); + expect(callBackSpy.calledOnceWith({})).to.be.true; + }); + + it('should call the Identity Exchange endpoint without the privided distributorId when appId is provided', function() { + getCookieStub.returns(null); + let callBackSpy = sinon.spy(); + let submoduleCallback = liveIntentIdSubmodule.getId({ params: { fireEventDelay: 1, distributorId: 'did-1111', liCollectConfig: { appId: 'a-0001' } } }).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.be.eq('https://idx.liadm.com/idex/prebid/any?resolve=nonId'); + request.respond( + 204, + responseHeader + ); + expect(callBackSpy.calledOnceWith({})).to.be.true; + }); + it('should call the default url of the LiveIntent Identity Exchange endpoint, with a partner', function() { getCookieStub.returns(null); let callBackSpy = sinon.spy(); From ed726c788cb1ba4dcdff6407dabc73d1210de4ec Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Thu, 18 May 2023 08:04:04 -0300 Subject: [PATCH 09/26] Docs Integration Examples : add Events UI page for Video Module (#9934) * adds UI for events * improves visuals * removes duplicate event * adds title * fixes scoping var bug * adds videojs page --- .../videoModule/eventsUI/eventsUI.css | 205 +++++++++++ .../videoModule/eventsUI/eventsUI.js | 181 ++++++++++ .../videoModule/jwplayer/eventsUI.html | 295 ++++++++++++++++ .../videoModule/videojs/eventsUI.html | 319 ++++++++++++++++++ 4 files changed, 1000 insertions(+) create mode 100644 integrationExamples/videoModule/eventsUI/eventsUI.css create mode 100644 integrationExamples/videoModule/eventsUI/eventsUI.js create mode 100644 integrationExamples/videoModule/jwplayer/eventsUI.html create mode 100644 integrationExamples/videoModule/videojs/eventsUI.html diff --git a/integrationExamples/videoModule/eventsUI/eventsUI.css b/integrationExamples/videoModule/eventsUI/eventsUI.css new file mode 100644 index 00000000000..ccfe17ca4c6 --- /dev/null +++ b/integrationExamples/videoModule/eventsUI/eventsUI.css @@ -0,0 +1,205 @@ +html { + width: 100%; +} +body { + background: #ccc; + margin: 20px 5px; + font: 42px/60px Helvetica, Arial, sans-serif; + min-width: 320px; + max-width: 1920px; +} +#player:first-child { + width: 100%; +} + +#player { + margin: auto; + background: rgba(0, 0, 20, 0.8); +} +#eventsLog.group-player-disabled .group-player, +#eventsLog.group-media-disabled .group-media, +#eventsLog.group-ads-disabled .group-ads, +#eventsLog.group-auction-disabled .group-auction, +#eventsLog.group-adRequest-disabled .group-adRequest, +#eventsLog.group-adBreak-disabled .group-adBreak, +#eventsLog.group-related-disabled .group-related, +#eventsLog.group-ping-disabled .group-ping, +#eventsLog.group-unknown-disabled .group-unknown, +#eventsLog.group-quickPeek-disabled .group-quickPeek, +#eventsLog.group-provider-disabled .group-provider, +#eventsLog.group-video-disabled .group-video { + display: none; +} +.input-field { + padding: 0 0.25em; + margin: 0 0 0 4px; + border: 0; + background-color: #232323; + color: #F8F8F8; +} +.input-field:invalid { + text-decoration: underline; + text-decoration-color: red; +} +#eventsLog .sequence > .pre.filter-not-matched { + display: none; + opacity: 0.2; +} +a.button { + -webkit-appearance: button; + cursor: pointer; + margin: 2px; + background: #ccc; + border-color: rgb(216, 216, 216) rgb(209, 209, 209) rgb(186, 186, 186); + border-style: solid; + border-width: 1px; + padding: 1px 7px 2px; + color: inherit; + text-decoration: inherit; + user-select: none; +} +.right { + float: right; +} +.disabled { + opacity: 0.5; +} +.nav.disabled { + user-select: none; + cursor: default; +} +.block { + margin: 5px; + background-color: #eee; +} +div.mode-player { + background: #acc; +} +div.mode-ads { + background: #fea; +} +div.sequence { + display: block; +} +.group-player { + background: #acc; +} +.group-media { + background: #fa6; +} +.group-ads { + background: #fcc; +} +.group-auction { + background: #228; + color: #eee; +} +pre { + margin: 0; +} +div.toggle-block pre { + margin: 1px 3px; +} +.events-block { + min-height: 1440px; +} +.events-block .pre, +.toggle-block pre { + display: inline-block; + padding: 0 5px; + margin: 1px 3px 1px 20px; + border-radius: 5px; + font-family: monospace; + white-space: pre; +} +.pre.group-ads, +.pre.group-media, +.pre.group-player, +.pre.group-auction, +.pre.event-ready { + display: inline-block; + padding: 0 5px; + margin: 1px 3px 1px 20px; + border-radius: 5px; + font-family: monospace; + white-space: pre; +} + +.list-events.events-block .pre.group-player, +.list-events.events-block .pre.group-media, +.list-events.events-block .pre.group-ads, +.list-events.events-block .pre.group-auction { + display: block; + margin: 0; + padding: 1px 10px; + border-radius: 0; +} + +.pre.event-playlistItem { + margin: 1px 3px 1px 10px; +} +.pre.event-adBreakStart, +.pre.event-adBreakEnd { + margin: 1px 3px 1px 20px; +} +.pre.event-adBreakStart, +.pre.event-ready, +.pre.event-adImpression, +.pre.event-adError, +.pre.event-adWarning { + font-weight: 800; +} +.pre pre { + display: inline; + margin: 0 0 0 20px; +} +.toggle { + cursor: pointer; + user-select: none; +} +button, +input, +optgroup, +select, +textarea { + color: inherit; + font: inherit; +} +button { + overflow: visible; + padding: 1px 7px 2px; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; + margin: 2px; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} + +@media only screen and (min-device-width : 320px) and (max-device-width : 768px) { + body { + font-size: 2vw; + line-height: 3vw; + } +} diff --git a/integrationExamples/videoModule/eventsUI/eventsUI.js b/integrationExamples/videoModule/eventsUI/eventsUI.js new file mode 100644 index 00000000000..618a4774765 --- /dev/null +++ b/integrationExamples/videoModule/eventsUI/eventsUI.js @@ -0,0 +1,181 @@ +"use strict"; + +let sequenceCount = 0; +const eventLogGroups = {}; +const Uint8Array = window.Uint8Array; +const TimeRanges = window.TimeRanges; + +function stringify(value, replacer, space) { + try { + return truncate(JSON.stringify(value, replacer || stringifyReplacer(value), space), 100000); + } catch (error) { + return `[${error}]`; + } +} + +function truncate(str, length) { + return (str && str.length) > length ? (str.substr(0, length) + + '\n... Event truncated due to length (see console for complete output)') : str; +} + +function stringifyReplacer(parentValue) { + const references = []; + const safeResults = []; + let complexity = 0; + return function stringifyKeyValue(key, value) { + if (typeof value === 'object') { + if (value === null || value instanceof Date || value instanceof RegExp) { + return value; + } + if (!!Uint8Array && value instanceof Uint8Array) { + // Stub values of Arrays with more than 1000 items + let str = ('' + value); + str = (str.length > 40 ? (str.substr(0, 40) + '...(see console)') : str); + return `Uint8Array(${value.length}) [${str}]`; + } + if (!!TimeRanges && value instanceof TimeRanges) { + const ranges = []; + for (let i = 0; i < value.length; i++) { + ranges[i] = `start(${i}) = ${value.start(i)} end(${i}) = ${value.end(i)}`; + } + return `TimeRanges(${value.length}) [${ranges}]`; + } + if (value === parentValue && complexity > 0) { + return ''; + } + const referenceIndex = references.indexOf(value); + if (referenceIndex !== -1) { + // Duplicate reference found + const safe = safeResults[referenceIndex]; + if (safe) { + return safe; + } + try { + // Test for circular references + JSON.stringify(value); + } catch (error) { + return (safeResults[referenceIndex] = '<' + value + '...(see console)>'); + } + safeResults[referenceIndex] = value; + } + if (complexity++ > 10000) { + return ''; + } + references.push(value); + return value; + } + if (typeof value === 'function') { + return `${value}`; + } + return value; + }; +} + +function createEventSequenceElement(eventGroup) { + const element = document.createElement('div'); + element.classList.add('sequence', `mode-${eventGroup}`); + element.setAttribute('data-sequence', `${sequenceCount++}`); + return element; +} + +function appendSequenceElement(container, element) { + container.appendChild(element); +} + +function textContentGrouped(inEvent, group) { + if (group) { + return `${inEvent} (${group[inEvent]})`; + } + return inEvent; +} + +function appendEvent(container, currentEventType, currentEventGroup, data) { + const div = document.createElement('div'); + div.classList.add('group-' + currentEventGroup, 'event-' + currentEventType, 'pre'); + div.textContent = textContentGrouped(currentEventType); + div.setAttribute('title', `${currentEventGroup} event "${currentEventType}"`); + div.setAttribute('tabindex', '0'); + const theData = Object.assign({}, data); + div.onclick = div.onkeyup = function(e) { + if (e && e.keyCode && e.keyCode !== 13) { + return; + } + + console.log(theData); + div.textContent = ((div.expanded = !div.expanded)) ? + textContentExpanded(currentEventType, [theData]) : textContentGrouped(currentEventType); + if (e) { + e.preventDefault(); + } + return [theData]; + }; + container.appendChild(div); + return div; +} + +function textContentExpanded(inEvent, allData) { + return `${inEvent} (${allData.map((item, i) => + (allData.length > 1 ? `[${i}] = ` : '') + stringify(item, null, 4)).join('\n')})`; +} + +function incrementEvent(group, currentEventType, currentEventGroup, div, datum) { + group[currentEventType]++; + div.textContent = textContentGrouped(currentEventType, group); + const logPreviousEvents = div.onclick; + const scopedDatum = Object.assign({}, datum); + div.onclick = div.onkeyup = function(e) { + if (e && e.keyCode && e.keyCode !== 13) { + return; + } + + const allData = logPreviousEvents(); + allData.push(scopedDatum); + console.log(scopedDatum); + div.textContent = (div.expanded) ? textContentExpanded(currentEventType, allData) : textContentGrouped(currentEventType, group); + if (e) { + e.preventDefault(); + } + return allData; + }; +} + +function getGenericEventHandler() { + const logContainer = document.querySelector('#eventsLog'); + let currentEventGroup = ''; + let currentEventType = ''; + let lastEvent = ''; + let lastGroup; + const genericEventHandler = function(e, type, eventGroup) { + currentEventGroup = eventGroup; + currentEventType = type; + + let group = eventLogGroups[eventGroup]; + if (!group || group !== lastGroup) { + const beforeReadyElement = createEventSequenceElement(currentEventGroup); + appendSequenceElement(logContainer, beforeReadyElement); + group = eventLogGroups[currentEventGroup] = { + eventGroup: currentEventGroup, + event: currentEventType, + container: logContainer, + eventElement: beforeReadyElement + }; + lastGroup = lastGroup || group; + } + if (lastEvent === currentEventType && !(/^(?:meta|hlsBufferAppend)/).test(currentEventType)) { + incrementEvent(group, currentEventType, currentEventGroup, group.pre, e); + } else { + const eventElement = createEventSequenceElement(currentEventGroup); + group[currentEventType] = 1; + group.eventElement = eventElement; + group.lastEventGroup = currentEventGroup; + group.pre = appendEvent(eventElement, currentEventType, currentEventGroup, e); + appendSequenceElement(group.container, eventElement); + } + lastEvent = currentEventType; + lastGroup = group; + }; + + return genericEventHandler; +} + +window.getGenericEventHandler = getGenericEventHandler; diff --git a/integrationExamples/videoModule/jwplayer/eventsUI.html b/integrationExamples/videoModule/jwplayer/eventsUI.html new file mode 100644 index 00000000000..69ace5f6c2b --- /dev/null +++ b/integrationExamples/videoModule/jwplayer/eventsUI.html @@ -0,0 +1,295 @@ + + + + + + + + + JW Player Event UI + + + + + + + +
+
+
+ +
+ + + + diff --git a/integrationExamples/videoModule/videojs/eventsUI.html b/integrationExamples/videoModule/videojs/eventsUI.html new file mode 100644 index 00000000000..04e0ca9eaf8 --- /dev/null +++ b/integrationExamples/videoModule/videojs/eventsUI.html @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + Video.JS Event UI + + + + + + + +
+ + +
+ +
+ + + + From 7224457772a579dfdb993db6cb3680dab5f966ba Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 18 May 2023 13:07:19 +0000 Subject: [PATCH 10/26] Prebid 7.50.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a6e6020a443..07e20fb864f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.50.0-pre", + "version": "7.50.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index c666ea19bda..add7185d761 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.50.0-pre", + "version": "7.50.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 343605d5cdb140c288fcf64c18419eeda573cad0 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 18 May 2023 13:07:19 +0000 Subject: [PATCH 11/26] Increment version to 7.51.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 07e20fb864f..af1d2cfde5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.50.0", + "version": "7.51.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index add7185d761..855ecd86437 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "7.50.0", + "version": "7.51.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 18f9774c7a9d989594ef6e6ef78231d0c80c0b3f Mon Sep 17 00:00:00 2001 From: MartinGumGum <109325501+MartinGumGum@users.noreply.github.com> Date: Thu, 18 May 2023 06:14:07 -0700 Subject: [PATCH 12/26] GumGum Bid Adapter : Id5 integration suppress link type for ttd (#9924) * ADJS-1271-send-envelope-param-for-lexicon * Read the id5 id linktype field and send it to the server. * Remove unit test for Id5 * Add unit test for id5. * minor changes to possibly fix edge error --------- Co-authored-by: John Bauzon Co-authored-by: John Ivan Bauzon --- modules/gumgumBidAdapter.js | 5 +++++ test/spec/modules/gumgumBidAdapter_spec.js | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index 8a6c4efa7fc..fdd3030bb7a 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -312,6 +312,11 @@ function buildRequests(validBidRequests, bidderRequest) { data.lt = lt; data.to = to; + // ADJS-1286 Read id5 id linktype field + if (userId && userId.id5id && userId.id5id.uid && userId.id5id.ext) { + data.id5Id = userId.id5id.uid || null + data.id5IdLinkType = userId.id5id.ext.linkType || null + } // ADTS-169 add adUnitCode to requests if (adUnitCode) data.aun = adUnitCode; diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 71f356a83ae..0e64ec67b27 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -111,6 +111,14 @@ describe('gumgumAdapter', function () { sizes: sizesArray } }, + userId: { + id5id: { + uid: 'uid-string', + ext: { + linkType: 2 + } + } + }, adUnitCode: 'adunit-code', sizes: sizesArray, bidId: '30b31c1838de1e', @@ -158,6 +166,12 @@ describe('gumgumAdapter', function () { const bidRequest = spec.buildRequests([request])[0]; expect(bidRequest.data.aun).to.equal(bidRequests[0].adUnitCode); }); + it('should set id5Id and id5IdLinkType if the uid and linkType are available', function () { + const request = { ...bidRequests[0] }; + const bidRequest = spec.buildRequests([request])[0]; + expect(bidRequest.data.id5Id).to.equal(bidRequests[0].userId.id5id.uid); + expect(bidRequest.data.id5IdLinkType).to.equal(bidRequests[0].userId.id5id.ext.linkType); + }); it('should set pubId param if found', function () { const request = { ...bidRequests[0], params: pubIdParam }; From 4d9b28c8bd7a19589d4d4e9b4b5925d35e52ac00 Mon Sep 17 00:00:00 2001 From: Andrew Slagle <42588549+spotxslagle@users.noreply.github.com> Date: Thu, 18 May 2023 12:08:57 -0600 Subject: [PATCH 13/26] PBS Bid Adapter: add context to emitted seatnonbid event (#9804) * Add context to emitted seatnonbid event * Add unit test coverage * Sanitize and emit context * Remove sanitize step for emitted data --- modules/prebidServerBidAdapter/index.js | 15 +++++++-------- test/spec/modules/prebidServerBidAdapter_spec.js | 4 +++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index 59af37b0686..9139ff9ad39 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -472,7 +472,13 @@ export function PrebidServer() { bidRequests.forEach(bidderRequest => events.emit(CONSTANTS.EVENTS.BIDDER_DONE, bidderRequest)); } if (shouldEmitNonbids(s2sBidRequest.s2sConfig, response)) { - emitNonBids(response.ext.seatnonbid, bidRequests[0].auctionId); + events.emit(CONSTANTS.EVENTS.SEAT_NON_BID, { + seatnonbid: response.ext.seatnonbid, + auctionId: bidRequests[0].auctionId, + requestedBidders, + response, + adapterMetrics + }); } done(); doClientSideSyncs(requestedBidders, gdprConsent, uspConsent, gppConsent); @@ -578,13 +584,6 @@ function shouldEmitNonbids(s2sConfig, response) { return s2sConfig?.extPrebid?.returnallbidstatus && response?.ext?.seatnonbid; } -function emitNonBids(seatnonbid, auctionId) { - events.emit(CONSTANTS.EVENTS.SEAT_NON_BID, { - seatnonbid, - auctionId - }); -} - /** * Global setter that sets eids permissions for bidders * This setter is to be used by userId module when included diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 9bdc39e3c76..6427b917134 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -3029,7 +3029,7 @@ describe('S2S Adapter', function () { expect(response).to.have.property('ttl', 60); }); - it('handles seatnonbid responses and calls SEAT_NON_BID', function () { + it('handles seatnonbid responses and emits SEAT_NON_BID', function () { const original = CONFIG; CONFIG.extPrebid = { returnallbidstatus: true }; const nonbidResponse = {...RESPONSE_OPENRTB, ext: {seatnonbid: [{}]}}; @@ -3042,6 +3042,8 @@ describe('S2S Adapter', function () { const event = events.emit.secondCall.args; expect(event[0]).to.equal(CONSTANTS.EVENTS.SEAT_NON_BID); expect(event[1].seatnonbid[0]).to.have.property('auctionId', 2); + expect(event[1].requestedBidders).to.deep.equal(['appnexus']); + expect(event[1].response).to.deep.equal(responding); }); it('respects defaultTtl', function () { From c43648b7fe150ece65c63b794f0ee59d8ea1664b Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 19 May 2023 15:18:36 +0200 Subject: [PATCH 14/26] Criteo bid adapter: Add video outstream renderer (#9955) Co-authored-by: v.raybaud --- modules/criteoBidAdapter.js | 41 +++++++++++++++++++- test/spec/modules/criteoBidAdapter_spec.js | 45 ++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 63c4482ab84..655b22083da 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -8,8 +8,11 @@ import { verify } from 'criteo-direct-rsa-validate/build/verify.js'; // ref#2 import { getStorageManager } from '../src/storageManager.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { hasPurpose1Consent } from '../src/utils/gpdr.js'; +import { Renderer } from '../src/Renderer.js'; +import { OUTSTREAM } from '../src/video.js'; + const GVLID = 91; -export const ADAPTER_VERSION = 35; +export const ADAPTER_VERSION = 36; const BIDDER_CODE = 'criteo'; const CDB_ENDPOINT = 'https://bidder.criteo.com/cdb'; const PROFILE_ID_INLINE = 207; @@ -29,6 +32,7 @@ export const FAST_BID_VERSION_CURRENT = 135; const FAST_BID_VERSION_LATEST = 'latest'; const FAST_BID_VERSION_NONE = 'none'; const PUBLISHER_TAG_URL_TEMPLATE = 'https://static.criteo.net/js/ld/publishertag.prebid' + FAST_BID_VERSION_PLACEHOLDER + '.js'; +const PUBLISHER_TAG_OUTSTREAM_SRC = 'https://static.criteo.net/js/ld/publishertag.renderer.js' const FAST_BID_PUBKEY_E = 65537; const FAST_BID_PUBKEY_N = 'ztQYwCE5BU7T9CDM5he6rKoabstXRmkzx54zFPZkWbK530dwtLBDeaWBMxHBUT55CYyboR/EZ4efghPi3CoNGfGWezpjko9P6p2EwGArtHEeS4slhu/SpSIFMjG6fdrpRoNuIAMhq1Z+Pr/+HOd1pThFKeGFr2/NhtAg+TXAzaU='; @@ -243,6 +247,11 @@ export const spec = { } else if (slot.video) { bid.vastUrl = slot.displayurl; bid.mediaType = VIDEO; + const context = deepAccess(bidRequest, 'mediaTypes.video.context'); + // if outstream video, add a default render for it. + if (context === OUTSTREAM) { + bid.renderer = createOutstreamVideoRenderer(slot); + } } else { bid.ad = slot.creative; } @@ -252,7 +261,6 @@ export const spec = { return bids; }, - /** * @param {TimedOutBid} timeoutData */ @@ -711,6 +719,35 @@ export function getFastBidUrl(fastBidVersion) { return PUBLISHER_TAG_URL_TEMPLATE.replace(FAST_BID_VERSION_PLACEHOLDER, version); } +function createOutstreamVideoRenderer(slot) { + if (slot.ext.videoPlayerConfig === undefined || slot.ext.videoPlayerType === undefined) { + return undefined; + } + + const config = { + documentResolver: (bid, sourceDocument, renderDocument) => { + return renderDocument ?? sourceDocument; + } + } + + const render = (bid, renderDocument) => { + let payload = { + slotid: slot.impid, + vastUrl: slot.displayurl, + vastXml: slot.creative, + documentContext: renderDocument, + }; + + let outstreamConfig = slot.ext.videoPlayerConfig; + + window.CriteoOutStream[slot.ext.videoPlayerType].play(payload, outstreamConfig) + }; + + const renderer = Renderer.install({url: PUBLISHER_TAG_OUTSTREAM_SRC, config: config}); + renderer.setRender(render); + return renderer; +} + export function tryGetCriteoFastBid() { // begin ref#1 try { diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 17527eb42c7..04c72f499aa 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -1762,6 +1762,51 @@ describe('The Criteo bidding adapter', function () { expect(bids[0].mediaType).to.equal(VIDEO); }); + it('should properly parse a bid response with a outstream video', function () { + const response = { + body: { + slots: [{ + impid: 'test-requestId', + bidId: 'abc123', + cpm: 1.23, + displayurl: 'http://test-ad', + width: 728, + height: 90, + zoneid: 123, + video: true, + ext: { + videoPlayerType: 'RadiantMediaPlayer', + videoPlayerConfig: { + + } + } + }], + }, + }; + const request = { + bidRequests: [{ + adUnitCode: 'test-requestId', + bidId: 'test-bidId', + params: { + zoneId: 123, + }, + mediaTypes: { + video: { + context: 'outstream' + } + } + }] + }; + const bids = spec.interpretResponse(response, request); + expect(bids).to.have.lengthOf(1); + expect(bids[0].requestId).to.equal('test-bidId'); + expect(bids[0].cpm).to.equal(1.23); + expect(bids[0].vastUrl).to.equal('http://test-ad'); + expect(bids[0].renderer.url).to.equal('https://static.criteo.net/js/ld/publishertag.renderer.js'); + expect(typeof bids[0].renderer.config.documentResolver).to.equal('function'); + expect(typeof bids[0].renderer._render).to.equal('function'); + }); + it('should properly parse a bid response with native', function () { const response = { body: { From 69c8db81f4d6dade63fe17bef199aad5b8892c25 Mon Sep 17 00:00:00 2001 From: Gena Date: Fri, 19 May 2023 18:05:55 +0300 Subject: [PATCH 15/26] add SSP Copper6 alias adapter (#9972) --- modules/adtelligentBidAdapter.js | 6 ++++-- test/spec/modules/adtelligentBidAdapter_spec.js | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index ebba4f3ec6b..cbba1bb49eb 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -24,7 +24,8 @@ const HOST_GETTERS = { pgam: () => 'ghb.pgamssp.com', ocm: () => 'ghb.cenarius.orangeclickmedia.com', vidcrunchllc: () => 'ghb.platform.vidcrunch.com', - '9dotsmedia': () => 'ghb.platform.audiodots.com' + '9dotsmedia': () => 'ghb.platform.audiodots.com', + copper6: () => 'ghb.app.copper6.com' } const getUri = function (bidderCode) { let bidderWithoutSuffix = bidderCode.split('_')[0]; @@ -51,7 +52,8 @@ export const spec = { 'pgam', { code: 'ocm', gvlid: 1148 }, { code: 'vidcrunchllc', gvlid: 1145 }, - '9dotsmedia' + '9dotsmedia', + 'copper6', ], supportedMediaTypes: [VIDEO, BANNER], isBidRequestValid: function (bid) { diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index c57e51c44fc..26284d539d4 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -22,6 +22,7 @@ const aliasEP = { 'ocm': 'https://ghb.cenarius.orangeclickmedia.com/v2/auction/', 'vidcrunchllc': 'https://ghb.platform.vidcrunch.com/v2/auction/', '9dotsmedia': 'https://ghb.platform.audiodots.com/v2/auction/', + 'copper6': 'https://ghb.app.copper6.com/v2/auction/', }; const DEFAULT_ADATPER_REQ = { bidderCode: 'adtelligent' }; From 73a1a723a5f0314cc4bbbac96a32fa2e63d642b4 Mon Sep 17 00:00:00 2001 From: Pgb-Criteo <92986445+Pgb-Criteo@users.noreply.github.com> Date: Fri, 19 May 2023 17:16:08 +0200 Subject: [PATCH 16/26] Criteo Bid Adapter: add support for video.plcmt (#9975) --- modules/criteoBidAdapter.js | 3 ++- test/spec/modules/criteoBidAdapter_spec.js | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 655b22083da..8b17d0372b5 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -485,7 +485,8 @@ function buildCdbRequest(context, bidRequests, bidderRequest) { placement: bidRequest.mediaTypes.video.placement, minduration: bidRequest.mediaTypes.video.minduration, playbackmethod: bidRequest.mediaTypes.video.playbackmethod, - startdelay: bidRequest.mediaTypes.video.startdelay + startdelay: bidRequest.mediaTypes.video.startdelay, + plcmt: bidRequest.mediaTypes.video.plcmt }; const paramsVideo = bidRequest.params.video; if (paramsVideo !== undefined) { diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index 04c72f499aa..a13e81b6d88 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -304,7 +304,8 @@ describe('The Criteo bidding adapter', function () { placement: 1, minduration: 0, playbackmethod: 1, - startdelay: 0 + startdelay: 0, + plcmt: 1 } }, params: { @@ -1099,7 +1100,8 @@ describe('The Criteo bidding adapter', function () { mimes: ['video/mp4', 'video/x-flv'], maxduration: 30, api: [1, 2], - protocols: [2, 3] + protocols: [2, 3], + plcmt: 3 } }, params: { @@ -1129,6 +1131,7 @@ describe('The Criteo bidding adapter', function () { expect(ortbRequest.slots[0].video.startdelay).to.equal(5); expect(ortbRequest.slots[0].video.playbackmethod).to.deep.equal([1, 3]); expect(ortbRequest.slots[0].video.placement).to.equal(2); + expect(ortbRequest.slots[0].video.plcmt).to.equal(3); }); it('should properly build a video request with more than one player size', function () { From f0e7488025ebf162973ab808767d3f23c33fbab7 Mon Sep 17 00:00:00 2001 From: pm-azhar-mulla <75726247+pm-azhar-mulla@users.noreply.github.com> Date: Mon, 22 May 2023 18:22:11 +0530 Subject: [PATCH 17/26] PubMatic Bid Adapter: Added support for video.plcmt (#9979) * Added support for plcmt param * Added unit test cases --------- Co-authored-by: pm-azhar-mulla --- modules/pubmaticBidAdapter.js | 1 + test/spec/modules/pubmaticBidAdapter_spec.js | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index edfee7a0b6d..2861b71017a 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -51,6 +51,7 @@ const VIDEO_CUSTOM_PARAMS = { 'battr': DATA_TYPES.ARRAY, 'linearity': DATA_TYPES.NUMBER, 'placement': DATA_TYPES.NUMBER, + 'plcmt': DATA_TYPES.NUMBER, 'minbitrate': DATA_TYPES.NUMBER, 'maxbitrate': DATA_TYPES.NUMBER, 'skip': DATA_TYPES.NUMBER diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 3198fe406e7..beb8a248d13 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -111,6 +111,7 @@ describe('PubMatic adapter', function () { battr: [13, 14], linearity: 1, placement: 2, + plcmt: 1, minbitrate: 10, maxbitrate: 10 } @@ -162,6 +163,7 @@ describe('PubMatic adapter', function () { battr: [13, 14], linearity: 1, placement: 2, + plcmt: 1, minbitrate: 100, maxbitrate: 4096 } @@ -382,6 +384,7 @@ describe('PubMatic adapter', function () { battr: [13, 14], linearity: 1, placement: 2, + plcmt: 1, minbitrate: 100, maxbitrate: 4096 } @@ -508,6 +511,7 @@ describe('PubMatic adapter', function () { battr: [13, 14], linearity: 1, placement: 2, + plcmt: 1, minbitrate: 100, maxbitrate: 4096 } @@ -577,6 +581,7 @@ describe('PubMatic adapter', function () { battr: [13, 14], linearity: 1, placement: 2, + plcmt: 1, minbitrate: 100, maxbitrate: 4096 } @@ -2738,6 +2743,7 @@ describe('PubMatic adapter', function () { expect(data.imp[1]['video']['linearity']).to.equal(multipleMediaRequests[1].params.video['linearity']); expect(data.imp[1]['video']['placement']).to.equal(multipleMediaRequests[1].params.video['placement']); + expect(data.imp[1]['video']['plcmt']).to.equal(multipleMediaRequests[1].params.video['plcmt']); expect(data.imp[1]['video']['minbitrate']).to.equal(multipleMediaRequests[1].params.video['minbitrate']); expect(data.imp[1]['video']['maxbitrate']).to.equal(multipleMediaRequests[1].params.video['maxbitrate']); @@ -3033,6 +3039,7 @@ describe('PubMatic adapter', function () { expect(data.imp[0]['video']['linearity']).to.equal(videoBidRequests[0].params.video['linearity']); expect(data.imp[0]['video']['placement']).to.equal(videoBidRequests[0].params.video['placement']); + expect(data.imp[0]['video']['plcmt']).to.equal(videoBidRequests[0].params.video['plcmt']); expect(data.imp[0]['video']['minbitrate']).to.equal(videoBidRequests[0].params.video['minbitrate']); expect(data.imp[0]['video']['maxbitrate']).to.equal(videoBidRequests[0].params.video['maxbitrate']); From 1a5ad0cdbcc7ca772ae9fd0f2cede09f8276cd13 Mon Sep 17 00:00:00 2001 From: xwang202 <57196235+xwang202@users.noreply.github.com> Date: Mon, 22 May 2023 20:54:02 +0800 Subject: [PATCH 18/26] FreeWheel SSP Adapter: add support for video.plcmt (#9978) * FreeWheel add floor price * FreeWheel code update * FreeWheel-SSP-Adapter: Update to use Vast 4.2 by default * FreeWheel-SSP-Adapter add userIdAsEids support * Freewheel-SSP-Adapter add test for eids * Freewheel SSP Adapter: add prebid version in request * code cleanup * FreeWheel SSP Bid Adapter: support video context and placement * update test * FreeWheel SSP Bid Adapter: add GPP support * Freewheel SSP Bid Adapter: test update * FreeWheel SSP Adapter: update the default value for video placement and context * update test * FreeWheel SSP Adapter: add support for video.plcmt --- modules/freewheel-sspBidAdapter.js | 12 ++++++++++-- test/spec/modules/freewheel-sspBidAdapter_spec.js | 7 +++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/freewheel-sspBidAdapter.js b/modules/freewheel-sspBidAdapter.js index c0524ca3cd6..cd4785cdc78 100644 --- a/modules/freewheel-sspBidAdapter.js +++ b/modules/freewheel-sspBidAdapter.js @@ -340,6 +340,7 @@ export const spec = { var timeInMillis = new Date().getTime(); var keyCode = hashcode(zone + '' + timeInMillis); var bidfloor = getBidFloor(currentBidRequest, config); + var format = currentBidRequest.params.format; var requestParams = { reqType: 'AdsSetup', @@ -435,10 +436,17 @@ export const spec = { // Add video context and placement in requestParams if (currentBidRequest.mediaTypes.video) { - var videoContext = currentBidRequest.mediaTypes.video.context ? currentBidRequest.mediaTypes.video.context : 'instream'; - var videoPlacement = currentBidRequest.mediaTypes.video.placement ? currentBidRequest.mediaTypes.video.placement : 1; + var videoContext = currentBidRequest.mediaTypes.video.context ? currentBidRequest.mediaTypes.video.context : ''; + var videoPlacement = currentBidRequest.mediaTypes.video.placement ? currentBidRequest.mediaTypes.video.placement : null; + var videoPlcmt = currentBidRequest.mediaTypes.video.plcmt ? currentBidRequest.mediaTypes.video.plcmt : null; + + if (format == 'inbanner') { + videoPlacement = 2; + videoContext = 'In-Banner'; + } requestParams.video_context = videoContext; requestParams.video_placement = videoPlacement; + requestParams.video_plcmt = videoPlcmt; } return { diff --git a/test/spec/modules/freewheel-sspBidAdapter_spec.js b/test/spec/modules/freewheel-sspBidAdapter_spec.js index d8dc6b18b8f..fe04a430ce6 100644 --- a/test/spec/modules/freewheel-sspBidAdapter_spec.js +++ b/test/spec/modules/freewheel-sspBidAdapter_spec.js @@ -284,8 +284,9 @@ describe('freewheelSSP BidAdapter Test', () => { it('should return context and placement with default values', () => { const request = spec.buildRequests(bidRequests); const payload = request[0].data; - expect(payload.video_context).to.equal('instream'); ; - expect(payload.video_placement).to.equal(1); + expect(payload.video_context).to.equal(''); ; + expect(payload.video_placement).to.equal(null); + expect(payload.video_plcmt).to.equal(null); }); it('should add parameters to the tag', () => { @@ -367,6 +368,7 @@ describe('freewheelSSP BidAdapter Test', () => { 'video': { 'context': 'outstream', 'placement': 2, + 'plcmt': 3, 'playerSize': [300, 600], } }, @@ -382,6 +384,7 @@ describe('freewheelSSP BidAdapter Test', () => { const payload = request[0].data; expect(payload.video_context).to.equal('outstream'); ; expect(payload.video_placement).to.equal(2); + expect(payload.video_plcmt).to.equal(3); }); }) From be23ae2d8bbed1017007ea2d044801ce50507a13 Mon Sep 17 00:00:00 2001 From: Antonios Sarhanis Date: Mon, 22 May 2023 23:40:20 +1000 Subject: [PATCH 19/26] Adnuntius Bid Adaptor: Handling deals better (#9943) --- modules/adnuntiusBidAdapter.js | 177 ++-- modules/adnuntiusBidAdapter.md | 3 +- test/spec/modules/adnuntiusBidAdapter_spec.js | 799 ++++++++++++------ 3 files changed, 635 insertions(+), 344 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index e59d12eadad..d1192c2eb64 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -5,11 +5,17 @@ import { config } from '../src/config.js'; import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'adnuntius'; +const BIDDER_CODE_DEAL_ALIAS_BASE = 'adndeal'; +const BIDDER_CODE_DEAL_ALIAS_1 = BIDDER_CODE_DEAL_ALIAS_BASE + '1'; +const BIDDER_CODE_DEAL_ALIAS_2 = BIDDER_CODE_DEAL_ALIAS_BASE + '2'; +const BIDDER_CODE_DEAL_ALIAS_3 = BIDDER_CODE_DEAL_ALIAS_BASE + '3'; +const BIDDER_CODE_DEAL_ALIAS_4 = BIDDER_CODE_DEAL_ALIAS_BASE + '4'; +const BIDDER_CODE_DEAL_ALIAS_5 = BIDDER_CODE_DEAL_ALIAS_BASE + '5'; const ENDPOINT_URL = 'https://ads.adnuntius.delivery/i'; const ENDPOINT_URL_EUROPE = 'https://europe.delivery.adnuntius.com/i'; const GVLID = 855; const DEFAULT_VAST_VERSION = 'vast4' -// const DEFAULT_NATIVE = 'native' +const MAXIMUM_DEALS_LIMIT = 5; const checkSegment = function (segment) { if (isStr(segment)) return segment; @@ -29,40 +35,13 @@ const getSegmentsFromOrtb = function (ortb2) { return segments } -// function createNative(ad) { -// const native = {}; -// const assets = ad.assets -// native.title = ad.text.title.content; -// native.image = { -// url: assets.image.cdnId, -// height: assets.image.height, -// width: assets.image.width, -// }; -// if (assets.icon) { -// native.icon = { -// url: assets.icon.cdnId, -// height: assets.icon.height, -// width: assets.icon.width, -// }; -// } - -// native.sponsoredBy = ad.text.sponsoredBy?.content || ''; -// native.body = ad.text.body?.content || ''; -// native.cta = ad.text.cta?.content || ''; -// native.clickUrl = ad.destinationUrls.destination || ''; -// native.impressionTrackers = ad.impressionTrackingUrls || [ad.renderedPixel]; - -// return native; -// } - const handleMeta = function () { const storage = getStorageManager({ bidderCode: BIDDER_CODE }) let adnMeta = null if (storage.localStorageIsEnabled()) { adnMeta = JSON.parse(storage.getDataFromLocalStorage('adn.metaData')) } - const meta = (adnMeta !== null) ? adnMeta.reduce((acc, cur) => { return { ...acc, [cur.key]: cur.value } }, {}) : {} - return meta + return (adnMeta !== null) ? adnMeta.reduce((acc, cur) => { return { ...acc, [cur.key]: cur.value } }, {}) : {} } const getUsi = function (meta, ortb2, bidderRequest) { @@ -71,12 +50,23 @@ const getUsi = function (meta, ortb2, bidderRequest) { return usi } +const AU_ID_REGEX = new RegExp('^[0-9A-Fa-f]{1,20}$'); + export const spec = { code: BIDDER_CODE, + aliases: [ + BIDDER_CODE_DEAL_ALIAS_1, + BIDDER_CODE_DEAL_ALIAS_2, + BIDDER_CODE_DEAL_ALIAS_3, + BIDDER_CODE_DEAL_ALIAS_4, + BIDDER_CODE_DEAL_ALIAS_5 + ], gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function (bid) { - return !!(bid.bidId || (bid.params.member && bid.params.invCode)); + // The auId MUST be a hexadecimal string + const validAuId = AU_ID_REGEX.test(bid.params.auId); + return !!(validAuId && (bid.bidId || (bid.params.member && bid.params.invCode))); }, buildRequests: function (validBidRequests, bidderRequest) { @@ -100,20 +90,18 @@ export const spec = { if (gdprApplies !== undefined) request.push('consentString=' + consentString); if (segments.length > 0) request.push('segments=' + segments.join(',')); if (usi) request.push('userId=' + usi); - if (bidderConfig.useCookie === false) request.push('noCookies=true') - for (var i = 0; i < validBidRequests.length; i++) { + if (bidderConfig.useCookie === false) request.push('noCookies=true'); + if (bidderConfig.maxDeals > 0) request.push('ds=' + Math.min(bidderConfig.maxDeals, MAXIMUM_DEALS_LIMIT)); + for (let i = 0; i < validBidRequests.length; i++) { const bid = validBidRequests[i] let network = bid.params.network || 'network'; + const maxDeals = Math.max(0, Math.min(bid.params.maxDeals || 0, MAXIMUM_DEALS_LIMIT)); const targeting = bid.params.targeting || {}; if (bid.mediaTypes && bid.mediaTypes.video && bid.mediaTypes.video.context !== 'outstream') { network += '_video' } - // if (bid.mediaTypes && bid.mediaTypes.native) { - // network += '_native' - // } - bidRequests[network] = bidRequests[network] || []; bidRequests[network].push(bid); @@ -121,18 +109,17 @@ export const spec = { networks[network].adUnits = networks[network].adUnits || []; if (bidderRequest && bidderRequest.refererInfo) networks[network].context = bidderRequest.refererInfo.page; if (adnMeta) networks[network].metaData = adnMeta; - const adUnit = { ...targeting, auId: bid.params.auId, targetId: bid.bidId } + const adUnit = { ...targeting, auId: bid.params.auId, targetId: bid.bidId, maxDeals: maxDeals } if (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) adUnit.dimensions = bid.mediaTypes.banner.sizes networks[network].adUnits.push(adUnit); } const networkKeys = Object.keys(networks) - for (var j = 0; j < networkKeys.length; j++) { + for (let j = 0; j < networkKeys.length; j++) { const network = networkKeys[j]; const networkRequest = [...request] if (network.indexOf('_video') > -1) { networkRequest.push('tt=' + DEFAULT_VAST_VERSION) } const requestURL = gdprApplies ? ENDPOINT_URL_EUROPE : ENDPOINT_URL - // if (network.indexOf('_native') > -1) { networkRequest.push('tt=' + DEFAULT_NATIVE) } requests.push({ method: 'POST', url: requestURL + '?' + networkRequest.join('&'), @@ -146,50 +133,82 @@ export const spec = { interpretResponse: function (serverResponse, bidRequest) { const adUnits = serverResponse.body.adUnits; - const bidResponsesById = adUnits.reduce((response, adUnit) => { - if (adUnit.matchedAdCount >= 1) { - const ad = adUnit.ads[0]; - const effectiveCpm = (ad.bid) ? ad.bid.amount * 1000 : 0; - const adResponse = { - ...response, - [adUnit.targetId]: { - requestId: adUnit.targetId, - cpm: effectiveCpm, - width: Number(ad.creativeWidth), - height: Number(ad.creativeHeight), - creativeId: ad.creativeId, - currency: (ad.bid) ? ad.bid.currency : 'EUR', - dealId: ad.dealId || '', - meta: { - advertiserDomains: (ad.destinationUrls.destination) ? [ad.destinationUrls.destination.split('/')[2]] : [] - - }, - netRevenue: false, - ttl: 360, - } - } - - if (adUnit.vastXml) { - adResponse[adUnit.targetId].vastXml = adUnit.vastXml - adResponse[adUnit.targetId].mediaType = VIDEO - // } else if (ad.assets && ad.assets.image && ad.text && ad.text.title && ad.text.body && ad.destinationUrls && ad.destinationUrls.destination) { - // adResponse[adUnit.targetId].native = createNative(ad); - // adResponse[adUnit.targetId].mediaType = NATIVE; - } else { - adResponse[adUnit.targetId].ad = adUnit.html - } - - return adResponse - } else return response + + function buildAdResponse(bidderCode, ad, adUnit, dealCount) { + const destinationUrls = ad.destinationUrls || {}; + const advertiserDomains = []; + for (const value of Object.values(destinationUrls)) { + advertiserDomains.push(value.split('/')[2]) + } + const adResponse = { + bidderCode: bidderCode, + requestId: adUnit.targetId, + cpm: (ad.bid) ? ad.bid.amount * 1000 : 0, + width: Number(ad.creativeWidth), + height: Number(ad.creativeHeight), + creativeId: ad.creativeId, + currency: (ad.bid) ? ad.bid.currency : 'EUR', + dealId: ad.dealId || '', + dealCount: dealCount, + meta: { + advertiserDomains: advertiserDomains + }, + netRevenue: false, + ttl: 360, + }; + // Deal bids provide the rendered ad content along with the + // bid; whereas regular bids have it stored on the ad-unit. + const isDeal = dealCount > 0; + const renderSource = isDeal ? ad : adUnit; + if (renderSource.vastXml) { + adResponse.vastXml = renderSource.vastXml + adResponse.mediaType = VIDEO + } else { + adResponse.ad = renderSource.html + } + return adResponse; + } + + const bidsById = bidRequest.bid.reduce((response, bid) => { + return { + ...response, + [bid.bidId]: bid + }; }, {}); - const bidResponse = bidRequest.bid.map(bid => bid.bidId).reduce((request, adunitId) => { - if (bidResponsesById[adunitId]) { request.push(bidResponsesById[adunitId]) } - return request + const hasBidAdUnits = adUnits.filter((au) => { + const bid = bidsById[au.targetId]; + if (bid && bid.bidder === BIDDER_CODE) { + return au.matchedAdCount > 0; + } else { + // We do NOT accept bids when using this adaptor via one of the + // "deals" aliases; those requests are for ONLY getting deals from Adnuntius + return false; + } + }); + const hasDealsAdUnits = adUnits.filter((au) => { + return au.deals && au.deals.length > 0; + }); + + const dealAdResponses = hasDealsAdUnits.reduce((response, au) => { + const bid = bidsById[au.targetId]; + if (bid) { + (au.deals || []).forEach((deal, i) => { + response.push(buildAdResponse(bid.bidder, deal, au, i + 1)); + }); + } + return response; }, []); - return bidResponse - }, + const bidAdResponses = hasBidAdUnits.reduce((response, au) => { + const bid = bidsById[au.targetId]; + if (bid) { + response.push(buildAdResponse(bid.bidder, au.ads[0], au, 0)); + } + return response; + }, []); + return [...dealAdResponses, ...bidAdResponses]; + } } registerBidder(spec); diff --git a/modules/adnuntiusBidAdapter.md b/modules/adnuntiusBidAdapter.md index aed12079856..d5ecf5d4ee3 100644 --- a/modules/adnuntiusBidAdapter.md +++ b/modules/adnuntiusBidAdapter.md @@ -9,7 +9,7 @@ Maintainer: info@adnuntius.com # Description Adnuntius Bidder Adapter for Prebid.js. -Only Banner format is supported. +Banner and Video formats are supported. # Test Parameters ``` @@ -27,6 +27,7 @@ Only Banner format is supported. params: { auId: "8b6bc", network: "adnuntius", + maxDeals: 0 } }, ] diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 153565eb3ca..b1657ed1596 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -1,12 +1,13 @@ // import or require modules necessary for the test, e.g.: -import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' -import { spec } from 'modules/adnuntiusBidAdapter.js'; -import { newBidder } from 'src/adapters/bidderFactory.js'; -import { config } from 'src/config.js'; +import {expect} from 'chai'; // may prefer 'assert' in place of 'expect' +import {misc, spec} from 'modules/adnuntiusBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; +import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; -import { getStorageManager } from 'src/storageManager.js'; +import {getStorageManager} from 'src/storageManager.js'; +import {getGlobal} from '../../../src/prebidGlobal'; -describe('adnuntiusBidAdapter', function () { +describe('adnuntiusBidAdapter', function() { const URL = 'https://ads.adnuntius.delivery/i?tzo='; const EURO_URL = 'https://europe.delivery.adnuntius.com/i?tzo='; const GVLID = 855; @@ -14,7 +15,7 @@ describe('adnuntiusBidAdapter', function () { const meta = [{ key: 'usi', value: usi }] before(() => { - $$PREBID_GLOBAL$$.bidderSettings = { + getGlobal().bidderSettings = { adnuntius: { storageAllowed: true } @@ -24,43 +25,58 @@ describe('adnuntiusBidAdapter', function () { }); after(() => { - $$PREBID_GLOBAL$$.bidderSettings = {}; + getGlobal().bidderSettings = {}; }); - afterEach(function () { + afterEach(function() { config.resetConfig(); }); const tzo = new Date().getTimezoneOffset(); - const ENDPOINT_URL = `${URL}${tzo}&format=json&userId=${usi}`; - const ENDPOINT_URL_VIDEO = `${URL}${tzo}&format=json&userId=${usi}&tt=vast4`; - const ENDPOINT_URL_NOCOOKIE = `${URL}${tzo}&format=json&userId=${usi}&noCookies=true`; - const ENDPOINT_URL_SEGMENTS = `${URL}${tzo}&format=json&segments=segment1,segment2,segment3&userId=${usi}`; + const ENDPOINT_URL_BASE = `${URL}${tzo}&format=json`; + const ENDPOINT_URL = `${ENDPOINT_URL_BASE}&userId=${usi}`; + const ENDPOINT_URL_VIDEO = `${ENDPOINT_URL_BASE}&userId=${usi}&tt=vast4`; + const ENDPOINT_URL_NOCOOKIE = `${ENDPOINT_URL_BASE}&userId=${usi}&noCookies=true`; + const ENDPOINT_URL_SEGMENTS = `${ENDPOINT_URL_BASE}&segments=segment1,segment2,segment3&userId=${usi}`; const ENDPOINT_URL_CONSENT = `${EURO_URL}${tzo}&format=json&consentString=consentString&userId=${usi}`; const adapter = newBidder(spec); const bidderRequests = [ { - bidId: '123', + bidId: 'adn-000000000008b6bc', bidder: 'adnuntius', params: { - auId: '8b6bc', + auId: '000000000008b6bc', network: 'adnuntius', + maxDeals: 1 }, mediaTypes: { banner: { sizes: [[640, 480], [600, 400]], } }, + }, + { + bidId: 'adn-0000000000000551', + bidder: 'adnuntius', + params: { + auId: '0000000000000551', + network: 'adnuntius', + }, + mediaTypes: { + banner: { + sizes: [[1640, 1480], [1600, 1400]], + } + }, } ] const videoBidderRequest = [ { - bidId: '123', + bidId: 'adn-0000000000000551', bidder: 'adnuntius', params: { - auId: '8b6bc', + auId: '0000000000000551', network: 'adnuntius', }, mediaTypes: { @@ -72,264 +88,364 @@ describe('adnuntiusBidAdapter', function () { } ] - // const nativeBidderRequest = [ - // { - // bidId: '123', - // bidder: 'adnuntius', - // params: { - // auId: '8b6bc', - // network: 'adnuntius', - // }, - // mediaTypes: { - // native: { - // title: { - // required: true - // }, - // image: { - // required: true - // }, - // body: { - // required: true - // } - // } - // }, - // } - // ] - const singleBidRequest = { bid: [ { - bidId: '123', + bidder: 'adnuntius', + bidId: 'adn-0000000000000551', } ] } const videoBidRequest = { - bid: videoBidderRequest + bid: videoBidderRequest, + bidder: 'adnuntius' } - // const nativeBidRequest = { - // bid: nativeBidderRequest - // } - const serverResponse = { body: { 'adUnits': [ { - 'auId': '000000000008b6bc', - 'targetId': '123', - 'html': '

hi!

', + 'auId': '0000000000000551', + 'targetId': 'adn-0000000000000551', + 'html': "\n\n\n \n \n \n\n\n
\n
\"\"/
\n
\n \n\n", 'matchedAdCount': 1, - 'responseId': 'adn-rsp-1460129238', + 'responseId': 'adn-rsp--229633088', + 'deals': [ + { + 'destinationUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fc%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com', + 'assets': { + 'Image': { + 'cdnId': 'https://cdn.adnuntius.com/cdn/iBgqruUNbaUb2hmD3vws7WTi84jg_WB_-VOF_FeOZ7A.png', + 'width': '640', + 'height': '480' + } + }, + 'text': {}, + 'choices': {}, + 'clickUrl': 'https://ads.adnuntius.delivery/c/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'urls': { + 'destination': 'https://ads.adnuntius.delivery/c/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg?ct=673&r=http%3A%2F%2Fadnuntius.com' + }, + 'urlsEsc': { + 'destination': 'https%3A%2F%2Fads.adnuntius.delivery%2Fc%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com' + }, + 'destinationUrls': { + 'destination': 'https://adnuntius.com' + }, + 'cpm': { + 'amount': 9, + 'currency': 'USD' + }, + 'bid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'grossBid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'netBid': { + 'amount': 0.0081, + 'currency': 'USD' + }, + 'dealId': 'abc123xyz', + 'impressionTrackingUrls': [], + 'impressionTrackingUrlsEsc': [], + 'adId': 'adn-id-1064238860', + 'selectedColumn': '0', + 'selectedColumnPosition': '0', + 'renderedPixel': 'https://ads.adnuntius.delivery/b/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg.html', + 'renderedPixelEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fb%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg.html', + 'visibleUrl': 'https://ads.adnuntius.delivery/s?rt=yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'visibleUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fs%3Frt%3DyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'viewUrl': 'https://ads.adnuntius.delivery/v?rt=yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'viewUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fv%3Frt%3DyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'rt': 'yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'creativeWidth': '640', + 'creativeHeight': '480', + 'creativeId': 's90t0q03pm', + 'lineItemId': 'cr3hnkkxhnkw9ldy', + 'layoutId': 'buyers_network_image_layout_1', + 'layoutName': 'Image', + 'layoutExternalReference': '', + 'html': "\n\n\n \n \n \n\n\n
\n
\"\"/
\n
\n \n\n", + 'renderTemplate': '' + } + ], 'ads': [ { - 'destinationUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fc%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN%3Fct%3D2501%26r%3Dhttp%253A%252F%252Fgoogle.com', + 'destinationUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fc%2F5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com', 'assets': { - 'image': { - 'cdnId': 'https://assets.adnuntius.com/oEmZa5uYjxENfA1R692FVn6qIveFpO8wUbpyF2xSOCc.jpg', - 'width': '980', - 'height': '120' + 'Image': { + 'cdnId': 'https://cdn.adnuntius.com/cdn/iBgqruUNbaUb2hmD3vws7WTi84jg_WB_-VOF_FeOZ7A.png', + 'width': '640', + 'height': '480' } }, - 'clickUrl': 'https://delivery.adnuntius.com/c/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'dealId': 'not-in-deal-array-here', + 'text': {}, + 'choices': {}, + 'clickUrl': 'https://ads.adnuntius.delivery/c/5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg', 'urls': { - 'destination': 'https://delivery.adnuntius.com/c/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN?ct=2501&r=http%3A%2F%2Fgoogle.com' + 'destination': 'https://ads.adnuntius.delivery/c/5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg?ct=673&r=http%3A%2F%2Fadnuntius.com' }, 'urlsEsc': { - 'destination': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fc%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN%3Fct%3D2501%26r%3Dhttp%253A%252F%252Fgoogle.com' + 'destination': 'http%3A%2F%2Fads.adnuntius.delivery%2Fc%2F5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com' }, 'destinationUrls': { - 'destination': 'http://google.com' + 'destination': 'https://adnuntius.com' + }, + 'cpm': { + 'amount': 9, + 'currency': 'USD' + }, + 'bid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'grossBid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'netBid': { + 'amount': 0.0081, + 'currency': 'USD' }, - 'cpm': { 'amount': 5.0, 'currency': 'NOK' }, - 'bid': { 'amount': 0.005, 'currency': 'NOK' }, - 'cost': { 'amount': 0.005, 'currency': 'NOK' }, 'impressionTrackingUrls': [], 'impressionTrackingUrlsEsc': [], - 'adId': 'adn-id-1347343135', + 'adId': 'adn-id-1488629603', 'selectedColumn': '0', 'selectedColumnPosition': '0', - 'renderedPixel': 'https://delivery.adnuntius.com/b/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN.html', - 'renderedPixelEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fb%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN.html', - 'visibleUrl': 'https://delivery.adnuntius.com/s?rt=52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - 'visibleUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fs%3Frt%3D52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - 'viewUrl': 'https://delivery.adnuntius.com/v?rt=52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - 'viewUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fv%3Frt%3D52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - 'rt': '52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - 'creativeWidth': '980', - 'creativeHeight': '120', - 'creativeId': 'wgkq587vgtpchsx1', - 'lineItemId': 'scyjdyv3mzgdsnpf', - 'layoutId': 'sw6gtws2rdj1kwby', - 'layoutName': 'Responsive image' - }, - + 'renderedPixel': 'https://ads.adnuntius.delivery/b/5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg.html', + 'renderedPixelEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fb%2F5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg.html', + 'visibleUrl': 'https://ads.adnuntius.delivery/s?rt=5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg', + 'visibleUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fs%3Frt%3D5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg', + 'viewUrl': 'https://ads.adnuntius.delivery/v?rt=5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg', + 'viewUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fv%3Frt%3D5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg', + 'rt': '5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg', + 'creativeWidth': '640', + 'creativeHeight': '480', + 'creativeId': 's90t0q03pm', + 'lineItemId': 'cr3hnkkxhnkw9ldy', + 'layoutId': 'buyers_network_image_layout_1', + 'layoutName': 'Image', + 'layoutExternalReference': '', + 'html': '', + 'renderTemplate': '' + } ] }, { 'auId': '000000000008b6bc', - 'targetId': '456', + 'targetId': 'adn-000000000008b6bc', 'matchedAdCount': 0, 'responseId': 'adn-rsp-1460129238', } - ] + ], + 'metaData': { + 'usi': 'from-api-server dude', + 'voidAuIds': '00000000000abcde;00000000000fffff', + 'randomApiKey': 'randomApiValue' + } } } const serverVideoResponse = { body: { 'adUnits': [ { - 'auId': '000000000008b6bc', - 'targetId': '123', - 'html': '

hi!

', + 'auId': '0000000000000551', + 'targetId': 'adn-0000000000000551', + 'vastXml': '\\n\\n \\n Adnuntius\\n adn-id-500662301\\n \\n \\n \\n \\n 00:00:15\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n\\n', 'matchedAdCount': 1, - 'responseId': 'adn-rsp-1460129238', + 'responseId': '', + 'deals': [ + { + 'destinationUrlEsc': '', + 'assets': { + 'Video': { + 'cdnId': 'http://localhost:8079/cdn/9urJusYWpjFDLcpOwfejrkWlLP1heM3vWIJjuHk48BQ.mp4', + 'width': '1920', + 'height': '1080' + } + }, + 'text': { + 'thirdQuartile': { + 'content': 'whatevs', + 'length': '7', + 'minLength': '1', + 'maxLength': '64' + }, + 'complete': { + 'content': 'whatevs', + 'length': '7', + 'minLength': '1', + 'maxLength': '64' + }, + 'firstQuartile': { + 'content': 'whatevs', + 'length': '7', + 'minLength': '1', + 'maxLength': '64' + } + }, + 'choices': {}, + 'clickUrl': 'http://localhost:8078/c/7MRhdEcIndSrWxWhFtMVVnDULl8BOwEaV6vi9558AbYAAAAQCtjQz9kbGWD4nuZy3q6HaCYx_6vZlT33ShgzYb616hZZSpoy2jkAHkGrB-XmXyJmEOCSQ0PLk6_FNLuCBzONJyx6ZZCB10wGqA3OaSe1EqMWoJp41f83FcJLX0SpBoT1-qBbx5_8La-ULiAnqaGtMRDYSRqTBZh5DCANFQWnm1fa8rEE9VRgRORwTNxvZFjq5JCSkEQ2peWFIuxFlOYyeX9Kzg', + 'urls': { + 'destinationUrl': 'http://localhost:8078/c/7MRhdEcIndSrWxWhFtMVVnDULl8BOwEaV6vi9558AbYAAAAQCtjQz9kbGWD4nuZy3q6HaCYx_6vZlT33ShgzYb616hZZSpoy2jkAHkGrB-XmXyJmEOCSQ0PLk6_FNLuCBzONJyx6ZZCB10wGqA3OaSe1EqMWoJp41f83FcJLX0SpBoT1-qBbx5_8La-ULiAnqaGtMRDYSRqTBZh5DCANFQWnm1fa8rEE9VRgRORwTNxvZFjq5JCSkEQ2peWFIuxFlOYyeX9Kzg?ct=682&r=http%3A%2F%2Fadnuntius.com' + }, + 'urlsEsc': { + 'destinationUrl': 'http%3A%2F%2Flocalhost%3A8078%2Fc%2F7MRhdEcIndSrWxWhFtMVVnDULl8BOwEaV6vi9558AbYAAAAQCtjQz9kbGWD4nuZy3q6HaCYx_6vZlT33ShgzYb616hZZSpoy2jkAHkGrB-XmXyJmEOCSQ0PLk6_FNLuCBzONJyx6ZZCB10wGqA3OaSe1EqMWoJp41f83FcJLX0SpBoT1-qBbx5_8La-ULiAnqaGtMRDYSRqTBZh5DCANFQWnm1fa8rEE9VRgRORwTNxvZFjq5JCSkEQ2peWFIuxFlOYyeX9Kzg%3Fct%3D682%26r%3Dhttp%253A%252F%252Fadnuntius.com' + }, + 'destinationUrls': { + 'destinationUrl': 'http://adnuntius.com' + }, + 'cpm': { + 'amount': 9, + 'currency': 'USD' + }, + 'bid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'grossBid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'netBid': { + 'amount': 0.0081, + 'currency': 'USD' + }, + 'dealId': 'abc123xyz', + 'impressionTrackingUrls': [], + 'impressionTrackingUrlsEsc': [], + 'adId': 'adn-id-1465065992', + 'selectedColumn': '0', + 'selectedColumnPosition': '0', + 'renderedPixel': 'http://localhost:8078/b/7MRhdEcIndSrWxWhFtMVVnDULl8BOwEaV6vi9558AbYAAAAQCtjQz9kbGWD4nuZy3q6HaCYx_6vZlT33ShgzYb616hZZSpoy2jkAHkGrB-XmXyJmEOCSQ0PLk6_FNLuCBzONJyx6ZZCB10wGqA3OaSe1EqMWoJp41f83FcJLX0SpBoT1-qBbx5_8La-ULiAnqaGtMRDYSRqTBZh5DCANFQWnm1fa8rEE9VRgRORwTNxvZFjq5JCSkEQ2peWFIuxFlOYyeX9Kzg.html', + 'renderedPixelEsc': 'http%3A%2F%2Flocalhost%3A8078%2Fb%2F7MRhdEcIndSrWxWhFtMVVnDULl8BOwEaV6vi9558AbYAAAAQCtjQz9kbGWD4nuZy3q6HaCYx_6vZlT33ShgzYb616hZZSpoy2jkAHkGrB-XmXyJmEOCSQ0PLk6_FNLuCBzONJyx6ZZCB10wGqA3OaSe1EqMWoJp41f83FcJLX0SpBoT1-qBbx5_8La-ULiAnqaGtMRDYSRqTBZh5DCANFQWnm1fa8rEE9VRgRORwTNxvZFjq5JCSkEQ2peWFIuxFlOYyeX9Kzg.html', + 'visibleUrl': 'http://localhost:8078/s?rt=7MRhdEcIndSrWxWhFtMVVnDULl8BOwEaV6vi9558AbYAAAAQCtjQz9kbGWD4nuZy3q6HaCYx_6vZlT33ShgzYb616hZZSpoy2jkAHkGrB-XmXyJmEOCSQ0PLk6_FNLuCBzONJyx6ZZCB10wGqA3OaSe1EqMWoJp41f83FcJLX0SpBoT1-qBbx5_8La-ULiAnqaGtMRDYSRqTBZh5DCANFQWnm1fa8rEE9VRgRORwTNxvZFjq5JCSkEQ2peWFIuxFlOYyeX9Kzg', + 'visibleUrlEsc': 'http%3A%2F%2Flocalhost%3A8078%2Fs%3Frt%3D7MRhdEcIndSrWxWhFtMVVnDULl8BOwEaV6vi9558AbYAAAAQCtjQz9kbGWD4nuZy3q6HaCYx_6vZlT33ShgzYb616hZZSpoy2jkAHkGrB-XmXyJmEOCSQ0PLk6_FNLuCBzONJyx6ZZCB10wGqA3OaSe1EqMWoJp41f83FcJLX0SpBoT1-qBbx5_8La-ULiAnqaGtMRDYSRqTBZh5DCANFQWnm1fa8rEE9VRgRORwTNxvZFjq5JCSkEQ2peWFIuxFlOYyeX9Kzg', + 'viewUrl': 'http://localhost:8078/v?rt=7MRhdEcIndSrWxWhFtMVVnDULl8BOwEaV6vi9558AbYAAAAQCtjQz9kbGWD4nuZy3q6HaCYx_6vZlT33ShgzYb616hZZSpoy2jkAHkGrB-XmXyJmEOCSQ0PLk6_FNLuCBzONJyx6ZZCB10wGqA3OaSe1EqMWoJp41f83FcJLX0SpBoT1-qBbx5_8La-ULiAnqaGtMRDYSRqTBZh5DCANFQWnm1fa8rEE9VRgRORwTNxvZFjq5JCSkEQ2peWFIuxFlOYyeX9Kzg', + 'viewUrlEsc': 'http%3A%2F%2Flocalhost%3A8078%2Fv%3Frt%3D7MRhdEcIndSrWxWhFtMVVnDULl8BOwEaV6vi9558AbYAAAAQCtjQz9kbGWD4nuZy3q6HaCYx_6vZlT33ShgzYb616hZZSpoy2jkAHkGrB-XmXyJmEOCSQ0PLk6_FNLuCBzONJyx6ZZCB10wGqA3OaSe1EqMWoJp41f83FcJLX0SpBoT1-qBbx5_8La-ULiAnqaGtMRDYSRqTBZh5DCANFQWnm1fa8rEE9VRgRORwTNxvZFjq5JCSkEQ2peWFIuxFlOYyeX9Kzg', + 'rt': '7MRhdEcIndSrWxWhFtMVVnDULl8BOwEaV6vi9558AbYAAAAQCtjQz9kbGWD4nuZy3q6HaCYx_6vZlT33ShgzYb616hZZSpoy2jkAHkGrB-XmXyJmEOCSQ0PLk6_FNLuCBzONJyx6ZZCB10wGqA3OaSe1EqMWoJp41f83FcJLX0SpBoT1-qBbx5_8La-ULiAnqaGtMRDYSRqTBZh5DCANFQWnm1fa8rEE9VRgRORwTNxvZFjq5JCSkEQ2peWFIuxFlOYyeX9Kzg', + 'creativeWidth': '640', + 'creativeHeight': '480', + 'creativeId': 'p6sqtvcgxczy258j', + 'lineItemId': 'cr3hnkkxhnkw9ldy', + 'layoutId': 'buyers_networkvast_single_format_layout', + 'layoutName': 'Vast (video upload)', + 'layoutExternalReference': '', + 'vastXml': '\n\n \n Adnuntius\n adn-id-1465065992\n \n \n \n \n 00:00:15\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n', + 'renderTemplate': '\n \n Adnuntius\n {{{adId}}}\n \n \n {{#ifEquals vastVersion "4.0"}}\n {{/ifEquals}}\n \n {{assets.Video.duration}}\n \n \n {{#unless preview}}{{#each impressionTrackingUrls}}\n {{/each}}{{/unless}}{{#if text.firstQuartile.content}}\n {{/if}}\n {{#if text.thirdQuartile.content}}\n {{/if}}{{#if text.complete.content}}\n {{/if}}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n' + } + ], 'ads': [ { - 'destinationUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fc%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN%3Fct%3D2501%26r%3Dhttp%253A%252F%252Fgoogle.com', + 'destinationUrlEsc': '', + 'dealId': 'not-in-deal-array', 'assets': { - 'image': { - 'cdnId': 'https://assets.adnuntius.com/oEmZa5uYjxENfA1R692FVn6qIveFpO8wUbpyF2xSOCc.jpg', - 'width': '980', - 'height': '120' + 'Video': { + 'cdnId': 'http://localhost:8079/cdn/9urJusYWpjFDLcpOwfejrkWlLP1heM3vWIJjuHk48BQ.mp4', + 'width': '1920', + 'height': '1080' + } + }, + 'text': { + 'thirdQuartile': { + 'content': 'whatevs', + 'length': '7', + 'minLength': '1', + 'maxLength': '64' + }, + 'complete': { + 'content': 'whatevs', + 'length': '7', + 'minLength': '1', + 'maxLength': '64' + }, + 'firstQuartile': { + 'content': 'whatevs', + 'length': '7', + 'minLength': '1', + 'maxLength': '64' } }, - 'clickUrl': 'https://delivery.adnuntius.com/c/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', + 'choices': {}, + 'clickUrl': 'http://localhost:8078/c/ZxGSsEJ8IzI8iCTQ17fHxnE29-y7VILLg2CLjhEbphMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxpfXelzygS0s1MLax6koIS8pnjDAOShX4CbPkDyIzELe5BlfLk6_FNLuCBzOMJyx6ZZCB10wGqA3OaSe1EqpGosot0vsyFMtIWRWtUoKk-aANx531KaOWLyonoP2uC6UpxkXRUJ8iVCcMF1KmmAfe9rNYplI0E-ErHtVvZgm7uZeal_VymQxr0zkhjS_bW0PX', 'urls': { - 'destination': 'https://delivery.adnuntius.com/c/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN?ct=2501&r=http%3A%2F%2Fgoogle.com' + 'destinationUrl': 'http://localhost:8078/c/ZxGSsEJ8IzI8iCTQ17fHxnE29-y7VILLg2CLjhEbphMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxpfXelzygS0s1MLax6koIS8pnjDAOShX4CbPkDyIzELe5BlfLk6_FNLuCBzOMJyx6ZZCB10wGqA3OaSe1EqpGosot0vsyFMtIWRWtUoKk-aANx531KaOWLyonoP2uC6UpxkXRUJ8iVCcMF1KmmAfe9rNYplI0E-ErHtVvZgm7uZeal_VymQxr0zkhjS_bW0PX?ct=682&r=http%3A%2F%2Fadnuntius.com' }, 'urlsEsc': { - 'destination': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fc%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN%3Fct%3D2501%26r%3Dhttp%253A%252F%252Fgoogle.com' + 'destinationUrl': 'http%3A%2F%2Flocalhost%3A8078%2Fc%2FZxGSsEJ8IzI8iCTQ17fHxnE29-y7VILLg2CLjhEbphMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxpfXelzygS0s1MLax6koIS8pnjDAOShX4CbPkDyIzELe5BlfLk6_FNLuCBzOMJyx6ZZCB10wGqA3OaSe1EqpGosot0vsyFMtIWRWtUoKk-aANx531KaOWLyonoP2uC6UpxkXRUJ8iVCcMF1KmmAfe9rNYplI0E-ErHtVvZgm7uZeal_VymQxr0zkhjS_bW0PX%3Fct%3D682%26r%3Dhttp%253A%252F%252Fadnuntius.com' }, 'destinationUrls': { - 'destination': 'http://google.com' + 'destinationUrl': 'http://adnuntius.com' + }, + 'cpm': { + 'amount': 9, + 'currency': 'USD' + }, + 'bid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'grossBid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'netBid': { + 'amount': 0.0081, + 'currency': 'USD' }, - 'cpm': { 'amount': 5.0, 'currency': 'NOK' }, - 'bid': { 'amount': 0.005, 'currency': 'NOK' }, - 'cost': { 'amount': 0.005, 'currency': 'NOK' }, 'impressionTrackingUrls': [], 'impressionTrackingUrlsEsc': [], - 'adId': 'adn-id-1347343135', + 'adId': 'adn-id-500662301', 'selectedColumn': '0', 'selectedColumnPosition': '0', - 'renderedPixel': 'https://delivery.adnuntius.com/b/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN.html', - 'renderedPixelEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fb%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN.html', - 'visibleUrl': 'https://delivery.adnuntius.com/s?rt=52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - 'visibleUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fs%3Frt%3D52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - 'viewUrl': 'https://delivery.adnuntius.com/v?rt=52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - 'viewUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fv%3Frt%3D52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - 'rt': '52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - 'creativeWidth': '980', - 'creativeHeight': '120', - 'creativeId': 'wgkq587vgtpchsx1', - 'lineItemId': 'scyjdyv3mzgdsnpf', - 'layoutId': 'sw6gtws2rdj1kwby', - 'layoutName': 'Responsive image' - }, - + 'renderedPixel': 'http://localhost:8078/b/ZxGSsEJ8IzI8iCTQ17fHxnE29-y7VILLg2CLjhEbphMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxpfXelzygS0s1MLax6koIS8pnjDAOShX4CbPkDyIzELe5BlfLk6_FNLuCBzOMJyx6ZZCB10wGqA3OaSe1EqpGosot0vsyFMtIWRWtUoKk-aANx531KaOWLyonoP2uC6UpxkXRUJ8iVCcMF1KmmAfe9rNYplI0E-ErHtVvZgm7uZeal_VymQxr0zkhjS_bW0PX.html', + 'renderedPixelEsc': 'http%3A%2F%2Flocalhost%3A8078%2Fb%2FZxGSsEJ8IzI8iCTQ17fHxnE29-y7VILLg2CLjhEbphMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxpfXelzygS0s1MLax6koIS8pnjDAOShX4CbPkDyIzELe5BlfLk6_FNLuCBzOMJyx6ZZCB10wGqA3OaSe1EqpGosot0vsyFMtIWRWtUoKk-aANx531KaOWLyonoP2uC6UpxkXRUJ8iVCcMF1KmmAfe9rNYplI0E-ErHtVvZgm7uZeal_VymQxr0zkhjS_bW0PX.html', + 'visibleUrl': 'http://localhost:8078/s?rt=ZxGSsEJ8IzI8iCTQ17fHxnE29-y7VILLg2CLjhEbphMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxpfXelzygS0s1MLax6koIS8pnjDAOShX4CbPkDyIzELe5BlfLk6_FNLuCBzOMJyx6ZZCB10wGqA3OaSe1EqpGosot0vsyFMtIWRWtUoKk-aANx531KaOWLyonoP2uC6UpxkXRUJ8iVCcMF1KmmAfe9rNYplI0E-ErHtVvZgm7uZeal_VymQxr0zkhjS_bW0PX', + 'visibleUrlEsc': 'http%3A%2F%2Flocalhost%3A8078%2Fs%3Frt%3DZxGSsEJ8IzI8iCTQ17fHxnE29-y7VILLg2CLjhEbphMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxpfXelzygS0s1MLax6koIS8pnjDAOShX4CbPkDyIzELe5BlfLk6_FNLuCBzOMJyx6ZZCB10wGqA3OaSe1EqpGosot0vsyFMtIWRWtUoKk-aANx531KaOWLyonoP2uC6UpxkXRUJ8iVCcMF1KmmAfe9rNYplI0E-ErHtVvZgm7uZeal_VymQxr0zkhjS_bW0PX', + 'viewUrl': 'http://localhost:8078/v?rt=ZxGSsEJ8IzI8iCTQ17fHxnE29-y7VILLg2CLjhEbphMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxpfXelzygS0s1MLax6koIS8pnjDAOShX4CbPkDyIzELe5BlfLk6_FNLuCBzOMJyx6ZZCB10wGqA3OaSe1EqpGosot0vsyFMtIWRWtUoKk-aANx531KaOWLyonoP2uC6UpxkXRUJ8iVCcMF1KmmAfe9rNYplI0E-ErHtVvZgm7uZeal_VymQxr0zkhjS_bW0PX', + 'viewUrlEsc': 'http%3A%2F%2Flocalhost%3A8078%2Fv%3Frt%3DZxGSsEJ8IzI8iCTQ17fHxnE29-y7VILLg2CLjhEbphMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxpfXelzygS0s1MLax6koIS8pnjDAOShX4CbPkDyIzELe5BlfLk6_FNLuCBzOMJyx6ZZCB10wGqA3OaSe1EqpGosot0vsyFMtIWRWtUoKk-aANx531KaOWLyonoP2uC6UpxkXRUJ8iVCcMF1KmmAfe9rNYplI0E-ErHtVvZgm7uZeal_VymQxr0zkhjS_bW0PX', + 'rt': 'ZxGSsEJ8IzI8iCTQ17fHxnE29-y7VILLg2CLjhEbphMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxpfXelzygS0s1MLax6koIS8pnjDAOShX4CbPkDyIzELe5BlfLk6_FNLuCBzOMJyx6ZZCB10wGqA3OaSe1EqpGosot0vsyFMtIWRWtUoKk-aANx531KaOWLyonoP2uC6UpxkXRUJ8iVCcMF1KmmAfe9rNYplI0E-ErHtVvZgm7uZeal_VymQxr0zkhjS_bW0PX', + 'creativeWidth': '640', + 'creativeHeight': '480', + 'creativeId': 'p6sqtvcgxczy258j', + 'lineItemId': 'cr3hnkkxhnkw9ldy', + 'layoutId': 'buyers_networkvast_single_format_layout', + 'layoutName': 'Vast (video upload)', + 'layoutExternalReference': '', + 'vastXml': '\n \n Adnuntius\n adn-id-500662301\n \n \n \n \n 00:00:15\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n', + 'renderTemplate': '\n \n Adnuntius\n {{{adId}}}\n \n \n {{#ifEquals vastVersion "4.0"}}\n {{/ifEquals}}\n \n {{assets.Video.duration}}\n \n \n {{#unless preview}}{{#each impressionTrackingUrls}}\n {{/each}}{{/unless}}{{#if text.firstQuartile.content}}\n {{/if}}\n {{#if text.thirdQuartile.content}}\n {{/if}}{{#if text.complete.content}}\n {{/if}}\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n' + } ] }, { 'auId': '000000000008b6bc', - 'targetId': '456', + 'targetId': 'adn-000000000008b6bc', 'matchedAdCount': 0, 'responseId': 'adn-rsp-1460129238', } ] } } - // const serverNativeResponse = { - // body: { - // 'adUnits': [ - // { - // 'auId': '000000000008b6bc', - // 'targetId': '123', - // 'html': '

hi!

', - // 'matchedAdCount': 1, - // 'responseId': 'adn-rsp-1460129238', - // 'ads': [ - // { - // 'destinationUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fc%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN%3Fct%3D2501%26r%3Dhttp%253A%252F%252Fgoogle.com', - // 'assets': { - // 'image': { - // 'cdnId': 'https://assets.adnuntius.com/K9rfXC6wJvgVuy4Fbt5P8oEEGXme9ZaP8BNDzz3OMGQ.jpg', - // 'width': '300', - // 'height': '250' - // } - // }, - // 'text': { - // 'body': { - // 'content': 'Testing Native ad from Adnuntius', - // 'length': '32', - // 'minLength': '0', - // 'maxLength': '100' - // }, - // 'title': { - // 'content': 'Native Ad', - // 'length': '9', - // 'minLength': '5', - // 'maxLength': '100' - // } - // }, - // 'clickUrl': 'https://delivery.adnuntius.com/c/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - // 'urls': { - // 'destination': 'https://delivery.adnuntius.com/c/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN?ct=2501&r=http%3A%2F%2Fgoogle.com' - // }, - // 'urlsEsc': { - // 'destination': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fc%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN%3Fct%3D2501%26r%3Dhttp%253A%252F%252Fgoogle.com' - // }, - // 'destinationUrls': { - // 'destination': 'http://google.com' - // }, - // 'cpm': { 'amount': 5.0, 'currency': 'NOK' }, - // 'bid': { 'amount': 0.005, 'currency': 'NOK' }, - // 'cost': { 'amount': 0.005, 'currency': 'NOK' }, - // 'impressionTrackingUrls': [], - // 'impressionTrackingUrlsEsc': [], - // 'adId': 'adn-id-1347343135', - // 'selectedColumn': '0', - // 'selectedColumnPosition': '0', - // 'renderedPixel': 'https://delivery.adnuntius.com/b/52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN.html', - // 'renderedPixelEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fb%2F52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN.html', - // 'visibleUrl': 'https://delivery.adnuntius.com/s?rt=52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - // 'visibleUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fs%3Frt%3D52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - // 'viewUrl': 'https://delivery.adnuntius.com/v?rt=52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - // 'viewUrlEsc': 'https%3A%2F%2Fdelivery.adnuntius.com%2Fv%3Frt%3D52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - // 'rt': '52AHNuxCqxB_Y9ZP9ERWkMBPCOha4zuV3aKn5cog5jsAAAAQCtjQz9kbGWD4nuZy3q6HaHGLB4-k_fySWECIOOmHKY6iokgHNFH-U57ew_-1QHlKnFr2NT8y4QK1oU5HxnDLbYPz-GmQ3C2JyxLGpKmIb-P-3bm7HYPEreNjPdhjRG51A8NGuc4huUhns7nEUejHuOjOHE5sV1zfYxCRWRx9wPDN9EUCC7KN', - // 'creativeWidth': '980', - // 'creativeHeight': '120', - // 'creativeId': 'wgkq587vgtpchsx1', - // 'lineItemId': 'scyjdyv3mzgdsnpf', - // 'layoutId': 'sw6gtws2rdj1kwby', - // 'layoutName': 'Responsive image' - // }, - - // ] - // }, - // { - // 'auId': '000000000008b6bc', - // 'targetId': '456', - // 'matchedAdCount': 0, - // 'responseId': 'adn-rsp-1460129238', - // } - // ] - // } - // } - - describe('inherited functions', function () { - it('exists and is a function', function () { + + describe('inherited functions', function() { + it('exists and is a function', function() { expect(adapter.callBids).to.exist.and.to.be.a('function'); }); }); - describe('isBidRequestValid', function () { - it('should return true when required params found', function () { + describe('isBidRequestValid', function() { + it('should return true when required params found', function() { expect(spec.isBidRequestValid(bidderRequests[0])).to.equal(true); }); }); - describe('buildRequests', function () { - it('Test requests', function () { + describe('buildRequests', function() { + it('Test requests', function() { const request = spec.buildRequests(bidderRequests, {}); expect(request.length).to.equal(1); expect(request[0]).to.have.property('bid'); @@ -338,10 +454,10 @@ describe('adnuntiusBidAdapter', function () { expect(request[0]).to.have.property('url'); expect(request[0].url).to.equal(ENDPOINT_URL); expect(request[0]).to.have.property('data'); - expect(request[0].data).to.equal('{\"adUnits\":[{\"auId\":\"8b6bc\",\"targetId\":\"123\",\"dimensions\":[[640,480],[600,400]]}],\"metaData\":{\"usi\":\"' + usi + '\"}}'); + expect(request[0].data).to.equal('{"adUnits":[{"auId":"000000000008b6bc","targetId":"adn-000000000008b6bc","maxDeals":1,"dimensions":[[640,480],[600,400]]},{"auId":"0000000000000551","targetId":"adn-0000000000000551","maxDeals":0,"dimensions":[[1640,1480],[1600,1400]]}],"metaData":{"usi":"' + usi + '"}}'); }); - it('Test Video requests', function () { + it('Test Video requests', function() { const request = spec.buildRequests(videoBidderRequest, {}); expect(request.length).to.equal(1); expect(request[0]).to.have.property('bid'); @@ -351,12 +467,12 @@ describe('adnuntiusBidAdapter', function () { expect(request[0].url).to.equal(ENDPOINT_URL_VIDEO); }); - it('should pass segments if available in config', function () { + it('should pass segments if available in config', function() { const ortb2 = { user: { data: [{ name: 'adnuntius', - segment: [{ id: 'segment1' }, { id: 'segment2' }] + segment: [{id: 'segment1'}, {id: 'segment2'}] }, { name: 'other', @@ -365,18 +481,18 @@ describe('adnuntiusBidAdapter', function () { } }; - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {ortb2})); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); }); - it('should skip segments in config if not either id or array of strings', function () { + it('should skip segments in config if not either id or array of strings', function() { const ortb2 = { user: { data: [{ name: 'adnuntius', - segment: [{ id: 'segment1' }, { id: 'segment2' }, { id: 'segment3' }] + segment: [{id: 'segment1'}, {id: 'segment2'}, {id: 'segment3'}] }, { name: 'other', @@ -387,34 +503,34 @@ describe('adnuntiusBidAdapter', function () { } }; - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {ortb2})); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); }); }); - describe('user privacy', function () { - it('should send GDPR Consent data if gdprApplies', function () { - let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: true, consentString: 'consentString' } }); + describe('user privacy', function() { + it('should send GDPR Consent data if gdprApplies', function() { + let request = spec.buildRequests(bidderRequests, {gdprConsent: {gdprApplies: true, consentString: 'consentString'}}); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_CONSENT); }); - it('should not send GDPR Consent data if gdprApplies equals undefined', function () { - let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'consentString' } }); + it('should not send GDPR Consent data if gdprApplies equals undefined', function() { + let request = spec.buildRequests(bidderRequests, {gdprConsent: {gdprApplies: undefined, consentString: 'consentString'}}); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL); }); - it('should pass segments if available in config', function () { + it('should pass segments if available in config', function() { const ortb2 = { user: { data: [{ name: 'adnuntius', - segment: [{ id: 'segment1' }, { id: 'segment2' }] + segment: [{id: 'segment1'}, {id: 'segment2'}] }, { name: 'other', @@ -423,18 +539,18 @@ describe('adnuntiusBidAdapter', function () { } } - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {ortb2})); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); }); - it('should skip segments in config if not either id or array of strings', function () { + it('should skip segments in config if not either id or array of strings', function() { const ortb2 = { user: { data: [{ name: 'adnuntius', - segment: [{ id: 'segment1' }, { id: 'segment2' }, { id: 'segment3' }] + segment: [{id: 'segment1'}, {id: 'segment2'}, {id: 'segment3'}] }, { name: 'other', @@ -445,44 +561,44 @@ describe('adnuntiusBidAdapter', function () { } }; - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {ortb2})); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_SEGMENTS); }); - it('should user user ID if present in ortb2.user.id field', function () { + it('should user user ID if present in ortb2.user.id field', function() { const ortb2 = { user: { id: usi } }; - const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, { ortb2 })); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {ortb2})); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL); }); }); - describe('user privacy', function () { - it('should send GDPR Consent data if gdprApplies', function () { - let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: true, consentString: 'consentString' } }); + describe('user privacy', function() { + it('should send GDPR Consent data if gdprApplies', function() { + let request = spec.buildRequests(bidderRequests, {gdprConsent: {gdprApplies: true, consentString: 'consentString'}}); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL_CONSENT); }); - it('should not send GDPR Consent data if gdprApplies equals undefined', function () { - let request = spec.buildRequests(bidderRequests, { gdprConsent: { gdprApplies: undefined, consentString: 'consentString' } }); + it('should not send GDPR Consent data if gdprApplies equals undefined', function() { + let request = spec.buildRequests(bidderRequests, {gdprConsent: {gdprApplies: undefined, consentString: 'consentString'}}); expect(request.length).to.equal(1); expect(request[0]).to.have.property('url') expect(request[0].url).to.equal(ENDPOINT_URL); }); }); - describe('use cookie', function () { - it('should send noCookie in url if set to false.', function () { + describe('use cookie', function() { + it('should send noCookie in url if set to false.', function() { config.setBidderConfig({ bidders: ['adnuntius'], config: { @@ -497,56 +613,211 @@ describe('adnuntiusBidAdapter', function () { }); }); - describe('interpretResponse', function () { - it('should return valid response when passed valid server response', function () { + describe('validate auId', function() { + it('should fail when auId is not hexadecimal', function() { + const invalidRequest = { + bidId: 'adn-000000000008b6bc', + bidder: 'adnuntius', + params: { + auId: 'nothexadecimal', + } + }; + const valid = config.runWithBidder('adnuntius', () => spec.isBidRequestValid(invalidRequest)); + expect(valid).to.equal(false); + }); + + it('should pass when auId is hexadecimal', function() { + const invalidRequest = { + bidId: 'adn-000000000008b6bc', + bidder: 'adnuntius', + params: { + auId: '0123456789abcDEF', + } + }; + const valid = config.runWithBidder('adnuntius', () => spec.isBidRequestValid(invalidRequest)); + expect(valid).to.equal(true); + }); + }); + + describe('request deals', function() { + it('Should set max deals.', function() { + config.setBidderConfig({ + bidders: ['adnuntius'] + }); + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {})); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL); + expect(request[0]).to.have.property('data'); + const data = JSON.parse(request[0].data); + expect(data.adUnits.length).to.equal(2); + expect(bidderRequests[0].params.maxDeals).to.equal(1); + expect(data.adUnits[0].maxDeals).to.equal(bidderRequests[0].params.maxDeals); + expect(bidderRequests[1].params).to.not.have.property('maxBids'); + expect(data.adUnits[1].maxDeals).to.equal(0); + }); + it('Should allow a maximum of 5 deals.', function() { + config.setBidderConfig({ + bidders: ['adnuntius'], + }); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests([ + { + bidId: 'adn-000000000008b6bc', + bidder: 'adnuntius', + params: { + auId: '000000000008b6bc', + network: 'adnuntius', + maxDeals: 10 + } + } + ], {})); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL); + expect(request[0]).to.have.property('data'); + const data = JSON.parse(request[0].data); + expect(data.adUnits.length).to.equal(1); + expect(data.adUnits[0].maxDeals).to.equal(5); + }); + it('Should allow a minumum of 0 deals.', function() { + config.setBidderConfig({ + bidders: ['adnuntius'], + }); + const request = config.runWithBidder('adnuntius', () => spec.buildRequests([ + { + bidId: 'adn-000000000008b6bc', + bidder: 'adnuntius', + params: { + auId: '000000000008b6bc', + network: 'adnuntius', + maxDeals: -1 + } + } + ], {})); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL); + expect(request[0]).to.have.property('data'); + const data = JSON.parse(request[0].data); + expect(data.adUnits.length).to.equal(1); + expect(data.adUnits[0].maxDeals).to.equal(0); + }); + it('Should set max deals using bidder config.', function() { + config.setBidderConfig({ + bidders: ['adnuntius'], + config: { + maxDeals: 2 + } + }); + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {})); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL + '&ds=2'); + }); + it('Should allow a maximum of 5 deals when using bidder config.', function() { + config.setBidderConfig({ + bidders: ['adnuntius'], + config: { + maxDeals: 6 + } + }); + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {})); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + expect(request[0].url).to.equal(ENDPOINT_URL + '&ds=5'); + }); + it('Should allow a minimum of 0 deals when using bidder config.', function() { + config.setBidderConfig({ + bidders: ['adnuntius'], + config: { + maxDeals: -1 + } + }); + + const request = config.runWithBidder('adnuntius', () => spec.buildRequests(bidderRequests, {})); + expect(request.length).to.equal(1); + expect(request[0]).to.have.property('url') + // The maxDeals value is ignored because it is less than zero + expect(request[0].url).to.equal(ENDPOINT_URL); + }); + }); + + describe('interpretResponse', function() { + it('should return valid response when passed valid server response', function() { const interpretedResponse = spec.interpretResponse(serverResponse, singleBidRequest); - const ad = serverResponse.body.adUnits[0].ads[0] - expect(interpretedResponse).to.have.lengthOf(1); - expect(interpretedResponse[0].cpm).to.equal(ad.cpm.amount); - expect(interpretedResponse[0].width).to.equal(Number(ad.creativeWidth)); - expect(interpretedResponse[0].height).to.equal(Number(ad.creativeHeight)); - expect(interpretedResponse[0].creativeId).to.equal(ad.creativeId); - expect(interpretedResponse[0].currency).to.equal(ad.bid.currency); + expect(interpretedResponse).to.have.lengthOf(2); + + const deal = serverResponse.body.adUnits[0].deals[0]; + expect(interpretedResponse[0].bidderCode).to.equal('adnuntius'); + expect(interpretedResponse[0].cpm).to.equal(deal.bid.amount * 1000); + expect(interpretedResponse[0].width).to.equal(Number(deal.creativeWidth)); + expect(interpretedResponse[0].height).to.equal(Number(deal.creativeHeight)); + expect(interpretedResponse[0].creativeId).to.equal(deal.creativeId); + expect(interpretedResponse[0].currency).to.equal(deal.bid.currency); expect(interpretedResponse[0].netRevenue).to.equal(false); expect(interpretedResponse[0].meta).to.have.property('advertiserDomains'); expect(interpretedResponse[0].meta.advertiserDomains).to.have.lengthOf(1); - expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal('google.com'); - expect(interpretedResponse[0].ad).to.equal(serverResponse.body.adUnits[0].html); + expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal('adnuntius.com'); + expect(interpretedResponse[0].ad).to.equal(serverResponse.body.adUnits[0].deals[0].html); expect(interpretedResponse[0].ttl).to.equal(360); + expect(interpretedResponse[0].dealId).to.equal('abc123xyz'); + expect(interpretedResponse[0].dealCount).to.equal(1); + + const ad = serverResponse.body.adUnits[0].ads[0]; + expect(interpretedResponse[1].bidderCode).to.equal('adnuntius'); + expect(interpretedResponse[1].cpm).to.equal(ad.bid.amount * 1000); + expect(interpretedResponse[1].width).to.equal(Number(ad.creativeWidth)); + expect(interpretedResponse[1].height).to.equal(Number(ad.creativeHeight)); + expect(interpretedResponse[1].creativeId).to.equal(ad.creativeId); + expect(interpretedResponse[1].currency).to.equal(ad.bid.currency); + expect(interpretedResponse[1].netRevenue).to.equal(false); + expect(interpretedResponse[1].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[1].meta.advertiserDomains).to.have.lengthOf(1); + expect(interpretedResponse[1].meta.advertiserDomains[0]).to.equal('adnuntius.com'); + expect(interpretedResponse[1].ad).to.equal(serverResponse.body.adUnits[0].html); + expect(interpretedResponse[1].ttl).to.equal(360); + expect(interpretedResponse[1].dealId).to.equal('not-in-deal-array-here'); + expect(interpretedResponse[1].dealCount).to.equal(0); }); }); - describe('interpretVideoResponse', function () { - it('should return valid response when passed valid server response', function () { + + describe('interpretVideoResponse', function() { + it('should return valid response when passed valid server response', function() { const interpretedResponse = spec.interpretResponse(serverVideoResponse, videoBidRequest); const ad = serverVideoResponse.body.adUnits[0].ads[0] - expect(interpretedResponse).to.have.lengthOf(1); - expect(interpretedResponse[0].cpm).to.equal(ad.cpm.amount); - expect(interpretedResponse[0].width).to.equal(Number(ad.creativeWidth)); - expect(interpretedResponse[0].height).to.equal(Number(ad.creativeHeight)); - expect(interpretedResponse[0].creativeId).to.equal(ad.creativeId); - expect(interpretedResponse[0].currency).to.equal(ad.bid.currency); + const deal = serverVideoResponse.body.adUnits[0].deals[0] + expect(interpretedResponse).to.have.lengthOf(2); + + expect(interpretedResponse[0].bidderCode).to.equal('adnuntius'); + expect(interpretedResponse[0].cpm).to.equal(deal.bid.amount * 1000); + expect(interpretedResponse[0].width).to.equal(Number(deal.creativeWidth)); + expect(interpretedResponse[0].height).to.equal(Number(deal.creativeHeight)); + expect(interpretedResponse[0].creativeId).to.equal(deal.creativeId); + expect(interpretedResponse[0].currency).to.equal(deal.bid.currency); expect(interpretedResponse[0].netRevenue).to.equal(false); expect(interpretedResponse[0].meta).to.have.property('advertiserDomains'); expect(interpretedResponse[0].meta.advertiserDomains).to.have.lengthOf(1); - expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal('google.com'); - expect(interpretedResponse[0].vastXml).to.equal(serverVideoResponse.body.adUnits[0].vastXml); + expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal('adnuntius.com'); + expect(interpretedResponse[0].vastXml).to.equal(deal.vastXml); + expect(interpretedResponse[0].dealId).to.equal('abc123xyz'); + expect(interpretedResponse[0].dealCount).to.equal(1); + + expect(interpretedResponse[1].bidderCode).to.equal('adnuntius'); + expect(interpretedResponse[1].cpm).to.equal(ad.bid.amount * 1000); + expect(interpretedResponse[1].width).to.equal(Number(ad.creativeWidth)); + expect(interpretedResponse[1].height).to.equal(Number(ad.creativeHeight)); + expect(interpretedResponse[1].creativeId).to.equal(ad.creativeId); + expect(interpretedResponse[1].currency).to.equal(ad.bid.currency); + expect(interpretedResponse[1].netRevenue).to.equal(false); + expect(interpretedResponse[1].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[1].meta.advertiserDomains).to.have.lengthOf(1); + expect(interpretedResponse[1].meta.advertiserDomains[0]).to.equal('adnuntius.com'); + expect(interpretedResponse[1].vastXml).to.equal(serverVideoResponse.body.adUnits[0].vastXml); + expect(interpretedResponse[1].dealId).to.equal('not-in-deal-array'); + expect(interpretedResponse[1].dealCount).to.equal(0); }); }); - // describe('interpretNativeResponse', function () { - // it('should return valid response when passed valid server response', function () { - // const interpretedResponse = spec.interpretResponse(serverNativeResponse, nativeBidRequest); - // const ad = serverNativeResponse.body.adUnits[0].ads[0] - // expect(interpretedResponse).to.have.lengthOf(1); - // expect(interpretedResponse[0].cpm).to.equal(ad.cpm.amount); - // expect(interpretedResponse[0].width).to.equal(Number(ad.creativeWidth)); - // expect(interpretedResponse[0].height).to.equal(Number(ad.creativeHeight)); - // expect(interpretedResponse[0].creativeId).to.equal(ad.creativeId); - // expect(interpretedResponse[0].currency).to.equal(ad.bid.currency); - // expect(interpretedResponse[0].netRevenue).to.equal(false); - // expect(interpretedResponse[0].meta).to.have.property('advertiserDomains'); - // expect(interpretedResponse[0].meta.advertiserDomains).to.have.lengthOf(1); - // expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal('google.com'); - // expect(interpretedResponse[0].native.body).to.equal(serverNativeResponse.body.adUnits[0].ads[0].text.body.content); - // }); - // }); }); From 5ac6dccf8e6f53a43a5730d651395d74f0f4d1e6 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Mon, 22 May 2023 10:45:45 -0700 Subject: [PATCH 20/26] Core: fix in ortb -> legacy native asset conversion (#9923) --- src/native.js | 24 ++++++++++++++++++++---- test/spec/native_spec.js | 24 ++++++++++++------------ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/native.js b/src/native.js index 79a972371da..927423c8d72 100644 --- a/src/native.js +++ b/src/native.js @@ -556,6 +556,21 @@ export function toOrtbNativeRequest(legacyNativeAssets) { return ortb; } +/** + * Greatest common divisor between two positive integers + * https://en.wikipedia.org/wiki/Euclidean_algorithm + */ +function gcd(a, b) { + while (a && b && a !== b) { + if (a > b) { + a = a - b; + } else { + b = b - a; + } + } + return a || b; +} + /** * This function converts an OpenRTB native request object to Prebid proprietary * format. The purpose of this function is to help adapters to handle the @@ -584,12 +599,13 @@ export function fromOrtbNativeRequest(openRTBRequest) { if (asset.img.w && asset.img.h) { image.sizes = [asset.img.w, asset.img.h]; } else if (asset.img.wmin && asset.img.hmin) { - image.aspect_ratios = { + const scale = gcd(asset.img.wmin, asset.img.hmin) + image.aspect_ratios = [{ min_width: asset.img.wmin, min_height: asset.img.hmin, - ratio_width: asset.img.wmin, - ratio_height: asset.img.hmin - } + ratio_width: asset.img.wmin / scale, + ratio_height: asset.img.hmin / scale + }] } if (asset.img.type === NATIVE_IMAGE_TYPES.MAIN) { diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index 9150329ff60..dee177d4b9b 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -1002,22 +1002,22 @@ describe('validate native', function () { expect(oldNativeRequest.image).to.deep.include({ required: false, - aspect_ratios: { + aspect_ratios: [{ min_width: 836, min_height: 627, - ratio_width: 836, - ratio_height: 627 - } + ratio_width: 4, + ratio_height: 3 + }] }); expect(oldNativeRequest.icon).to.deep.include({ required: true, - aspect_ratios: { + aspect_ratios: [{ min_width: 50, min_height: 50, - ratio_width: 50, - ratio_height: 50 - } + ratio_width: 1, + ratio_height: 1 + }] }); expect(oldNativeRequest.sponsoredBy).to.include({ required: true, @@ -1119,12 +1119,12 @@ describe('validate native', function () { }, icon: { required: true, - aspect_ratios: { + aspect_ratios: [{ min_width: 50, min_height: 50, - ratio_width: 50, - ratio_height: 50 - } + ratio_width: 1, + ratio_height: 1 + }] }, sponsoredBy: { required: true, From bfe4538cccd74a2a7fdbb74d499cc61be5a7f8c2 Mon Sep 17 00:00:00 2001 From: Piotr Jaworski <109736938+piotrj-rtbh@users.noreply.github.com> Date: Tue, 23 May 2023 18:12:23 +0200 Subject: [PATCH 21/26] RTB House Bid Adapter: docs update - FLEDGE support (#9986) * RTB House Bid Adapter: docs update - FLEDGE support * RTB House Bid Adapter: shortening and rewording --- modules/rtbhouseBidAdapter.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/modules/rtbhouseBidAdapter.md b/modules/rtbhouseBidAdapter.md index b8b59aa9edc..31e9377cb96 100644 --- a/modules/rtbhouseBidAdapter.md +++ b/modules/rtbhouseBidAdapter.md @@ -65,3 +65,27 @@ Please reach out to pmp@rtbhouse.com to receive your own } ]; ``` + +# Protected Audience API (FLEDGE) support +There’s an option to receive demand for Protected Audience API (FLEDGE/PAAPI) +ads using RTB House bid adapter. +Prebid’s [fledgeForGpt](https://docs.prebid.org/dev-docs/modules/fledgeForGpt.html) +module and Google Ad Manager is currently required. + +The following steps should be taken to setup Protected Audience for RTB House: + +1. Reach out to your RTB House representative for setup coordination. + +2. Build and enable FLEDGE module as described in +[fledgeForGpt](https://docs.prebid.org/dev-docs/modules/fledgeForGpt.html) +module documentation. + + a. Make sure to enable RTB House bidder to participate in FLEDGE. If there are any other bidders to be allowed for that, add them to the **bidders** array: +```javascript +pbjs.setBidderConfig({ + bidders: ["rtbhouse"], + config: { + fledgeEnabled: true + } +}); +``` From 8e3c263c3374040e27fb96073ddd35d5f7b7c334 Mon Sep 17 00:00:00 2001 From: Damyan Date: Tue, 23 May 2023 19:37:32 +0300 Subject: [PATCH 22/26] AdHash Bid Adapter: changes to support preroll videos (#9870) * AdHash Bidder Adapter: minor changes We're operating on a com TLD now. Added publisher in URL for easier routing. * Implemented brand safety Implemented brand safety checks * Fix for GDPR consent Removing the extra information as request data becomes too big and is sometimes truncated * Ad fraud prevention formula changed Ad fraud prevention formula changed to support negative values as well as linear distribution of article length * AdHash brand safety additions Adding starts-with and ends-with rules that will help us with languages such as German where a single word can be written in multiple ways depending on the gender and grammatical case. * AdHash brand safety updates Added support for Cyrillic characters. Added support for bidderURL parameter. Fixed score multiplier from 500 to 1000. * AdHash Analytics adapter * Support for recent ads Support for recent ads which gives us the option to do frequency and recency capping. * Fix for timestamp * PUB-222 Added logic for measuring the fill rate (fallbacks) for Prebid impressions * Unit tests for the analytics adapter Added unit tests for the analytics adapter * Removed export causing errors Removed an unneeded export of a const that was causing errors with the analytics adapter * Added globalScript parameter * PUB-227 Support for non-latin and non-cyrillic symbols * GEN-964 - Brand safety now checks the page URL for bad words. No ad is shown if there is at least one match. - Repeating code is optimized and moved to helper function - Multi-language support for brand safety * GEN-1025 Sending the needed ad density data to the bidder * Removing the analytics adaptor * Fix for regexp match * Version change * MINOR Code review changes * GEN-1153 Adding support for preroll ads * MINOR Video unit test added * Removing globalScript flag --------- Co-authored-by: NikolayMGeorgiev Co-authored-by: Ventsislav Saraminev Co-authored-by: Dimitar Kalenderov --- modules/adhashBidAdapter.js | 45 ++++++++++++------- test/spec/modules/adhashBidAdapter_spec.js | 52 +++++++++++++++++++++- 2 files changed, 79 insertions(+), 18 deletions(-) diff --git a/modules/adhashBidAdapter.js b/modules/adhashBidAdapter.js index 33a85a81525..08f9466823b 100644 --- a/modules/adhashBidAdapter.js +++ b/modules/adhashBidAdapter.js @@ -1,7 +1,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; import { includes } from '../src/polyfill.js'; -import { BANNER } from '../src/mediaTypes.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; const VERSION = '3.2'; const BAD_WORD_STEP = 0.1; @@ -130,13 +130,13 @@ function brandSafety(badWords, maxScore) { export const spec = { code: ADHASH_BIDDER_CODE, - supportedMediaTypes: [ BANNER ], + supportedMediaTypes: [ BANNER, VIDEO ], isBidRequestValid: (bid) => { try { const { publisherId, platformURL, bidderURL } = bid.params; return ( - includes(Object.keys(bid.mediaTypes), BANNER) && + (includes(Object.keys(bid.mediaTypes), BANNER) || includes(Object.keys(bid.mediaTypes), VIDEO)) && typeof publisherId === 'string' && publisherId.length === 42 && typeof platformURL === 'string' && @@ -168,7 +168,14 @@ export const spec = { const url = `${bidderURL}/rtb?version=${VERSION}&prebid=true`; const index = Math.floor(Math.random() * validBidRequests[i].sizes.length); const size = validBidRequests[i].sizes[index].join('x'); - + const creativeData = includes(Object.keys(validBidRequests[i].mediaTypes), VIDEO) ? { + size: 'preroll', + position: validBidRequests[i].adUnitCode, + playerSize: size + } : { + size: size, + position: validBidRequests[i].adUnitCode + }; let recentAds = []; if (storage.localStorageIsEnabled()) { const prefix = validBidRequests[i].params.prefix || 'adHash'; @@ -204,10 +211,7 @@ export const spec = { language: window.navigator.language, userAgent: window.navigator.userAgent }, - creatives: [{ - size: size, - position: validBidRequests[i].adUnitCode - }], + creatives: [creativeData], blockedCreatives: [], currentTimestamp: (new Date().getTime() / 1000) | 0, recentAds: recentAds, @@ -229,7 +233,6 @@ export const spec = { interpretResponse: (serverResponse, request) => { const responseBody = serverResponse ? serverResponse.body : {}; - if ( !responseBody.creatives || responseBody.creatives.length === 0 || @@ -241,18 +244,12 @@ export const spec = { const publisherURL = JSON.stringify(request.bidRequest.params.platformURL); const bidderURL = request.bidRequest.params.bidderURL || 'https://bidder.adhash.com'; const oneTimeId = request.bidRequest.adUnitCode + Math.random().toFixed(16).replace('0.', '.'); - const globalScript = !request.bidRequest.params.globalScript - ? `` - : ''; const bidderResponse = JSON.stringify({ responseText: JSON.stringify(responseBody) }); const requestData = JSON.stringify(request.data); - return [{ + var response = { requestId: request.bidRequest.bidId, cpm: responseBody.creatives[0].costEUR, - ad: - `
${globalScript} - `, width: request.bidRequest.sizes[0][0], height: request.bidRequest.sizes[0][1], creativeId: request.bidRequest.adUnitCode, @@ -262,7 +259,21 @@ export const spec = { meta: { advertiserDomains: responseBody.advertiserDomains ? [responseBody.advertiserDomains] : [] } - }]; + }; + if (typeof request == 'object' && typeof request.bidRequest == 'object' && typeof request.bidRequest.mediaTypes == 'object' && includes(Object.keys(request.bidRequest.mediaTypes), BANNER)) { + response = Object.assign({ + ad: + `
+ + ` + }, response); + } else if (includes(Object.keys(request.bidRequest.mediaTypes), VIDEO)) { + response = Object.assign({ + vastUrl: responseBody.creatives[0].vastURL, + mediaType: VIDEO + }, response); + } + return [response]; } }; diff --git a/test/spec/modules/adhashBidAdapter_spec.js b/test/spec/modules/adhashBidAdapter_spec.js index 4ea525c59d5..2d3458fff79 100644 --- a/test/spec/modules/adhashBidAdapter_spec.js +++ b/test/spec/modules/adhashBidAdapter_spec.js @@ -74,7 +74,12 @@ describe('adhashBidAdapter', function () { publisherId: '0xc3b09b27e9c6ef73957901aa729b9e69e5bbfbfb' }, sizes: [[300, 250]], - adUnitCode: 'adUnitCode' + adUnitCode: 'adUnitCode', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + } }; it('should build the request correctly', function () { const result = spec.buildRequests( @@ -122,6 +127,11 @@ describe('adhashBidAdapter', function () { sizes: [[300, 250]], params: { platformURL: 'https://adhash.com/p/struma/' + }, + mediaTypes: { + banner: { + sizes: [[300, 250]] + } } } }; @@ -267,5 +277,45 @@ describe('adhashBidAdapter', function () { it('should return empty array when something is not right', function () { expect(spec.interpretResponse(null, request).length).to.equal(0); }); + + it('should interpret the video response correctly', function () { + const result = spec.interpretResponse({ + body: { + creatives: [{ costEUR: 1.234, vastURL: 'https://example.com/vast' }], + advertiserDomains: 'adhash.com' + } + }, { + data: { some: 'data' }, + bidRequest: { + bidId: '12345678901234', + adUnitCode: 'adunit-code', + sizes: [[300, 250]], + params: { + platformURL: 'https://adhash.com/p/struma/' + }, + mediaTypes: { + video: { + context: 'instream', + playerSize: [300, 250], + mimes: ['video/mp4'], + protocols: [1, 2, 3, 4, 5, 6, 7, 8], + playbackmethod: [2], + skip: 1 + } + } + } + }); + expect(result.length).to.equal(1); + expect(result[0].requestId).to.equal('12345678901234'); + expect(result[0].cpm).to.equal(1.234); + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(250); + expect(result[0].creativeId).to.equal('adunit-code'); + expect(result[0].netRevenue).to.equal(true); + expect(result[0].currency).to.equal('EUR'); + expect(result[0].ttl).to.equal(60); + expect(result[0].meta.advertiserDomains).to.eql(['adhash.com']); + expect(result[0].vastUrl).to.equal('https://example.com/vast'); + }); }); }); From c6fa40b3dc636bd912c08ddee55e7ac31c769e99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 19:56:39 -0700 Subject: [PATCH 23/26] Bump socket.io-parser from 4.2.1 to 4.2.3 (#9992) Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 4.2.1 to 4.2.3. - [Release notes](https://github.com/socketio/socket.io-parser/releases) - [Changelog](https://github.com/socketio/socket.io-parser/blob/main/CHANGELOG.md) - [Commits](https://github.com/socketio/socket.io-parser/compare/4.2.1...4.2.3) --- updated-dependencies: - dependency-name: socket.io-parser dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index af1d2cfde5a..0d02c91e2f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "7.48.0-pre", + "version": "7.51.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -21668,9 +21668,9 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", - "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", @@ -42022,9 +42022,9 @@ } }, "socket.io-parser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.1.tgz", - "integrity": "sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", "dev": true, "requires": { "@socket.io/component-emitter": "~3.1.0", From ce9c1646b09e5817114945f182cd551c28ef357a Mon Sep 17 00:00:00 2001 From: eugen-tikhonov <31205179+eugen-tikhonov@users.noreply.github.com> Date: Wed, 24 May 2023 08:42:35 +0300 Subject: [PATCH 24/26] clean.io RTD Module: Use loadExternalScript function instead of insertElement() method to insert the Clean.io script. (#9991) Co-authored-by: yevhen.tykhonov --- modules/cleanioRtdProvider.js | 6 ++---- src/adloader.js | 3 ++- test/spec/modules/cleanioRtdProvider_spec.js | 15 +++++++-------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/modules/cleanioRtdProvider.js b/modules/cleanioRtdProvider.js index 09c5f722d7f..7d0f461108b 100644 --- a/modules/cleanioRtdProvider.js +++ b/modules/cleanioRtdProvider.js @@ -7,6 +7,7 @@ */ import { submodule } from '../src/hook.js'; +import { loadExternalScript } from '../src/adloader.js'; import { logError, generateUUID, insertElement } from '../src/utils.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; @@ -52,10 +53,7 @@ function pageInitStepPreloadScript(scriptURL) { * @param {string} scriptURL The script URL to add to the page for protection */ function pageInitStepProtectPage(scriptURL) { - const scriptElement = document.createElement('script'); - scriptElement.type = 'text/javascript'; - scriptElement.src = scriptURL; - insertElement(scriptElement); + loadExternalScript(scriptURL, 'clean.io'); } /** diff --git a/src/adloader.js b/src/adloader.js index f0b7f7f3e8c..62078293884 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -22,7 +22,8 @@ const _approvedLoadExternalJSList = [ 'improvedigital', 'aaxBlockmeter', 'confiant', - 'arcspan' + 'arcspan', + 'clean.io' ] /** diff --git a/test/spec/modules/cleanioRtdProvider_spec.js b/test/spec/modules/cleanioRtdProvider_spec.js index 8453b633906..1d21fbd8457 100644 --- a/test/spec/modules/cleanioRtdProvider_spec.js +++ b/test/spec/modules/cleanioRtdProvider_spec.js @@ -1,3 +1,4 @@ +import { loadExternalScriptStub } from 'test/mocks/adloaderStub.js'; import * as utils from '../../../src/utils.js'; import * as hook from '../../../src/hook.js' import * as events from '../../../src/events.js'; @@ -68,10 +69,8 @@ describe('clean.io RTD module', function () { it('pageInitStepProtectPage() should insert script element', function() { pageInitStepProtectPage(fakeScriptURL); - sinon.assert.calledOnce(insertElementStub); - sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'SCRIPT')); - sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.type === 'text/javascript')); - sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.src === fakeScriptURL)); + sinon.assert.calledOnce(loadExternalScriptStub); + sinon.assert.calledWith(loadExternalScriptStub, fakeScriptURL, 'clean.io'); }); }); @@ -133,14 +132,14 @@ describe('clean.io RTD module', function () { it('should refuse initialization with incorrect parameters', function () { const { init } = getModule(); expect(init({ params: { cdnUrl: 'abc', protectionMode: 'full' } }, {})).to.equal(false); // too short distribution name - sinon.assert.notCalled(insertElementStub); + sinon.assert.notCalled(loadExternalScriptStub); }); - it('should iniitalize in full (page) protection mode', function () { + it('should initialize in full (page) protection mode', function () { const { init, onBidResponseEvent } = getModule(); expect(init({ params: { cdnUrl: 'https://abc1234567890.cloudfront.net/script.js', protectionMode: 'full' } }, {})).to.equal(true); - sinon.assert.calledOnce(insertElementStub); - sinon.assert.calledWith(insertElementStub, sinon.match(elem => elem.tagName === 'SCRIPT')); + sinon.assert.calledOnce(loadExternalScriptStub); + sinon.assert.calledWith(loadExternalScriptStub, 'https://abc1234567890.cloudfront.net/script.js', 'clean.io'); const fakeBidResponse = makeFakeBidResponse(); onBidResponseEvent(fakeBidResponse, {}, {}); From 892013bfead96216665936bdd83e2a9d601b650c Mon Sep 17 00:00:00 2001 From: YakirLavi <73641910+YakirLavi@users.noreply.github.com> Date: Wed, 24 May 2023 17:46:57 +0300 Subject: [PATCH 25/26] Rise Bid Adapter: support sua and plcmt params. (#9996) * add Rise adapter * fixes * change param isOrg to org * Rise adapter * change email for rise * fix circle failed * bump * bump * bump * remove space * Upgrade Rise adapter to 5.0 * added isWrapper param * addes is_wrapper parameter to documentation * added is_wrapper to test * removed isWrapper * Rise Bid Adapter: support Coppa param (#24) * MinuteMedia Bid Adapter: support Coppa param (#25) * Revert "MinuteMedia Bid Adapter: support Coppa param (#25)" (#26) This reverts commit 66c4e7b46121afc5331c8bca6e2fc972fc55f090. * bump * update coppa fetch * setting coppa param update * update Coppa tests * update test naming * Rise Bid Adapter: support plcmt and sua (#27) --------- Co-authored-by: Noam Tzuberi Co-authored-by: noamtzu Co-authored-by: Noam Tzuberi Co-authored-by: Laslo Chechur Co-authored-by: OronW <41260031+OronW@users.noreply.github.com> Co-authored-by: lasloche <62240785+lasloche@users.noreply.github.com> Co-authored-by: inna Co-authored-by: YakirLavi --- modules/riseBidAdapter.js | 16 +++++- test/spec/modules/riseBidAdapter_spec.js | 70 +++++++++++++++++++++++- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/modules/riseBidAdapter.js b/modules/riseBidAdapter.js index e0f196fb072..1e04ab6239f 100644 --- a/modules/riseBidAdapter.js +++ b/modules/riseBidAdapter.js @@ -320,6 +320,16 @@ function generateBidParameters(bid, bidderRequest) { bidObject.api = api; } + const sua = deepAccess(bid, `ortb2.device.sua`); + if (sua) { + bidObject.sua = sua; + } + + const coppa = deepAccess(bid, `ortb2.regs.coppa`) + if (coppa) { + bidObject.coppa = 1; + } + if (mediaType === VIDEO) { const playbackMethod = deepAccess(bid, `mediaTypes.video.playbackmethod`); let playbackMethodValue; @@ -366,9 +376,9 @@ function generateBidParameters(bid, bidderRequest) { bidObject.protocols = protocols; } - const coppa = deepAccess(bid, 'ortb2.regs.coppa') - if (coppa) { - bidObject.coppa = 1; + const plcmt = deepAccess(bid, `mediaTypes.video.plcmt`); + if (plcmt) { + bidObject.plcmt = plcmt; } } diff --git a/test/spec/modules/riseBidAdapter_spec.js b/test/spec/modules/riseBidAdapter_spec.js index d22cbc01d39..eed8d74f271 100644 --- a/test/spec/modules/riseBidAdapter_spec.js +++ b/test/spec/modules/riseBidAdapter_spec.js @@ -62,7 +62,8 @@ describe('riseAdapter', function () { 'mediaTypes': { 'video': { 'playerSize': [[640, 480]], - 'context': 'instream' + 'context': 'instream', + 'plcmt': 1 } }, 'vastXml': '"..."' @@ -116,6 +117,11 @@ describe('riseAdapter', function () { expect(request.data.bids[0].placementId).to.equal(placementId); }); + it('sends the plcmt to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[0].plcmt).to.equal(1); + }); + it('sends the is_wrapper parameter to ENDPOINT via POST', function() { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.params).to.be.an('object'); @@ -342,6 +348,68 @@ describe('riseAdapter', function () { expect(request.data.bids[0]).to.have.property('floorPrice', 1.5); }); + it('should check sua param in bid request', function() { + const sua = { + 'platform': { + 'brand': 'macOS', + 'version': ['12', '4', '0'] + }, + 'browsers': [ + { + 'brand': 'Chromium', + 'version': [ '106', '0', '5249', '119' ] + }, + { + 'brand': 'Google Chrome', + 'version': [ '106', '0', '5249', '119' ] + }, + { + 'brand': 'Not;A=Brand', + 'version': [ '99', '0', '0', '0' ] + } + ], + 'mobile': 0, + 'model': '', + 'bitness': '64', + 'architecture': 'x86' + } + const bid = utils.deepClone(bidRequests[0]); + bid.ortb2 = { + 'device': { + 'sua': { + 'platform': { + 'brand': 'macOS', + 'version': [ '12', '4', '0' ] + }, + 'browsers': [ + { + 'brand': 'Chromium', + 'version': [ '106', '0', '5249', '119' ] + }, + { + 'brand': 'Google Chrome', + 'version': [ '106', '0', '5249', '119' ] + }, + { + 'brand': 'Not;A=Brand', + 'version': [ '99', '0', '0', '0' ] + } + ], + 'mobile': 0, + 'model': '', + 'bitness': '64', + 'architecture': 'x86' + } + } + } + const requestWithSua = spec.buildRequests([bid], bidderRequest); + const data = requestWithSua.data; + expect(data.bids[0].sua).to.exist; + expect(data.bids[0].sua).to.deep.equal(sua); + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[0].sua).to.not.exist; + }); + describe('COPPA Param', function() { it('should set coppa equal 0 in bid request if coppa is set to false', function() { const request = spec.buildRequests(bidRequests, bidderRequest); From c59dd0695674fecc0c47a9230eea85705cbca174 Mon Sep 17 00:00:00 2001 From: SmartyAdman <59048845+SmartyAdman@users.noreply.github.com> Date: Thu, 25 May 2023 06:26:39 +0300 Subject: [PATCH 26/26] Adman Adapter: remove useless parameter (#9967) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Adman bid adapter * Add supportedMediaTypes property * Update ADman Media bidder adapter * Remove console.log * Fix typo * revert package-json.lock * Delete package-lock.json * back to original package-lock.json * catch pbjs error * catch pbjs error * catch pbjs error * log * remove eu url * remove eu url * remove eu url * remove eu url * remove eu url * Update admanBidAdapter.js add consnet to sync url * Update admanBidAdapter.js fix import * Update admanBidAdapter.js lint fix * Update admanBidAdapter.js lint fix * Update admanBidAdapter.js check consent object data availability * сompatible with prebid v5 * add Lotame Panorama ID * update getUserSyncs * fix * fix tests * remove package-lock.json * update sync url * update test * add idx (UserID Module) * update tests * remove traffic param --------- Co-authored-by: minoru katogi Co-authored-by: minoru katogi Co-authored-by: ADman Media Co-authored-by: SmartyAdman --- modules/admanBidAdapter.js | 52 +++++++++++++---------- test/spec/modules/admanBidAdapter_spec.js | 3 +- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js index ed984645d0d..ff8e6b02ad5 100644 --- a/modules/admanBidAdapter.js +++ b/modules/admanBidAdapter.js @@ -99,43 +99,51 @@ export const spec = { const len = validBidRequests.length; for (let i = 0; i < len; i++) { - let bid = validBidRequests[i]; - let traff = bid.params.traffic || BANNER + const bid = validBidRequests[i]; + const { params, bidId, mediaTypes } = bid; + const placement = { - placementId: bid.params.placementId, - bidId: bid.bidId, - sizes: bid.mediaTypes && bid.mediaTypes[traff] && bid.mediaTypes[traff].sizes ? bid.mediaTypes[traff].sizes : [], - traffic: traff, + placementId: params.placementId, + bidId, eids: [], bidFloor: getBidFloor(bid) } + if (bid.schain) { placement.schain = bid.schain; } + if (bid.userId) { getUserId(placement.eids, bid.userId.uid2 && bid.userId.uid2.id, 'uidapi.com'); getUserId(placement.eids, bid.userId.lotamePanoramaId, 'lotame.com'); getUserId(placement.eids, bid.userId.idx, 'idx.lat'); } - if (traff === VIDEO) { - placement.playerSize = bid.mediaTypes[VIDEO].playerSize; - placement.minduration = bid.mediaTypes[VIDEO].minduration; - placement.maxduration = bid.mediaTypes[VIDEO].maxduration; - placement.mimes = bid.mediaTypes[VIDEO].mimes; - placement.protocols = bid.mediaTypes[VIDEO].protocols; - placement.startdelay = bid.mediaTypes[VIDEO].startdelay; - placement.placement = bid.mediaTypes[VIDEO].placement; - placement.skip = bid.mediaTypes[VIDEO].skip; - placement.skipafter = bid.mediaTypes[VIDEO].skipafter; - placement.minbitrate = bid.mediaTypes[VIDEO].minbitrate; - placement.maxbitrate = bid.mediaTypes[VIDEO].maxbitrate; - placement.delivery = bid.mediaTypes[VIDEO].delivery; - placement.playbackmethod = bid.mediaTypes[VIDEO].playbackmethod; - placement.api = bid.mediaTypes[VIDEO].api; - placement.linearity = bid.mediaTypes[VIDEO].linearity; + + if (mediaTypes?.[BANNER]) { + placement.traffic = BANNER; + placement.sizes = mediaTypes[BANNER].sizes; + } else if (mediaTypes?.[VIDEO]) { + placement.traffic = VIDEO; + placement.playerSize = mediaTypes[VIDEO].playerSize; + placement.minduration = mediaTypes[VIDEO].minduration; + placement.maxduration = mediaTypes[VIDEO].maxduration; + placement.mimes = mediaTypes[VIDEO].mimes; + placement.protocols = mediaTypes[VIDEO].protocols; + placement.startdelay = mediaTypes[VIDEO].startdelay; + placement.placement = mediaTypes[VIDEO].placement; + placement.skip = mediaTypes[VIDEO].skip; + placement.skipafter = mediaTypes[VIDEO].skipafter; + placement.minbitrate = mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; + placement.delivery = mediaTypes[VIDEO].delivery; + placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; + placement.api = mediaTypes[VIDEO].api; + placement.linearity = mediaTypes[VIDEO].linearity; } + placements.push(placement); } + return { method: 'POST', url: AD_URL, diff --git a/test/spec/modules/admanBidAdapter_spec.js b/test/spec/modules/admanBidAdapter_spec.js index feee8e39b45..f1d9a7047d3 100644 --- a/test/spec/modules/admanBidAdapter_spec.js +++ b/test/spec/modules/admanBidAdapter_spec.js @@ -130,14 +130,13 @@ describe('AdmanAdapter', function () { let placements = data['placements']; for (let i = 0; i < placements.length; i++) { let placement = placements[i]; - expect(placement).to.have.all.keys('placementId', 'eids', 'bidId', 'traffic', 'sizes', 'schain', 'bidFloor', + expect(placement).to.have.all.keys('placementId', 'eids', 'bidId', 'traffic', 'schain', 'bidFloor', 'playerSize', 'minduration', 'maxduration', 'mimes', 'protocols', 'startdelay', 'placement', 'skip', 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackmethod', 'api', 'linearity'); expect(placement.schain).to.be.an('object') expect(placement.placementId).to.be.a('number'); expect(placement.bidId).to.be.a('string'); expect(placement.traffic).to.be.a('string'); - expect(placement.sizes).to.be.an('array'); expect(placement.bidFloor).to.be.an('number'); } });