From 36d0f724a1b45ab321d57c7eb5741b591e6d327e Mon Sep 17 00:00:00 2001 From: hamper Date: Wed, 26 May 2021 23:00:08 +0300 Subject: [PATCH 01/63] Vuukle bid adapter: support for meta advertiserDomains (#6839) * add vuukle adapter * add readme * doc: add email * Vuukle bid adapter: support for meta advertiserDomains --- modules/vuukleBidAdapter.js | 6 +++++- test/spec/modules/vuukleBidAdapter_spec.js | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/vuukleBidAdapter.js b/modules/vuukleBidAdapter.js index e9770b5e62e..7a83bf4e331 100644 --- a/modules/vuukleBidAdapter.js +++ b/modules/vuukleBidAdapter.js @@ -55,8 +55,12 @@ export const spec = { currency: res.currency || 'USD', netRevenue: true, ttl: TIME_TO_LIVE, - ad: res.ad + ad: res.ad, + meta: { + advertiserDomains: Array.isArray(res.adomain) ? res.adomain : [] + } }; + return [bidResponse]; }, } diff --git a/test/spec/modules/vuukleBidAdapter_spec.js b/test/spec/modules/vuukleBidAdapter_spec.js index fdca4085c8e..17353a40b85 100644 --- a/test/spec/modules/vuukleBidAdapter_spec.js +++ b/test/spec/modules/vuukleBidAdapter_spec.js @@ -41,7 +41,8 @@ describe('vuukleBidAdapterTests', function() { 'width': 300, 'height': 250, 'creative_id': '12345', - 'ad': 'test ad' + 'ad': 'test ad', + 'adomain': ['example.com'] } }; @@ -55,5 +56,6 @@ describe('vuukleBidAdapterTests', function() { expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); expect(bid.creativeId).to.equal('12345'); + expect(bid.meta.advertiserDomains).to.deep.equal(['example.com']); }); }); From 8cf4ad9227257b37cd95b8ea2d598198e1ecb679 Mon Sep 17 00:00:00 2001 From: John Salis Date: Wed, 26 May 2021 18:44:39 -0400 Subject: [PATCH 02/63] add support for advertiser domains and other bid meta (#6842) Co-authored-by: John Salis --- modules/beachfrontBidAdapter.js | 6 +- .../spec/modules/beachfrontBidAdapter_spec.js | 73 ++++++++++++++----- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index da5f385b0da..43df7eef9ae 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -71,13 +71,15 @@ export const spec = { let firstSize = getFirstSize(sizes); let context = utils.deepAccess(bidRequest, 'mediaTypes.video.context'); let responseType = getVideoBidParam(bidRequest, 'responseType') || 'both'; + let responseMeta = Object.assign({ mediaType: VIDEO, advertiserDomains: [] }, response.meta); let bidResponse = { requestId: bidRequest.bidId, bidderCode: spec.code, cpm: response.bidPrice, width: firstSize.w, height: firstSize.h, - creativeId: response.crid || response.cmpId, + creativeId: response.crid, + meta: responseMeta, renderer: context === OUTSTREAM ? createRenderer(bidRequest) : null, mediaType: VIDEO, currency: CURRENCY, @@ -103,6 +105,7 @@ export const spec = { .filter(bid => bid.adm) .map((bid) => { let request = find(bidRequest, req => req.adUnitCode === bid.slot); + let responseMeta = Object.assign({ mediaType: BANNER, advertiserDomains: [] }, bid.meta); return { requestId: request.bidId, bidderCode: spec.code, @@ -111,6 +114,7 @@ export const spec = { cpm: bid.price, width: bid.w, height: bid.h, + meta: responseMeta, mediaType: BANNER, currency: CURRENCY, netRevenue: true, diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index fc74ec8a2aa..555f2c21262 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -673,6 +673,7 @@ describe('BeachfrontAdapter', function () { width: width, height: height, renderer: null, + meta: { mediaType: 'video', advertiserDomains: [] }, mediaType: 'video', currency: 'USD', netRevenue: true, @@ -680,26 +681,6 @@ describe('BeachfrontAdapter', function () { }); }); - it('should default to the legacy "cmpId" value for the creative ID', () => { - const width = 640; - const height = 480; - const bidRequest = bidRequests[0]; - bidRequest.mediaTypes = { - video: { - playerSize: [ width, height ] - } - }; - const serverResponse = { - bidPrice: 5.00, - url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', - cmpId: '123abc' - }; - const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); - expect(bidResponse).to.deep.contain({ - creativeId: serverResponse.cmpId - }); - }); - it('should return only vast url if the response type is "nurl"', () => { const width = 640; const height = 480; @@ -761,6 +742,31 @@ describe('BeachfrontAdapter', function () { }); expect(bidResponse.renderer.render).to.be.a('function'); }); + + it('should return meta data for the bid response', () => { + const width = 640; + const height = 480; + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { + video: { + playerSize: [ width, height ] + } + }; + const serverResponse = { + bidPrice: 5.00, + url: 'http://reachms.bfmio.com/getmu?aid=bid:19c4a196-fb21-4c81-9a1a-ecc5437a39da', + meta: { + advertiserDomains: ['example.com'], + advertiserId: '123' + } + }; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest }); + expect(bidResponse.meta).to.deep.equal({ + mediaType: 'video', + advertiserDomains: ['example.com'], + advertiserId: '123' + }); + }); }); describe('for banner bids', function () { @@ -815,6 +821,7 @@ describe('BeachfrontAdapter', function () { cpm: serverResponse[ i ].price, width: serverResponse[ i ].w, height: serverResponse[ i ].h, + meta: { mediaType: 'banner', advertiserDomains: [] }, mediaType: 'banner', currency: 'USD', netRevenue: true, @@ -822,6 +829,32 @@ describe('BeachfrontAdapter', function () { }); } }); + + it('should return meta data for the bid response', () => { + bidRequests[0].mediaTypes = { + banner: { + sizes: [[ 300, 250 ], [ 728, 90 ]] + } + }; + const serverResponse = [{ + slot: bidRequests[0].adUnitCode, + adm: '
', + crid: 'crid_1', + price: 3.02, + w: 728, + h: 90, + meta: { + advertiserDomains: ['example.com'], + advertiserId: '123' + } + }]; + const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest: bidRequests }); + expect(bidResponse[0].meta).to.deep.equal({ + mediaType: 'banner', + advertiserDomains: ['example.com'], + advertiserId: '123' + }); + }); }); }); From b72e584a024446f1502a74b8c788006ac110f333 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Wed, 26 May 2021 16:26:24 -0700 Subject: [PATCH 03/63] Prebid Core: Check for stale rendering (#6707) * Check for stale rendering * Check for stale rendering - code review changes --- src/auctionManager.js | 6 + src/config.js | 7 +- src/constants.json | 3 +- src/prebid.js | 118 +++++---- src/secureCreatives.js | 14 +- test/spec/config_spec.js | 19 +- test/spec/unit/pbjs_api_spec.js | 121 +++++++++ test/spec/unit/secureCreatives_spec.js | 331 ++++++++++++++++++++++++- 8 files changed, 558 insertions(+), 61 deletions(-) diff --git a/src/auctionManager.js b/src/auctionManager.js index 1aca1277aa8..bbafd7426d5 100644 --- a/src/auctionManager.js +++ b/src/auctionManager.js @@ -15,6 +15,8 @@ * @property {function(): Object} createAuction - creates auction instance and stores it for future reference * @property {function(): Object} findBidByAdId - find bid received by adId. This function will be called by $$PREBID_GLOBAL$$.renderAd * @property {function(): Object} getStandardBidderAdServerTargeting - returns standard bidder targeting for all the adapters. Refer http://prebid.org/dev-docs/publisher-api-reference.html#module_pbjs.bidderSettings for more details + * @property {function(Object): void} addWinningBid - add a winning bid to an auction based on auctionId + * @property {function(): void} clearAllAuctions - clear all auctions for testing */ import { uniques, flatten, logWarn } from './utils.js'; @@ -113,6 +115,10 @@ export function newAuctionManager() { return _auctions.length && _auctions[_auctions.length - 1].getAuctionId() }; + auctionManager.clearAllAuctions = function() { + _auctions.length = 0; + } + function _addAuction(auction) { _auctions.push(auction); } diff --git a/src/config.js b/src/config.js index 7cb1a81955d..00cf1efd854 100644 --- a/src/config.js +++ b/src/config.js @@ -265,7 +265,7 @@ export function newConfig() { } for (let k of Object.keys(val)) { - if (k !== 'secondaryBidders') { + if (k !== 'secondaryBidders' && k !== 'suppressStaleRender') { utils.logWarn(`Auction Options given an incorrect param: ${k}`) return false } @@ -277,6 +277,11 @@ export function newConfig() { utils.logWarn(`Auction Options ${k} must be only string`); return false } + } else if (k === 'suppressStaleRender') { + if (!utils.isBoolean(val[k])) { + utils.logWarn(`Auction Options ${k} must be of type boolean`); + return false; + } } } return true; diff --git a/src/constants.json b/src/constants.json index e6b9687f911..c43e88cf75f 100644 --- a/src/constants.json +++ b/src/constants.json @@ -39,7 +39,8 @@ "AD_RENDER_FAILED": "adRenderFailed", "TCF2_ENFORCEMENT": "tcf2Enforcement", "AUCTION_DEBUG": "auctionDebug", - "BID_VIEWABLE": "bidViewable" + "BID_VIEWABLE": "bidViewable", + "STALE_RENDER": "staleRender" }, "AD_RENDER_FAILED_REASON" : { "PREVENT_WRITING_ON_MAIN_DOCUMENT": "preventWritingOnMainDocument", diff --git a/src/prebid.js b/src/prebid.js index 2211e8ec2b5..f58c97ec581 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -23,7 +23,7 @@ const events = require('./events.js'); const { triggerUserSyncs } = userSync; /* private variables */ -const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED } = CONSTANTS.EVENTS; +const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED, STALE_RENDER } = CONSTANTS.EVENTS; const { PREVENT_WRITING_ON_MAIN_DOCUMENT, NO_AD, EXCEPTION, CANNOT_FIND_AD, MISSING_DOC_OR_ADID } = CONSTANTS.AD_RENDER_FAILED_REASON; const eventValidators = { @@ -390,63 +390,75 @@ $$PREBID_GLOBAL$$.renderAd = function (doc, id, options) { try { // lookup ad by ad Id const bid = auctionManager.findBidByAdId(id); + if (bid) { - // replace macros according to openRTB with price paid = bid.cpm - bid.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); - bid.adUrl = utils.replaceAuctionPrice(bid.adUrl, bid.cpm); - - // replacing clickthrough if submitted - if (options && options.clickThrough) { - const { clickThrough } = options; - bid.ad = utils.replaceClickThrough(bid.ad, clickThrough); - bid.adUrl = utils.replaceClickThrough(bid.adUrl, clickThrough); + let shouldRender = true; + if (bid && bid.status === CONSTANTS.BID_STATUS.RENDERED) { + utils.logWarn(`Ad id ${bid.adId} has been rendered before`); + events.emit(STALE_RENDER, bid); + if (utils.deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { + shouldRender = false; + } } - // save winning bids - auctionManager.addWinningBid(bid); - - // emit 'bid won' event here - events.emit(BID_WON, bid); - - const { height, width, ad, mediaType, adUrl, renderer } = bid; - - const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); - utils.insertElement(creativeComment, doc, 'body'); - - if (isRendererRequired(renderer)) { - executeRenderer(renderer, bid); - } else if ((doc === document && !utils.inIframe()) || mediaType === 'video') { - const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; - emitAdRenderFail({ reason: PREVENT_WRITING_ON_MAIN_DOCUMENT, message, bid, id }); - } else if (ad) { - // will check if browser is firefox and below version 67, if so execute special doc.open() - // for details see: https://github.com/prebid/Prebid.js/pull/3524 - // TODO remove this browser specific code at later date (when Firefox < 67 usage is mostly gone) - if (navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox/') > -1) { - const firefoxVerRegx = /firefox\/([\d\.]+)/; - let firefoxVer = navigator.userAgent.toLowerCase().match(firefoxVerRegx)[1]; // grabs the text in the 1st matching group - if (firefoxVer && parseInt(firefoxVer, 10) < 67) { - doc.open('text/html', 'replace'); + if (shouldRender) { + // replace macros according to openRTB with price paid = bid.cpm + bid.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); + bid.adUrl = utils.replaceAuctionPrice(bid.adUrl, bid.cpm); + + // replacing clickthrough if submitted + if (options && options.clickThrough) { + const {clickThrough} = options; + bid.ad = utils.replaceClickThrough(bid.ad, clickThrough); + bid.adUrl = utils.replaceClickThrough(bid.adUrl, clickThrough); + } + + // save winning bids + auctionManager.addWinningBid(bid); + + // emit 'bid won' event here + events.emit(BID_WON, bid); + + const {height, width, ad, mediaType, adUrl, renderer} = bid; + + const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`); + utils.insertElement(creativeComment, doc, 'body'); + + if (isRendererRequired(renderer)) { + executeRenderer(renderer, bid); + } else if ((doc === document && !utils.inIframe()) || mediaType === 'video') { + const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`; + emitAdRenderFail({reason: PREVENT_WRITING_ON_MAIN_DOCUMENT, message, bid, id}); + } else if (ad) { + // will check if browser is firefox and below version 67, if so execute special doc.open() + // for details see: https://github.com/prebid/Prebid.js/pull/3524 + // TODO remove this browser specific code at later date (when Firefox < 67 usage is mostly gone) + if (navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('firefox/') > -1) { + const firefoxVerRegx = /firefox\/([\d\.]+)/; + let firefoxVer = navigator.userAgent.toLowerCase().match(firefoxVerRegx)[1]; // grabs the text in the 1st matching group + if (firefoxVer && parseInt(firefoxVer, 10) < 67) { + doc.open('text/html', 'replace'); + } } + doc.write(ad); + doc.close(); + setRenderSize(doc, width, height); + utils.callBurl(bid); + } else if (adUrl) { + const iframe = utils.createInvisibleIframe(); + iframe.height = height; + iframe.width = width; + iframe.style.display = 'inline'; + iframe.style.overflow = 'hidden'; + iframe.src = adUrl; + + utils.insertElement(iframe, doc, 'body'); + setRenderSize(doc, width, height); + utils.callBurl(bid); + } else { + const message = `Error trying to write ad. No ad for bid response id: ${id}`; + emitAdRenderFail({reason: NO_AD, message, bid, id}); } - doc.write(ad); - doc.close(); - setRenderSize(doc, width, height); - utils.callBurl(bid); - } else if (adUrl) { - const iframe = utils.createInvisibleIframe(); - iframe.height = height; - iframe.width = width; - iframe.style.display = 'inline'; - iframe.style.overflow = 'hidden'; - iframe.src = adUrl; - - utils.insertElement(iframe, doc, 'body'); - setRenderSize(doc, width, height); - utils.callBurl(bid); - } else { - const message = `Error trying to write ad. No ad for bid response id: ${id}`; - emitAdRenderFail({ reason: NO_AD, message, bid, id }); } } else { const message = `Error trying to write ad. Cannot find ad by given id : ${id}`; diff --git a/src/secureCreatives.js b/src/secureCreatives.js index def1a9abdbb..a172ec62630 100644 --- a/src/secureCreatives.js +++ b/src/secureCreatives.js @@ -6,19 +6,21 @@ import events from './events.js'; import { fireNativeTrackers, getAssetMessage, getAllAssetsMessage } from './native.js'; import constants from './constants.json'; -import { logWarn, replaceAuctionPrice } from './utils.js'; +import { logWarn, replaceAuctionPrice, deepAccess } from './utils.js'; import { auctionManager } from './auctionManager.js'; import find from 'core-js-pure/features/array/find.js'; import { isRendererRequired, executeRenderer } from './Renderer.js'; import includes from 'core-js-pure/features/array/includes.js'; +import { config } from './config.js'; const BID_WON = constants.EVENTS.BID_WON; +const STALE_RENDER = constants.EVENTS.STALE_RENDER; export function listenMessagesFromCreative() { window.addEventListener('message', receiveMessage, false); } -function receiveMessage(ev) { +export function receiveMessage(ev) { var key = ev.message ? 'message' : 'data'; var data = {}; try { @@ -33,6 +35,14 @@ function receiveMessage(ev) { }); if (adObject && data.message === 'Prebid Request') { + if (adObject.status === constants.BID_STATUS.RENDERED) { + logWarn(`Ad id ${adObject.adId} has been rendered before`); + events.emit(STALE_RENDER, adObject); + if (deepAccess(config.getConfig('auctionOptions'), 'suppressStaleRender')) { + return; + } + } + _sendAdToCreative(adObject, ev); // save winning bids diff --git a/test/spec/config_spec.js b/test/spec/config_spec.js index 8161d9f1827..9492db6e849 100644 --- a/test/spec/config_spec.js +++ b/test/spec/config_spec.js @@ -233,7 +233,7 @@ describe('config API', function () { expect(logWarnSpy.called).to.equal(false); }); - it('sets auctionOptions', function () { + it('sets auctionOptions secondaryBidders', function () { const auctionOptionsConfig = { 'secondaryBidders': ['rubicon', 'appnexus'] } @@ -241,6 +241,14 @@ describe('config API', function () { expect(getConfig('auctionOptions')).to.eql(auctionOptionsConfig); }); + it('sets auctionOptions suppressStaleRender', function () { + const auctionOptionsConfig = { + 'suppressStaleRender': true + } + setConfig({ auctionOptions: auctionOptionsConfig }); + expect(getConfig('auctionOptions')).to.eql(auctionOptionsConfig); + }); + it('should log warning for the wrong value passed to auctionOptions', function () { setConfig({ auctionOptions: '' }); expect(logWarnSpy.calledOnce).to.equal(true); @@ -257,6 +265,15 @@ describe('config API', function () { assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); }); + it('should log warning for invalid auctionOptions suppress stale render', function () { + setConfig({ auctionOptions: { + 'suppressStaleRender': 'test', + }}); + expect(logWarnSpy.calledOnce).to.equal(true); + const warning = 'Auction Options suppressStaleRender must be of type boolean'; + assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged'); + }); + it('should log warning for invalid properties to auctionOptions', function () { setConfig({ auctionOptions: { 'testing': true diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js index 805eae9e3bc..0bd3380f737 100644 --- a/test/spec/unit/pbjs_api_spec.js +++ b/test/spec/unit/pbjs_api_spec.js @@ -199,6 +199,10 @@ describe('Unit: Prebid Module', function () { configObj.setConfig({ useBidCache: false }); }); + after(function() { + auctionManager.clearAllAuctions(); + }); + describe('getAdserverTargetingForAdUnitCodeStr', function () { beforeEach(function () { resetAuction(); @@ -1062,6 +1066,8 @@ describe('Unit: Prebid Module', function () { var adResponse = {}; var spyLogError = null; var spyLogMessage = null; + var spyLogWarn = null; + var spyAddWinningBid; var inIframe = true; var triggerPixelStub; @@ -1100,6 +1106,8 @@ describe('Unit: Prebid Module', function () { spyLogError = sinon.spy(utils, 'logError'); spyLogMessage = sinon.spy(utils, 'logMessage'); + spyLogWarn = sinon.spy(utils, 'logWarn'); + spyAddWinningBid = sinon.spy(auctionManager, 'addWinningBid'); inIframe = true; sinon.stub(utils, 'inIframe').callsFake(() => inIframe); @@ -1110,8 +1118,10 @@ describe('Unit: Prebid Module', function () { auction.getBidsReceived = getBidResponses; utils.logError.restore(); utils.logMessage.restore(); + utils.logWarn.restore(); utils.inIframe.restore(); triggerPixelStub.restore(); + spyAddWinningBid.restore(); }); it('should require doc and id params', function () { @@ -1218,6 +1228,117 @@ describe('Unit: Prebid Module', function () { sinon.assert.calledOnce(triggerPixelStub); sinon.assert.calledWith(triggerPixelStub, burl); }); + + it('should call addWinningBid', function () { + pushBidResponseToAuction({ + ad: "" + }); + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + var message = 'Calling renderAd with adId :' + bidId; + sinon.assert.calledWith(spyLogMessage, message); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + }); + + it('should warn stale rendering', function () { + var message = 'Calling renderAd with adId :' + bidId; + var warning = `Ad id ${bidId} has been rendered before`; + var onWonEvent = sinon.stub(); + var onStaleEvent = sinon.stub(); + + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + + pushBidResponseToAuction({ + ad: "" + }); + + // First render should pass with no warning and added to winning bids + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.neverCalledWith(spyLogWarn, warning); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + + sinon.assert.calledWith(onWonEvent, adResponse); + sinon.assert.notCalled(onStaleEvent); + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + // Reset call history for spies and stubs + spyLogMessage.resetHistory(); + spyLogWarn.resetHistory(); + spyAddWinningBid.resetHistory(); + onWonEvent.resetHistory(); + onStaleEvent.resetHistory(); + + // Second render should have a warning but still added to winning bids + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.calledWith(spyLogWarn, warning); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + + sinon.assert.calledWith(onWonEvent, adResponse); + sinon.assert.calledWith(onStaleEvent, adResponse); + + // Clean up + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + }); + + it('should stop stale rendering', function () { + var message = 'Calling renderAd with adId :' + bidId; + var warning = `Ad id ${bidId} has been rendered before`; + var onWonEvent = sinon.stub(); + var onStaleEvent = sinon.stub(); + + // Setting suppressStaleRender to true explicitly + configObj.setConfig({'auctionOptions': {'suppressStaleRender': true}}); + + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.onEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + + pushBidResponseToAuction({ + ad: "" + }); + + // First render should pass with no warning and added to winning bids + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.neverCalledWith(spyLogWarn, warning); + + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + sinon.assert.calledWith(onWonEvent, adResponse); + sinon.assert.notCalled(onStaleEvent); + + // Reset call history for spies and stubs + spyLogMessage.resetHistory(); + spyLogWarn.resetHistory(); + spyAddWinningBid.resetHistory(); + onWonEvent.resetHistory(); + onStaleEvent.resetHistory(); + + // Second render should have a warning and do not proceed further + $$PREBID_GLOBAL$$.renderAd(doc, bidId); + sinon.assert.calledWith(spyLogMessage, message); + sinon.assert.calledWith(spyLogWarn, warning); + + sinon.assert.notCalled(spyAddWinningBid); + + sinon.assert.notCalled(onWonEvent); + sinon.assert.calledWith(onStaleEvent, adResponse); + + // Clean up + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.BID_WON, onWonEvent); + $$PREBID_GLOBAL$$.offEvent(CONSTANTS.EVENTS.STALE_RENDER, onStaleEvent); + configObj.setConfig({'auctionOptions': {}}); + }); }); describe('requestBids', function () { diff --git a/test/spec/unit/secureCreatives_spec.js b/test/spec/unit/secureCreatives_spec.js index 566154f0003..eca00e8c8fa 100644 --- a/test/spec/unit/secureCreatives_spec.js +++ b/test/spec/unit/secureCreatives_spec.js @@ -1,8 +1,18 @@ import { - _sendAdToCreative -} from '../../../src/secureCreatives.js'; -import { expect } from 'chai'; + _sendAdToCreative, receiveMessage +} from 'src/secureCreatives.js'; import * as utils from 'src/utils.js'; +import {getAdUnits, getBidRequests, getBidResponses} from 'test/fixtures/fixtures.js'; +import {auctionManager} from 'src/auctionManager.js'; +import * as auctionModule from 'src/auction.js'; +import * as native from 'src/native.js'; +import {fireNativeTrackers, getAllAssetsMessage} from 'src/native.js'; +import events from 'src/events.js'; +import { config as configObj } from 'src/config.js'; + +import { expect } from 'chai'; + +var CONSTANTS = require('src/constants.json'); describe('secureCreatives', () => { describe('_sendAdToCreative', () => { @@ -42,4 +52,319 @@ describe('secureCreatives', () => { window.apntag = oldapntag; }); }); + + describe('receiveMessage', function() { + const bidId = 1; + const warning = `Ad id ${bidId} has been rendered before`; + let auction; + let adResponse = {}; + let spyAddWinningBid; + let spyLogWarn; + let stubFireNativeTrackers; + let stubGetAllAssetsMessage; + let stubEmit; + + function pushBidResponseToAuction(obj) { + adResponse = Object.assign({ + auctionId: 1, + adId: bidId, + width: 300, + height: 250, + renderer: null + }, obj); + auction.getBidsReceived = function() { + let bidsReceived = getBidResponses(); + bidsReceived.push(adResponse); + return bidsReceived; + } + auction.getAuctionId = () => 1; + } + + function resetAuction() { + $$PREBID_GLOBAL$$.setConfig({ enableSendAllBids: false }); + auction.getBidRequests = getBidRequests; + auction.getBidsReceived = getBidResponses; + auction.getAdUnits = getAdUnits; + auction.getAuctionStatus = function() { return auctionModule.AUCTION_COMPLETED } + } + + function resetHistories(...others) { + [ + spyAddWinningBid, + spyLogWarn, + stubFireNativeTrackers, + stubGetAllAssetsMessage, + stubEmit + ].forEach(s => s.resetHistory()); + + if (others && others.length > 0) { others.forEach(s => s.resetHistory()); } + } + + before(function() { + const adUnits = getAdUnits(); + const adUnitCodes = getAdUnits().map(unit => unit.code); + const bidsBackHandler = function() {}; + const timeout = 2000; + auction = auctionManager.createAuction({adUnits, adUnitCodes, callback: bidsBackHandler, cbTimeout: timeout}); + resetAuction(); + }); + + after(function() { + auctionManager.clearAllAuctions(); + }); + + beforeEach(function() { + spyAddWinningBid = sinon.spy(auctionManager, 'addWinningBid'); + spyLogWarn = sinon.spy(utils, 'logWarn'); + stubFireNativeTrackers = sinon.stub(native, 'fireNativeTrackers'); + stubGetAllAssetsMessage = sinon.stub(native, 'getAllAssetsMessage'); + stubEmit = sinon.stub(events, 'emit'); + }); + + afterEach(function() { + spyAddWinningBid.restore(); + spyLogWarn.restore(); + stubFireNativeTrackers.restore(); + stubGetAllAssetsMessage.restore(); + stubEmit.restore(); + resetAuction(); + }); + + describe('Prebid Request', function() { + it('should render', function () { + pushBidResponseToAuction({ + renderer: {render: sinon.stub(), url: 'some url'} + }); + + const data = { + adId: bidId, + message: 'Prebid Request' + }; + + const ev = { + data: JSON.stringify(data) + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + }); + + it('should allow stale rendering without config', function () { + pushBidResponseToAuction({ + renderer: {render: sinon.stub(), url: 'some url'} + }); + + const data = { + adId: bidId, + message: 'Prebid Request' + }; + + const ev = { + data: JSON.stringify(data) + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + resetHistories(adResponse.renderer.render); + + receiveMessage(ev); + + sinon.assert.calledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER, adResponse); + }); + + it('should stop stale rendering with config', function () { + configObj.setConfig({'auctionOptions': {'suppressStaleRender': true}}); + + pushBidResponseToAuction({ + renderer: {render: sinon.stub(), url: 'some url'} + }); + + const data = { + adId: bidId, + message: 'Prebid Request' + }; + + const ev = { + data: JSON.stringify(data) + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(spyAddWinningBid, adResponse); + sinon.assert.calledOnce(adResponse.renderer.render); + sinon.assert.calledWith(adResponse.renderer.render, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + resetHistories(adResponse.renderer.render); + + receiveMessage(ev); + + sinon.assert.calledWith(spyLogWarn, warning); + sinon.assert.notCalled(spyAddWinningBid); + sinon.assert.notCalled(adResponse.renderer.render); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER, adResponse); + + configObj.setConfig({'auctionOptions': {}}); + }); + }); + + describe('Prebid Native', function() { + it('Prebid native should render', function () { + pushBidResponseToAuction({}); + + const data = { + adId: bidId, + message: 'Prebid Native', + action: 'allAssetRequest' + }; + + const ev = { + data: JSON.stringify(data), + source: { + postMessage: sinon.stub() + }, + origin: 'any origin' + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubGetAllAssetsMessage); + sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); + sinon.assert.calledOnce(ev.source.postMessage); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + }); + + it('Prebid native should allow stale rendering without config', function () { + pushBidResponseToAuction({}); + + const data = { + adId: bidId, + message: 'Prebid Native', + action: 'allAssetRequest' + }; + + const ev = { + data: JSON.stringify(data), + source: { + postMessage: sinon.stub() + }, + origin: 'any origin' + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubGetAllAssetsMessage); + sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); + sinon.assert.calledOnce(ev.source.postMessage); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + resetHistories(ev.source.postMessage); + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubGetAllAssetsMessage); + sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); + sinon.assert.calledOnce(ev.source.postMessage); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + }); + + it('Prebid native should allow stale rendering with config', function () { + configObj.setConfig({'auctionOptions': {'suppressStaleRender': true}}); + + pushBidResponseToAuction({}); + + const data = { + adId: bidId, + message: 'Prebid Native', + action: 'allAssetRequest' + }; + + const ev = { + data: JSON.stringify(data), + source: { + postMessage: sinon.stub() + }, + origin: 'any origin' + }; + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubGetAllAssetsMessage); + sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); + sinon.assert.calledOnce(ev.source.postMessage); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + expect(adResponse).to.have.property('status', CONSTANTS.BID_STATUS.RENDERED); + + resetHistories(ev.source.postMessage); + + receiveMessage(ev); + + sinon.assert.neverCalledWith(spyLogWarn, warning); + sinon.assert.calledOnce(stubGetAllAssetsMessage); + sinon.assert.calledWith(stubGetAllAssetsMessage, data, adResponse); + sinon.assert.calledOnce(ev.source.postMessage); + sinon.assert.calledOnce(stubFireNativeTrackers); + sinon.assert.calledWith(stubFireNativeTrackers, data, adResponse); + sinon.assert.calledOnce(spyAddWinningBid); + sinon.assert.calledWith(stubEmit, CONSTANTS.EVENTS.BID_WON, adResponse); + sinon.assert.neverCalledWith(stubEmit, CONSTANTS.EVENTS.STALE_RENDER); + + configObj.setConfig({'auctionOptions': {}}); + }); + }); + }); }); From 9013e5564c5d773adc4f6ad2a51220c9b29307e3 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Thu, 27 May 2021 11:30:38 +0200 Subject: [PATCH 04/63] tappx Bid Adapter: fix wrong regex and gdpr bug (#6834) * tappxBidAdapter: hotfix gdpr url param * tappxBidAdapter: update version * tappxBidAdapter: update regex errors --- modules/tappxBidAdapter.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 566795a204b..48e0abc5c7f 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -8,7 +8,7 @@ import { config } from '../src/config.js'; const BIDDER_CODE = 'tappx'; const TTL = 360; const CUR = 'USD'; -const TAPPX_BIDDER_VERSION = '0.1.10514'; +const TAPPX_BIDDER_VERSION = '0.1.10526'; const TYPE_CNN = 'prebidjs'; const VIDEO_SUPPORT = ['instream']; @@ -79,7 +79,7 @@ export const spec = { // GDPR & CCPA if (gdprConsent) { - url += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + url += '&gdpr_optin=' + (gdprConsent.gdprApplies ? 1 : 0); url += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); } if (uspConsent) { @@ -120,7 +120,7 @@ function validBasic(bid) { } let classicEndpoint = true - if ((new RegExp(`^(vz.*|zz.*)\.*$`, 'i')).test(bid.params.host)) { + if ((new RegExp(`^(vz.*|zz.*)\\.*$`, 'i')).test(bid.params.host)) { classicEndpoint = false } @@ -398,8 +398,8 @@ function getHostInfo(validBidRequests) { domainInfo.domain = hostParam.split('/', 1)[0]; - let regexNewEndpoints = new RegExp(`^(vz.*|zz.*)\.pub\.tappx\.com$`, 'i'); - let regexClassicEndpoints = new RegExp(`^([a-z]{3}|testing)\.[a-z]{3}\.tappx\.com$`, 'i'); + let regexNewEndpoints = new RegExp(`^(vz.*|zz.*)\\.[a-z]{3}\\.tappx\\.com$`, 'i'); + let regexClassicEndpoints = new RegExp(`^([a-z]{3}|testing)\\.[a-z]{3}\\.tappx\\.com$`, 'i'); if (regexNewEndpoints.test(domainInfo.domain)) { domainInfo.newEndpoint = true; From 3cbdf86cb1d729c71f70c13221bfb22dc2c78ac0 Mon Sep 17 00:00:00 2001 From: guiann Date: Thu, 27 May 2021 12:21:34 +0200 Subject: [PATCH 05/63] Adyoulike bid adapter - improvements on Native case (#6831) * add required clickurl in every native adrequest * allows the native response to be given as is to prebid if possible * add unit tests on new Native case --- modules/adyoulikeBidAdapter.js | 101 ++++++++-------- test/spec/modules/adyoulikeBidAdapter_spec.js | 113 ++++++++++++------ 2 files changed, 130 insertions(+), 84 deletions(-) diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 00c7c02dc72..44d8e2e3016 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -78,6 +78,10 @@ export const spec = { if (nativeReq.type === 'image') { nativeReq = Object.assign({}, NATIVE_IMAGE, nativeReq); } + // click url is always mandatory even if not specified by publisher + nativeReq.clickUrl = { + required: true + }; accumulator[bidReq.bidId].Native = nativeReq; } if (mediatype === VIDEO) { @@ -329,6 +333,9 @@ function getVideoAd(response) { } function getNativeAssets(response, nativeConfig) { + if (typeof response.Native === 'object') { + return response.Native; + } const native = {}; var adJson = {}; @@ -362,59 +369,55 @@ function getNativeAssets(response, nativeConfig) { } Object.keys(nativeConfig).map(function(key, index) { - if (typeof response.Native === 'object') { - native[key] = response.Native[key]; - } else { - switch (key) { - case 'title': - native[key] = textsJson.TITLE; - break; - case 'body': - native[key] = textsJson.DESCRIPTION; - break; - case 'cta': - native[key] = textsJson.CALLTOACTION; - break; - case 'sponsoredBy': - native[key] = adJson.Content.Preview.Sponsor.Name; - break; - case 'image': - // main image requested size - const imgSize = nativeConfig.image.sizes || []; - if (!imgSize.length) { - imgSize[0] = response.Width || 300; - imgSize[1] = response.Height || 250; + switch (key) { + case 'title': + native[key] = textsJson.TITLE; + break; + case 'body': + native[key] = textsJson.DESCRIPTION; + break; + case 'cta': + native[key] = textsJson.CALLTOACTION; + break; + case 'sponsoredBy': + native[key] = adJson.Content.Preview.Sponsor.Name; + break; + case 'image': + // main image requested size + const imgSize = nativeConfig.image.sizes || []; + if (!imgSize.length) { + imgSize[0] = response.Width || 300; + imgSize[1] = response.Height || 250; + } + + native[key] = { + url: getImageUrl(adJson, adJson.Content.Preview.Thumbnail.Image, imgSize[0], imgSize[1]), + width: imgSize[0], + height: imgSize[1] + }; + break; + case 'icon': + if (adJson.HasSponsorImage) { + // icon requested size + const iconSize = nativeConfig.icon.sizes || []; + if (!iconSize.length) { + iconSize[0] = 50; + iconSize[1] = 50; } native[key] = { - url: getImageUrl(adJson, adJson.Content.Preview.Thumbnail.Image, imgSize[0], imgSize[1]), - width: imgSize[0], - height: imgSize[1] + url: getImageUrl(adJson, adJson.Content.Preview.Sponsor.Logo.Resource, iconSize[0], iconSize[1]), + width: iconSize[0], + height: iconSize[1] }; - break; - case 'icon': - if (adJson.HasSponsorImage) { - // icon requested size - const iconSize = nativeConfig.icon.sizes || []; - if (!iconSize.length) { - iconSize[0] = 50; - iconSize[1] = 50; - } - - native[key] = { - url: getImageUrl(adJson, adJson.Content.Preview.Sponsor.Logo.Resource, iconSize[0], iconSize[1]), - width: iconSize[0], - height: iconSize[1] - }; - } - break; - case 'privacyIcon': - native[key] = getImageUrl(adJson, adJson.Content.Preview.Credit.Logo.Resource, 25, 25); - break; - case 'privacyLink': - native[key] = adJson.Content.Preview.Credit.Url; - break; - } + } + break; + case 'privacyIcon': + native[key] = getImageUrl(adJson, adJson.Content.Preview.Credit.Logo.Resource, 25, 25); + break; + case 'privacyLink': + native[key] = adJson.Content.Preview.Credit.Url; + break; } }); diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index c432ad1b32d..abf8793865c 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -300,6 +300,74 @@ describe('Adyoulike Adapter', function () { 'Height': 600, } ]; + + const responseWithSingleNative = [{ + 'BidID': 'bid_id_0', + 'Placement': 'placement_0', + 'Native': { + 'body': 'Considérant l\'extrémité conjoncturelle, il serait bon d\'anticiper toutes les voies de bon sens.', + 'cta': 'Click here to learn more', + 'clickUrl': 'https://tracking.omnitagjs.com/tracking/ar?event_kind=CLICK&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991&url=https%3A%2F%2Fwww.w3.org%2FPeople%2Fmimasa%2Ftest%2Fxhtml%2Fentities%2Fentities-11.xhtml%23lat1', + 'image': { + 'height': 600, + 'url': 'https://blobs.omnitagjs.com/blobs/f1/f1c80d4bb5643c22/fd4362d35bb174d6f1c80d4bb5643c22', + 'width': 300 + }, + 'privacyIcon': 'https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png', + 'privacyLink': 'https://blobs.omnitagjs.com/adchoice/', + 'sponsoredBy': 'QA Team', + 'title': 'Adserver Traffic Redirect Internal', + 'impressionTrackers': [ + 'https://testPixelIMP.com/fake', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=INSERTION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', + ], + 'javascriptTrackers': [ + 'https://testJsIMP.com/fake.js' + ], + 'clickTrackers': [ + 'https://testPixelCLICK.com/fake' + ] + }, + 'Price': 0.5, + 'Height': 600, + }]; + + const nativeResult = [{ + cpm: 0.5, + creativeId: undefined, + currency: 'USD', + netRevenue: true, + requestId: 'bid_id_0', + ttl: 3600, + mediaType: 'native', + native: { + body: 'Considérant l\'extrémité conjoncturelle, il serait bon d\'anticiper toutes les voies de bon sens.', + clickTrackers: [ + 'https://testPixelCLICK.com/fake' + ], + clickUrl: 'https://tracking.omnitagjs.com/tracking/ar?event_kind=CLICK&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991&url=https%3A%2F%2Fwww.w3.org%2FPeople%2Fmimasa%2Ftest%2Fxhtml%2Fentities%2Fentities-11.xhtml%23lat1', + cta: 'Click here to learn more', + image: { + height: 600, + url: 'https://blobs.omnitagjs.com/blobs/f1/f1c80d4bb5643c22/fd4362d35bb174d6f1c80d4bb5643c22', + width: 300, + }, + impressionTrackers: [ + 'https://testPixelIMP.com/fake', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', + 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=INSERTION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991' + ], + javascriptTrackers: [ + 'https://testJsIMP.com/fake.js' + ], + privacyIcon: 'https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png', + privacyLink: 'https://blobs.omnitagjs.com/adchoice/', + sponsoredBy: 'QA Team', + title: 'Adserver Traffic Redirect Internal', + } + }]; + const responseWithMultiplePlacements = [ { 'BidID': 'bid_id_0', @@ -569,46 +637,21 @@ describe('Adyoulike Adapter', function () { }); it('receive reponse with Native ad', function () { - serverResponse.body = responseWithSinglePlacement; + serverResponse.body = responseWithSingleNative; let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); expect(result.length).to.equal(1); - expect(result).to.deep.equal([{ - cpm: 0.5, - creativeId: undefined, - currency: 'USD', - netRevenue: true, - requestId: 'bid_id_0', - ttl: 3600, - mediaType: 'native', - native: { - body: 'Considérant l\'extrémité conjoncturelle, il serait bon d\'anticiper toutes les voies de bon sens.', - clickTrackers: [ - 'https://testPixelCLICK.com/fake' - ], - clickUrl: 'https://tracking.omnitagjs.com/tracking/ar?event_kind=CLICK&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991&url=https%3A%2F%2Fwww.w3.org%2FPeople%2Fmimasa%2Ftest%2Fxhtml%2Fentities%2Fentities-11.xhtml%23lat1', - cta: 'Click here to learn more', - image: { - height: 600, - url: 'https://blobs.omnitagjs.com/blobs/f1/f1c80d4bb5643c22/fd4362d35bb174d6f1c80d4bb5643c22', - width: 300, - }, - impressionTrackers: [ - 'https://testPixelIMP.com/fake', - 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=IMPRESSION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991', - 'https://tracking.omnitagjs.com/tracking/pixel?event_kind=INSERTION&attempt=a11a121205932e75e622af275681965d&campaign=f1c80d4bb5643c222ae8de75e9b2f991' - ], - javascriptTrackers: [ - 'https://testJsIMP.com/fake.js' - ], - privacyIcon: 'https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png', - privacyLink: 'https://blobs.omnitagjs.com/adchoice/', - sponsoredBy: 'QA Team', - title: 'Adserver Traffic Redirect Internal', - } + expect(result).to.deep.equal(nativeResult); + }); + + it('receive reponse with Native from ad markup', function () { + serverResponse.body = responseWithSinglePlacement; + let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); + + expect(result.length).to.equal(1); - }]); + expect(result).to.deep.equal(nativeResult); }); }); }); From 438b0e9c8086ebbb06c291c4574136db710ce138 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Thu, 27 May 2021 16:23:55 +0600 Subject: [PATCH 06/63] Zeta Ssp Bid Adapter: Improve user sync logic (#6835) --- modules/zetaSspBidAdapter.js | 46 ++++++++++++++------- test/spec/modules/zetaSspBidAdapter_spec.js | 30 ++++++++++++-- 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index 450608a82f4..258478b0886 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -5,7 +5,8 @@ import {config} from '../src/config.js'; const BIDDER_CODE = 'zeta_global_ssp'; const ENDPOINT_URL = 'https://ssp.disqus.com/bid'; -const USER_SYNC_URL = 'https://ssp.disqus.com/match'; +const USER_SYNC_URL_IFRAME = 'https://ssp.disqus.com/sync?type=iframe'; +const USER_SYNC_URL_IMAGE = 'https://ssp.disqus.com/sync?type=image'; const DEFAULT_CUR = 'USD'; const TTL = 200; const NET_REV = true; @@ -58,7 +59,7 @@ export const spec = { app: params.app ? params.app : {}, ext: { tags: params.tags ? params.tags : {}, - sid: params.sid ? params.sid : {} + sid: params.sid ? params.sid : undefined } }; @@ -123,23 +124,38 @@ export const spec = { }, /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @param gdprConsent The GDPR consent parameters - * @param uspConsent The USP consent parameters - * @return {UserSync[]} The user syncs which should be dropped. + * Register User Sync. */ - getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = []; + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { + let syncurl = ''; + + // Attaching GDPR Consent Params in UserSync url + if (gdprConsent) { + syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); + syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); + } + + // CCPA + if (uspConsent) { + syncurl += '&us_privacy=' + encodeURIComponent(uspConsent); + } + + // coppa compliance + if (config.getConfig('coppa') === true) { + syncurl += '&coppa=1'; + } + if (syncOptions.iframeEnabled) { - syncs.push({ + return [{ type: 'iframe', - url: USER_SYNC_URL - }); + url: USER_SYNC_URL_IFRAME + syncurl + }]; + } else { + return [{ + type: 'image', + url: USER_SYNC_URL_IMAGE + syncurl + }]; } - return syncs; } } diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index bdfc64c3234..2766632f707 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -1,6 +1,6 @@ -import { spec } from '../../../modules/zetaSspBidAdapter.js' +import {spec} from '../../../modules/zetaSspBidAdapter.js' -describe('Zeta Ssp Bid Adapter', function() { +describe('Zeta Ssp Bid Adapter', function () { const bannerRequest = [{ bidId: 12345, auctionId: 67890, @@ -26,7 +26,7 @@ describe('Zeta Ssp Bid Adapter', function() { } }]; - it('Test the bid validation function', function() { + it('Test the bid validation function', function () { const validBid = spec.isBidRequestValid(bannerRequest[0]); const invalidBid = spec.isBidRequestValid(null); @@ -82,4 +82,28 @@ describe('Zeta Ssp Bid Adapter', function() { expect(bid.requestId).to.equal(receivedBid.impid); expect(bid.meta.advertiserDomains).to.equal(receivedBid.adomain); }); + + it('Different cases for user syncs', function () { + const USER_SYNC_URL_IFRAME = 'https://ssp.disqus.com/sync?type=iframe'; + const USER_SYNC_URL_IMAGE = 'https://ssp.disqus.com/sync?type=image'; + + const sync1 = spec.getUserSyncs({iframeEnabled: true})[0]; + expect(sync1.type).to.equal('iframe'); + expect(sync1.url).to.include(USER_SYNC_URL_IFRAME); + + const sync2 = spec.getUserSyncs({iframeEnabled: false})[0]; + expect(sync2.type).to.equal('image'); + expect(sync2.url).to.include(USER_SYNC_URL_IMAGE); + + const sync3 = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true})[0]; + expect(sync3.type).to.equal('iframe'); + expect(sync3.url).to.include(USER_SYNC_URL_IFRAME); + expect(sync3.url).to.include('&gdpr='); + + const sync4 = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true}, 'test')[0]; + expect(sync4.type).to.equal('iframe'); + expect(sync4.url).to.include(USER_SYNC_URL_IFRAME); + expect(sync4.url).to.include('&gdpr='); + expect(sync4.url).to.include('&us_privacy='); + }); }); From 77ab4ecf1bfe072ef48193372214de571338bcf3 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Thu, 27 May 2021 13:03:23 +0200 Subject: [PATCH 07/63] Mediasquare Bid Adapter: support advertiserDomains (#6843) * Mediasquare Bid Adapter: support advertiserDomains * Update mediasquareBidAdapter.js * add unit tests * fix unit test * fix string single quote --- modules/mediasquareBidAdapter.js | 3 +++ test/spec/modules/mediasquareBidAdapter_spec.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/modules/mediasquareBidAdapter.js b/modules/mediasquareBidAdapter.js index 84783eb0991..a586157bf20 100644 --- a/modules/mediasquareBidAdapter.js +++ b/modules/mediasquareBidAdapter.js @@ -101,6 +101,9 @@ export const spec = { mediasquare: { 'bidder': value['bidder'], 'code': value['code'] + }, + meta: { + 'advertiserDomains': value['adomain'] } }; if ('native' in value) { diff --git a/test/spec/modules/mediasquareBidAdapter_spec.js b/test/spec/modules/mediasquareBidAdapter_spec.js index 796be3420e8..20e5588a99e 100644 --- a/test/spec/modules/mediasquareBidAdapter_spec.js +++ b/test/spec/modules/mediasquareBidAdapter_spec.js @@ -76,6 +76,7 @@ describe('MediaSquare bid adapter tests', function () { 'bidder': 'msqClassic', 'code': 'test/publishername_atf_desktop_rg_pave', 'bid_id': 'aaaa1234', + 'adomain': ['test.com'], }], }}; @@ -135,6 +136,9 @@ describe('MediaSquare bid adapter tests', function () { expect(bid.mediasquare).to.exist; expect(bid.mediasquare.bidder).to.equal('msqClassic'); expect(bid.mediasquare.code).to.equal([DEFAULT_PARAMS[0].params.owner, DEFAULT_PARAMS[0].params.code].join('/')); + expect(bid.meta).to.exist; + expect(bid.meta.advertiserDomains).to.exist; + expect(bid.meta.advertiserDomains).to.have.lengthOf(1); }); it('Verifies bidder code', function () { From 7baf4b08cc79c5cdeab04bd6e0efb34762e17bea Mon Sep 17 00:00:00 2001 From: thuyhq <61451682+thuyhq@users.noreply.github.com> Date: Thu, 27 May 2021 18:40:41 +0700 Subject: [PATCH 08/63] Apacdex Bid Adapter: add support for meta.advertiserDomains, add and update sample adunit, validate dealId field from server response, add support Price Floors Module (#6711) --- modules/apacdexBidAdapter.js | 44 +++++++++++-- modules/apacdexBidAdapter.md | 56 ++++++++++++++-- test/spec/modules/apacdexBidAdapter_spec.js | 73 +++++++++++++++++---- 3 files changed, 153 insertions(+), 20 deletions(-) diff --git a/modules/apacdexBidAdapter.js b/modules/apacdexBidAdapter.js index 62ae3f54125..c0431ddf923 100644 --- a/modules/apacdexBidAdapter.js +++ b/modules/apacdexBidAdapter.js @@ -55,13 +55,13 @@ export const spec = { let eids; let geo; let test; + let bids = []; - var bids = JSON.parse(JSON.stringify(validBidRequests)) - bidderConfig = CONFIG[bids[0].bidder]; + bidderConfig = CONFIG[validBidRequests[0].bidder]; test = config.getConfig('debug'); - bids.forEach(bidReq => { + validBidRequests.forEach(bidReq => { siteId = siteId || bidReq.params.siteId; if (bidReq.schain) { @@ -95,6 +95,13 @@ export const spec = { } bySlotTargetKey[bidReq.adUnitCode] = targetKey; bidReq.targetKey = targetKey; + + let bidFloor = getBidFloor(bidReq); + if (bidFloor) { + bidReq.bidFloor = bidFloor; + } + + bids.push(JSON.parse(JSON.stringify(bidReq))); }); const payload = {}; @@ -169,23 +176,30 @@ export const spec = { const bidResponses = []; serverBids.forEach(bid => { + const dealId = bid.dealId || ''; const bidResponse = { requestId: bid.requestId, cpm: bid.cpm, width: bid.width, height: bid.height, creativeId: bid.creativeId, - dealId: bid.dealId, currency: bid.currency, netRevenue: bid.netRevenue, ttl: bid.ttl, mediaType: bid.mediaType }; + if (dealId.length > 0) { + bidResponse.dealId = dealId; + } if (bid.vastXml) { bidResponse.vastXml = utils.replaceAuctionPrice(bid.vastXml, bid.cpm); } else { bidResponse.ad = utils.replaceAuctionPrice(bid.ad, bid.cpm); } + bidResponse.meta = {}; + if (bid.meta && bid.meta.advertiserDomains && utils.isArray(bid.meta.advertiserDomains)) { + bidResponse.meta.advertiserDomains = bid.meta.advertiserDomains; + } bidResponses.push(bidResponse); }); return bidResponses; @@ -336,4 +350,26 @@ export function validateGeoObject(geo) { return true; } +/** + * Get bid floor from Price Floors Module + * + * @param {Object} bid + * @returns {float||null} + */ +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return (bid.params.floorPrice) ? bid.params.floorPrice : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); diff --git a/modules/apacdexBidAdapter.md b/modules/apacdexBidAdapter.md index b88190cda94..fa8c727d709 100644 --- a/modules/apacdexBidAdapter.md +++ b/modules/apacdexBidAdapter.md @@ -11,7 +11,7 @@ Maintainer: ken@apacdex.com Connects to APAC Digital Exchange for bids. Apacdex bid adapter supports Banner and Video (Instream and Outstream) ads. -# Test Parameters +# Sample Banner Ad Unit ``` var adUnits = [ { @@ -26,6 +26,7 @@ var adUnits = [ bidder: 'apacdex', params: { siteId: 'apacdex1234', // siteId provided by Apacdex + floorPrice: 0.01, // default is 0.01 if not declared } } ] @@ -33,7 +34,7 @@ var adUnits = [ ]; ``` -# Video Test Parameters +# Sample Video Ad Unit: Instream ``` var videoAdUnit = { code: 'test-div', @@ -41,7 +42,17 @@ var videoAdUnit = { mediaTypes: { video: { playerSize: [[640, 480]], - context: 'instream' + context: "instream" + api: [2], + placement: 1, + skip: 1, + linearity: 1, + minduration: 1, + maxduration: 120, + mimes: ["video/mp4", "video/x-flv", "video/x-ms-wmv", "application/vnd.apple.mpegurl", "application/x-mpegurl", "video/3gpp", "video/mpeg", "video/ogg", "video/quicktime", "video/webm", "video/x-m4v", "video/ms-asf", video/x-msvideo"], + playbackmethod: [6], + startdelay: 0, + protocols: [1, 2, 3, 4, 5, 6] }, }, bids: [ @@ -49,8 +60,45 @@ var videoAdUnit = { bidder: 'apacdex', params: { siteId: 'apacdex1234', // siteId provided by Apacdex + floorPrice: 0.01, // default is 0.01 if not declared } } ] }; -``` \ No newline at end of file +``` +mediaTypes.video object reference to section 3.2.7 Object: Video in the OpenRTB 2.5 document +You must review all video parameters to ensure validity for your player and DSPs + +# Sample Video Ad Unit: Outstream +``` +var videoAdUnit = { + code: 'test-div', + sizes: [[410, 231]], + mediaTypes: { + video: { + playerSize: [[410, 231]], + context: "outstream" + api: [2], + placement: 5, + linearity: 1, + minduration: 1, + maxduration: 120, + mimes: ["video/mp4", "video/x-flv", "video/x-ms-wmv", "application/vnd.apple.mpegurl", "application/x-mpegurl", "video/3gpp", "video/mpeg", "video/ogg", "video/quicktime", "video/webm", "video/x-m4v", "video/ms-asf", video/x-msvideo"], + playbackmethod: [6], + startdelay: 0, + protocols: [1, 2, 3, 4, 5, 6] + }, + }, + bids: [ + { + bidder: 'apacdex', + params: { + siteId: 'apacdex1234', // siteId provided by Apacdex + floorPrice: 0.01, // default is 0.01 if not declared + } + } + ] +}; +``` +mediaTypes.video object reference to section 3.2.7 Object: Video in the OpenRTB 2.5 document +You must review all video parameters to ensure validity for your player and DSPs \ No newline at end of file diff --git a/test/spec/modules/apacdexBidAdapter_spec.js b/test/spec/modules/apacdexBidAdapter_spec.js index 3a71833bc3e..5f6a935c453 100644 --- a/test/spec/modules/apacdexBidAdapter_spec.js +++ b/test/spec/modules/apacdexBidAdapter_spec.js @@ -3,6 +3,7 @@ import { spec, validateGeoObject, getDomain } from '../../../modules/apacdexBidA import { newBidder } from 'src/adapters/bidderFactory.js' import { userSync } from '../../../src/userSync.js'; import { config } from 'src/config.js'; +import { deepClone } from 'src/utils.js'; describe('ApacdexBidAdapter', function () { const adapter = newBidder(spec) @@ -200,7 +201,7 @@ describe('ApacdexBidAdapter', function () { 'bidder': 'apacdex', 'params': { 'siteId': '1a2b3c4d5e6f1a2b3c4d', - 'geo': {'lat': 123.13123456, 'lon': 54.23467311, 'accuracy': 60} + 'geo': { 'lat': 123.13123456, 'lon': 54.23467311, 'accuracy': 60 } }, 'adUnitCode': 'adunit-code-1', 'sizes': [[300, 250], [300, 600]], @@ -336,12 +337,50 @@ describe('ApacdexBidAdapter', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); expect(bidRequests.data.us_privacy).to.equal('someCCPAString'); }); - describe('debug test', function() { - beforeEach(function() { - config.setConfig({debug: true}); + it('should attach bidFloor param when either bid param floorPrice or getFloor function exists', function () { + let getFloorResponse = { currency: 'USD', floor: 3 }; + let singleBidRequest, request, payload = null; + + // 1 -> floorPrice not defined, getFloor not defined > empty + singleBidRequest = deepClone(bidRequest[0]); + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data; + expect(payload.bids[0].bidFloor).to.not.exist; + + // 2 -> floorPrice is defined, getFloor not defined > floorPrice is used + singleBidRequest = deepClone(bidRequest[0]); + singleBidRequest.params = { + 'siteId': '1890909', + 'floorPrice': 0.5 + }; + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data + expect(payload.bids[0].bidFloor).to.exist.and.to.equal(0.5); + + // 3 -> floorPrice is defined, getFloor is defined > getFloor is used + singleBidRequest = deepClone(bidRequest[0]); + singleBidRequest.params = { + 'siteId': '1890909', + 'floorPrice': 0.5 + }; + singleBidRequest.getFloor = () => getFloorResponse; + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data + expect(payload.bids[0].bidFloor).to.exist.and.to.equal(3); + + // 4 -> floorPrice not defined, getFloor is defined > getFloor is used + singleBidRequest = deepClone(bidRequest[0]); + singleBidRequest.getFloor = () => getFloorResponse; + request = spec.buildRequests([singleBidRequest], bidderRequests); + payload = request.data + expect(payload.bids[0].bidFloor).to.exist.and.to.equal(3); + }); + describe('debug test', function () { + beforeEach(function () { + config.setConfig({ debug: true }); }); - afterEach(function() { - config.setConfig({debug: false}); + afterEach(function () { + config.setConfig({ debug: false }); }); it('should return a properly formatted request with pbjs_debug is true', function () { const bidRequests = spec.buildRequests(bidRequest, bidderRequests); @@ -514,7 +553,10 @@ describe('ApacdexBidAdapter', function () { 'netRevenue': true, 'currency': 'USD', 'dealId': 'apacdex', - 'mediaType': 'banner' + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } }, { 'requestId': '30024615be22ef66a', @@ -527,7 +569,10 @@ describe('ApacdexBidAdapter', function () { 'netRevenue': true, 'currency': 'USD', 'dealId': 'apacdex', - 'mediaType': 'banner' + 'mediaType': 'banner', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } }, { 'requestId': '1854b40107d6745c', @@ -540,7 +585,10 @@ describe('ApacdexBidAdapter', function () { 'netRevenue': true, 'currency': 'USD', 'dealId': 'apacdex', - 'mediaType': 'video' + 'mediaType': 'video', + 'meta': { + 'advertiserDomains': ['https://example.com'] + } } ], 'pixel': [{ @@ -610,6 +658,7 @@ describe('ApacdexBidAdapter', function () { if (resp.mediaType === 'banner') { expect(resp.ad.indexOf('Apacdex AD')).to.be.greaterThan(0); } + expect(resp.meta.advertiserDomains).to.deep.equal(['https://example.com']); }); }); }); @@ -693,17 +742,17 @@ describe('ApacdexBidAdapter', function () { describe('getDomain', function () { it('should return valid domain from publisherDomain config', () => { let pageUrl = 'https://www.example.com/page/prebid/exam.html'; - config.setConfig({publisherDomain: pageUrl}); + config.setConfig({ publisherDomain: pageUrl }); expect(getDomain(pageUrl)).to.equal('example.com'); }); it('should return valid domain from pageUrl argument', () => { let pageUrl = 'https://www.example.com/page/prebid/exam.html'; - config.setConfig({publisherDomain: ''}); + config.setConfig({ publisherDomain: '' }); expect(getDomain(pageUrl)).to.equal('example.com'); }); it('should return undefined if pageUrl and publisherDomain not config', () => { let pageUrl; - config.setConfig({publisherDomain: ''}); + config.setConfig({ publisherDomain: '' }); expect(getDomain(pageUrl)).to.equal(pageUrl); }); }); From a5e7d08950f33301372942a64333522474285895 Mon Sep 17 00:00:00 2001 From: rcheptanariu <35690143+rcheptanariu@users.noreply.github.com> Date: Thu, 27 May 2021 16:07:36 +0300 Subject: [PATCH 09/63] Invibes Bid Adapter - support for meta taxonomy (#6849) --- modules/invibesBidAdapter.js | 19 ++++++++++++-- test/spec/modules/invibesBidAdapter_spec.js | 29 ++++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index 7d2942eea55..18011359a6d 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -11,7 +11,8 @@ const CONSTANTS = { PREBID_VERSION: 6, METHOD: 'GET', INVIBES_VENDOR_ID: 436, - USERID_PROVIDERS: ['pubcid', 'pubProvidedId', 'uid2', 'zeotapIdPlus', 'id5id'] + USERID_PROVIDERS: ['pubcid', 'pubProvidedId', 'uid2', 'zeotapIdPlus', 'id5id'], + META_TAXONOMY: ['networkId', 'networkName', 'agencyId', 'agencyName', 'advertiserId', 'advertiserName', 'advertiserDomains', 'brandId', 'brandName', 'primaryCatId', 'secondaryCatIds', 'mediaType'] }; const storage = getStorageManager(CONSTANTS.INVIBES_VENDOR_ID); @@ -236,10 +237,24 @@ function createBid(bidRequest, requestPlacement) { currency: bidModel.Currency || CONSTANTS.DEFAULT_CURRENCY, netRevenue: true, ttl: CONSTANTS.TIME_TO_LIVE, - ad: renderCreative(bidModel) + ad: renderCreative(bidModel), + meta: addMeta(bidModel.Meta) }; } +function addMeta(bidModelMeta) { + var meta = {}; + if (bidModelMeta != null) { + for (let i = 0; i < CONSTANTS.META_TAXONOMY.length; i++) { + if (bidModelMeta.hasOwnProperty(CONSTANTS.META_TAXONOMY[i])) { + meta[CONSTANTS.META_TAXONOMY[i]] = bidModelMeta[CONSTANTS.META_TAXONOMY[i]]; + } + } + } + + return meta; +} + function generateRandomId() { return (Math.round(Math.random() * 1e12)).toString(36).substring(0, 10); } diff --git a/test/spec/modules/invibesBidAdapter_spec.js b/test/spec/modules/invibesBidAdapter_spec.js index 6a59bf98dad..ea3e1d6611b 100644 --- a/test/spec/modules/invibesBidAdapter_spec.js +++ b/test/spec/modules/invibesBidAdapter_spec.js @@ -733,7 +733,8 @@ describe('invibesBidAdapter:', function () { - ` + `, + meta: {} }]; let multiResponse = { @@ -760,6 +761,23 @@ describe('invibesBidAdapter:', function () { }] }; + let responseWithMeta = { + Ads: [{ + BidPrice: 0.5, + VideoExposedId: 123 + }], + BidModel: { + BidVersion: 1, + PlacementId: '12345', + AuctionStartTime: Date.now(), + CreativeHtml: '', + Meta: { + advertiserDomains: ['theadvertiser.com', 'theadvertiser_2.com'], + advertiserName: 'theadvertiser' + } + } + }; + context('when the response is not valid', function () { it('handles response with no bids requested', function () { let emptyResult = spec.interpretResponse({body: response}); @@ -829,6 +847,15 @@ describe('invibesBidAdapter:', function () { expect(secondResult).to.be.empty; }); }); + + context('when the response has meta', function () { + it('responds with a valid bid, with the meta info', function () { + let result = spec.interpretResponse({body: responseWithMeta}, {bidRequests}); + expect(result[0].meta.advertiserName).to.equal('theadvertiser'); + expect(result[0].meta.advertiserDomains).to.contain('theadvertiser.com'); + expect(result[0].meta.advertiserDomains).to.contain('theadvertiser_2.com'); + }); + }); }); describe('getUserSyncs', function () { From 399292da7f9c7c11531be6cc05efe72b8f7a823f Mon Sep 17 00:00:00 2001 From: nyakove <43004249+nyakove@users.noreply.github.com> Date: Thu, 27 May 2021 16:57:07 +0300 Subject: [PATCH 10/63] adWMG Adapter: add 'adomain' support (#6852) * Support floorCPM parameter, fix some minor bugs * fix space-in-parens circleci error * example fix * clean usersync URL * spaces * spaces * add new unit tests, compatibility with IE11 * remove logInfo * Check for floorCPM value * Check params before sending * New endpoints * code format * new endpoint for cookie sync * update tests * Add meta.advertiserDomains data support * Add meta.advertiserDomains data support Co-authored-by: Mikhail Dykun --- modules/adWMGBidAdapter.js | 4 ++++ test/spec/modules/adWMGBidAdapter_spec.js | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/adWMGBidAdapter.js b/modules/adWMGBidAdapter.js index 689e7d02124..a3d78a69d91 100644 --- a/modules/adWMGBidAdapter.js +++ b/modules/adWMGBidAdapter.js @@ -116,6 +116,10 @@ export const spec = { netRevenue: response.netRevenue, ttl: response.ttl, ad: response.ad, + meta: { + advertiserDomains: response.adomain && response.adomain.length ? response.adomain : [], + mediaType: 'banner' + } }; bidResponses.push(bidResponse); } diff --git a/test/spec/modules/adWMGBidAdapter_spec.js b/test/spec/modules/adWMGBidAdapter_spec.js index 8b927ace84c..db536ca14e2 100644 --- a/test/spec/modules/adWMGBidAdapter_spec.js +++ b/test/spec/modules/adWMGBidAdapter_spec.js @@ -209,7 +209,8 @@ describe('adWMGBidAdapter', function () { 'ttl': 300, 'creativeId': 'creative-id', 'netRevenue': true, - 'currency': 'USD' + 'currency': 'USD', + 'adomain': ['testdomain.com'] } }; }); @@ -219,7 +220,7 @@ describe('adWMGBidAdapter', function () { expect(responses).to.be.an('array').that.is.not.empty; let response = responses[0]; - expect(response).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + expect(response).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'meta', 'ad', 'ttl', 'creativeId', 'netRevenue', 'currency'); expect(response.requestId).to.equal('request-id'); expect(response.cpm).to.equal(100); @@ -230,6 +231,8 @@ describe('adWMGBidAdapter', function () { expect(response.creativeId).to.equal('creative-id'); expect(response.netRevenue).to.be.true; expect(response.currency).to.equal('USD'); + expect(response.meta.advertiserDomains[0]).to.equal('testdomain.com'); + expect(response.meta.mediaType).to.equal('banner'); }); it('should return an empty array when serverResponse is empty', () => { From 0523a1daa9cf66a8a4df513e8a07b1d94428c8a3 Mon Sep 17 00:00:00 2001 From: ardit-baloku <77985953+ardit-baloku@users.noreply.github.com> Date: Thu, 27 May 2021 16:01:34 +0200 Subject: [PATCH 11/63] Added meta.advertiserDomains to bidResponse (#6853) --- modules/gjirafaBidAdapter.js | 5 ++++- test/spec/modules/gjirafaBidAdapter_spec.js | 21 +++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/modules/gjirafaBidAdapter.js b/modules/gjirafaBidAdapter.js index 77589cd9071..df33369d6ad 100644 --- a/modules/gjirafaBidAdapter.js +++ b/modules/gjirafaBidAdapter.js @@ -95,7 +95,10 @@ export const spec = { referrer: responses[i].Referrer, ad: responses[i].Ad, vastUrl: responses[i].VastUrl, - mediaType: responses[i].MediaType + mediaType: responses[i].MediaType, + meta: { + advertiserDomains: Array.isArray(responses[i].ADomain) ? responses[i].ADomain : [] + } }; bidResponses.push(bidResponse); } diff --git a/test/spec/modules/gjirafaBidAdapter_spec.js b/test/spec/modules/gjirafaBidAdapter_spec.js index f0fb01f4398..96bf319dfd2 100644 --- a/test/spec/modules/gjirafaBidAdapter_spec.js +++ b/test/spec/modules/gjirafaBidAdapter_spec.js @@ -136,7 +136,8 @@ describe('gjirafaAdapterTest', () => { 'CreativeId': '123abc', 'NetRevenue': false, 'Currency': 'EUR', - 'TTL': 360 + 'TTL': 360, + 'ADomain': ['somedomain.com'] }], headers: {} }; @@ -156,7 +157,8 @@ describe('gjirafaAdapterTest', () => { 'referrer', 'ad', 'vastUrl', - 'mediaType' + 'mediaType', + 'meta' ]; let resultKeys = Object.keys(result[0]); @@ -164,5 +166,20 @@ describe('gjirafaAdapterTest', () => { expect(keys.indexOf(key) !== -1).to.equal(true); }); }) + + it('all values correct', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(728); + expect(result[0].height).to.equal(90); + expect(result[0].creativeId).to.equal('123abc'); + expect(result[0].currency).to.equal('EUR'); + expect(result[0].netRevenue).to.equal(false); + expect(result[0].ttl).to.equal(360); + expect(result[0].referrer).to.equal('http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true'); + expect(result[0].ad).to.equal('
Test ad
'); + expect(result[0].meta.advertiserDomains).to.deep.equal(['somedomain.com']); + }) }); }); From f02c1fe37c6935dc2b3d9cef292eb8633ffb4679 Mon Sep 17 00:00:00 2001 From: ardit-baloku <77985953+ardit-baloku@users.noreply.github.com> Date: Thu, 27 May 2021 16:21:26 +0200 Subject: [PATCH 12/63] Added meta.advertiserDomains to bidResponse (#6854) --- modules/malltvBidAdapter.js | 5 ++++- test/spec/modules/malltvBidAdapter_spec.js | 21 +++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/modules/malltvBidAdapter.js b/modules/malltvBidAdapter.js index 7deffe6c07a..4e600135e0b 100644 --- a/modules/malltvBidAdapter.js +++ b/modules/malltvBidAdapter.js @@ -95,7 +95,10 @@ export const spec = { referrer: responses[i].Referrer, ad: responses[i].Ad, vastUrl: responses[i].VastUrl, - mediaType: responses[i].MediaType + mediaType: responses[i].MediaType, + meta: { + advertiserDomains: Array.isArray(responses[i].ADomain) ? responses[i].ADomain : [] + } }; bidResponses.push(bidResponse); } diff --git a/test/spec/modules/malltvBidAdapter_spec.js b/test/spec/modules/malltvBidAdapter_spec.js index ffe08ad1a5e..c31e91992f7 100644 --- a/test/spec/modules/malltvBidAdapter_spec.js +++ b/test/spec/modules/malltvBidAdapter_spec.js @@ -136,7 +136,8 @@ describe('malltvAdapterTest', () => { 'CreativeId': '123abc', 'NetRevenue': false, 'Currency': 'EUR', - 'TTL': 360 + 'TTL': 360, + 'ADomain': ['somedomain.com'] }], headers: {} }; @@ -156,7 +157,8 @@ describe('malltvAdapterTest', () => { 'referrer', 'ad', 'vastUrl', - 'mediaType' + 'mediaType', + 'meta' ]; let resultKeys = Object.keys(result[0]); @@ -164,5 +166,20 @@ describe('malltvAdapterTest', () => { expect(keys.indexOf(key) !== -1).to.equal(true); }); }) + + it('all values correct', () => { + const result = spec.interpretResponse(bidResponse, bidRequest); + + expect(result[0].cpm).to.equal(1); + expect(result[0].width).to.equal(300); + expect(result[0].height).to.equal(250); + expect(result[0].creativeId).to.equal('123abc'); + expect(result[0].currency).to.equal('EUR'); + expect(result[0].netRevenue).to.equal(false); + expect(result[0].ttl).to.equal(360); + expect(result[0].referrer).to.equal('http://localhost:9999/integrationExamples/gpt/hello_world.html?pbjs_debug=true'); + expect(result[0].ad).to.equal('
Test ad
'); + expect(result[0].meta.advertiserDomains).to.deep.equal(['somedomain.com']); + }) }); }); From d237cbffc88d822a03bd4d23e475aba1960814ac Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Thu, 27 May 2021 07:39:30 -0700 Subject: [PATCH 13/63] LGTM Fixes: fixes for current LGTM issues (#6851) --- integrationExamples/gpt/idImportLibrary_example.html | 1 + modules/aolBidAdapter.js | 2 +- modules/datablocksBidAdapter.js | 6 +++--- modules/gridBidAdapter.js | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/integrationExamples/gpt/idImportLibrary_example.html b/integrationExamples/gpt/idImportLibrary_example.html index a3ef3f168c0..f35b571e8f9 100644 --- a/integrationExamples/gpt/idImportLibrary_example.html +++ b/integrationExamples/gpt/idImportLibrary_example.html @@ -47,6 +47,7 @@ params: { pid: '14', // Set your real identityLink placement ID here // notUse3P: true // true/false - If you do not want to use 3P endpoint to retrieve envelope. If you do not set this property to true, 3p endpoint will be fired. By default this propertt is undefined and 3p request will be fired.}, + }, storage: { type: 'html5', name: 'idl_env', diff --git a/modules/aolBidAdapter.js b/modules/aolBidAdapter.js index 14b529f4973..03e4ac9021a 100644 --- a/modules/aolBidAdapter.js +++ b/modules/aolBidAdapter.js @@ -366,7 +366,7 @@ export const spec = { let tagName = item.match(tagNameRegExp)[0]; let url = item.match(srcRegExp)[2]; - if (tagName && tagName) { + if (tagName && url) { pixelsItems.push({ type: tagName === SYNC_TYPES.IMAGE.TAG ? SYNC_TYPES.IMAGE.TYPE : SYNC_TYPES.IFRAME.TYPE, url: url diff --git a/modules/datablocksBidAdapter.js b/modules/datablocksBidAdapter.js index b00a3eae659..47d2eb2652f 100644 --- a/modules/datablocksBidAdapter.js +++ b/modules/datablocksBidAdapter.js @@ -68,7 +68,7 @@ export const spec = { id: bidRequest.bidId, tagid: bidRequest.adUnitCode, secure: window.location.protocol == 'https:' - } + }; if (utils.deepAccess(bidRequest, `mediaTypes.banner`)) { let sizes = bidRequest.mediaTypes.banner.sizes; @@ -76,7 +76,7 @@ export const spec = { imp.banner = { w: sizes[0][0], h: sizes[0][1] - } + }; } else if (sizes.length > 1) { imp.banner = { format: sizes.map(size => ({ w: size[0], h: size[1] })) @@ -181,7 +181,7 @@ export const spec = { if (VIDEO_PARAMS.indexOf(k) > -1) { imp.video[k] = bidRequest.params.video[k]; } - }) + }); } } let host = bidRequest.params.host; diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 955aeff7168..dbeba27d836 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -350,7 +350,7 @@ function _addBidResponse(serverBid, bidRequest, bidResponses) { netRevenue: false, ttl: TIME_TO_LIVE, meta: { - advertiserDomains: serverBid && serverBid.adomain ? serverBid.adomain : [] + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] }, dealId: serverBid.dealid }; From 54bdd70426fb6ff5c2d4c22b1696a02b7d8ba16e Mon Sep 17 00:00:00 2001 From: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Date: Thu, 27 May 2021 17:14:10 +0200 Subject: [PATCH 14/63] sspBC Bid Adapter: add bidfloor to imp and other updates to bid adapter (#6820) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update tests for sspBC adapter Update tests for sspBC adapter: - change userSync test (due to tcf param appended in v4.6) - add tests for onBidWon and onTimeout * [sspbc-adapter] Update to v4.8 (floorprices, usersync, code cleaning) * [sspbc-adapter] remove bidloor param from md. Remove unused test for old module (sspBCAdapter) Co-authored-by: Wojciech Biały --- modules/sspBCBidAdapter.js | 84 +++++++++++++++++++------------------- modules/sspBCBidAdapter.md | 1 - 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 391f3a05721..d166a01a1da 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -9,7 +9,7 @@ const BIDDER_URL = 'https://ssp.wp.pl/bidder/'; const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const TMAX = 450; -const BIDDER_VERSION = '4.8'; +const BIDDER_VERSION = '4.9'; const W = window; const { navigator } = W; const oneCodeDetection = {}; @@ -102,25 +102,29 @@ const applyClientHints = ortbRequest => { ortbRequest.user = Object.assign(ortbRequest.user, { data }); }; -function applyGdpr(bidderRequest, ortbRequest) { - if (bidderRequest && bidderRequest.gdprConsent) { - consentApiVersion = bidderRequest.gdprConsent.apiVersion; - ortbRequest.regs = Object.assign(ortbRequest.regs, { '[ortb_extensions.gdpr]': bidderRequest.gdprConsent.gdprApplies ? 1 : 0 }); - ortbRequest.user = Object.assign(ortbRequest.user, { '[ortb_extensions.consent]': bidderRequest.gdprConsent.consentString }); +/** + * Add GDPR data to oRTB request + * Store conset API version (will be required by user sync) + */ +const applyGdpr = (bidderRequest, ortbRequest) => { + const { gdprConsent } = bidderRequest; + if (gdprConsent) { + const { apiVersion, gdprApplies, consentString } = gdprConsent; + consentApiVersion = apiVersion; + ortbRequest.regs = Object.assign(ortbRequest.regs, { '[ortb_extensions.gdpr]': gdprApplies ? 1 : 0 }); + ortbRequest.user = Object.assign(ortbRequest.user, { '[ortb_extensions.consent]': consentString }); } } -function setOnAny(collection, key) { - for (let i = 0, result; i < collection.length; i++) { - result = utils.deepAccess(collection[i], key); - - if (result) { - return result; - } - } -} +/** + * Get value for first occurence of key within the collection + */ +const setOnAny = (collection, key) => collection.reduce((prev, next) => prev || utils.deepAccess(next, key), false); -function sendNotification(payload) { +/** + * Send payload to notification endpoint + */ +const sendNotification = payload => { ajax(NOTIFY_URL, null, JSON.stringify(payload), { withCredentials: false, method: 'POST', @@ -132,7 +136,7 @@ function sendNotification(payload) { * @param {object} slot Ad Unit Params by Prebid * @returns {object} Banner by OpenRTB 2.5 §3.2.6 */ -function mapBanner(slot) { +const mapBanner = slot => { if (slot.mediaType === 'banner' || utils.deepAccess(slot, 'mediaTypes.banner') || (!slot.mediaType && !slot.mediaTypes)) { @@ -141,8 +145,6 @@ function mapBanner(slot) { h: size[1], })); - // override - tylko 1szy wymiar - // format = format.slice(0, 1); return { format, id: slot.bidId, @@ -150,7 +152,7 @@ function mapBanner(slot) { } } -function mapImpression(slot) { +const mapImpression = slot => { const { adUnitCode, bidId, params } = slot; const { id, siteId } = params || {}; const imp = { @@ -160,16 +162,18 @@ function mapImpression(slot) { tagid: adUnitCode, }; - const bidfloor = (slot.params && slot.params.bidFloor) ? parseFloat(slot.params.bidFloor) : undefined; - - if (bidfloor) { - imp.bidfloor = bidfloor; + // Check floorprices for this imp + if (typeof slot.getFloor === 'function') { + // sspBC adapter accepts only floor per imp - check for maximum value for requested ad sizes + imp.bidfloor = slot.sizes.reduce((prev, next) => { + const currentFloor = slot.getFloor({ mediaType: 'banner', size: next }).floor; + return prev > currentFloor ? prev : currentFloor; + }, 0); } - return imp; } -function renderCreative(site, auctionId, bid, seat, request) { +const renderCreative = (site, auctionId, bid, seat, request) => { let gam; const mcad = { @@ -292,7 +296,7 @@ const spec = { return { method: 'POST', - url: BIDDER_URL + '?cs=' + cookieSupport() + '&bdver=' + BIDDER_VERSION + '&pbver=' + pbver + '&inver=0', + url: `${BIDDER_URL}?cs=${cookieSupport()}&bdver=${BIDDER_VERSION}&pbver=${pbver}&inver=0`, data: JSON.stringify(payload), bidderRequest, }; @@ -315,11 +319,11 @@ const spec = { seat = seatbid.seat; seatbid.bid.forEach(serverBid => { // get data from bid response - const { adomain, crid, impid, exp, ext, price, w, h } = serverBid; + const { adomain, crid = `mcad_${bidderRequest.auctionId}_${site.slot}`, impid, exp = 300, ext, price, w, h } = serverBid; const bidRequest = bidderRequest.bids.filter(b => { - const { bidId, params } = b; - const { id, siteId } = params || {}; + const { bidId, params = {} } = b; + const { id, siteId } = params; const currentBidId = id && siteId ? id : 'bidid-' + bidId; return currentBidId === impid; })[0]; @@ -348,10 +352,10 @@ const spec = { const bid = { requestId: bidId, - creativeId: crid || 'mcad_' + bidderRequest.auctionId + '_' + site.slot, + creativeId: crid, cpm: price, currency: response.cur, - ttl: exp || 300, + ttl: exp, width: w, height: h, bidderCode: BIDDER_CODE, @@ -365,14 +369,7 @@ const spec = { }; if (bid.cpm > 0) { - // check bidFloor (if present in params) - const { bidFloor } = params || {}; - - if (!bidFloor || bid.cpm >= bidFloor) { - bids.push(bid); - } else { - utils.logWarn('Discarding bid due to bidFloor setting', bid.cpm, bidFloor); - } + bids.push(bid); } } else { utils.logWarn('Discarding response - no matching request / site id', serverBid.impid); @@ -384,13 +381,14 @@ const spec = { return bids; }, getUserSyncs(syncOptions) { - if (syncOptions.iframeEnabled) { + if (syncOptions.iframeEnabled && consentApiVersion != 1) { return [{ type: 'iframe', - url: SYNC_URL + '?tcf=' + consentApiVersion, + url: `${SYNC_URL}?tcf=${consentApiVersion}`, }]; + } else { + utils.logWarn('sspBC adapter requires iframe based user sync.'); } - utils.logWarn('sspBC adapter requires iframe based user sync.'); }, onTimeout(timeoutData) { diff --git a/modules/sspBCBidAdapter.md b/modules/sspBCBidAdapter.md index f22e8e6c458..1678f3be594 100644 --- a/modules/sspBCBidAdapter.md +++ b/modules/sspBCBidAdapter.md @@ -20,7 +20,6 @@ Optional parameters: - domain - page - tmax -- bidFloor - test # Test Parameters From bcd1ebe079c40354a33b9496aa1edcce8f0f9681 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 27 May 2021 17:19:37 +0200 Subject: [PATCH 15/63] user id module - force calls to getId if there was previously no consent data stored (#6760) --- modules/userId/index.js | 11 ++++++----- test/spec/modules/userId_spec.js | 15 ++++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index 199934f4cdb..3f235d15dd1 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -277,6 +277,7 @@ function makeStoredConsentDataHash(consentData) { storedConsentData.gdprApplies = consentData.gdprApplies; storedConsentData.apiVersion = consentData.apiVersion; } + return utils.cyrb53Hash(JSON.stringify(storedConsentData)); } @@ -306,17 +307,17 @@ function getStoredConsentData() { } /** - * test if the consent object stored locally matches the current consent data. - * if there is nothing in storage, return true and we'll do an actual comparison next time. - * this way, we don't force a refresh for every user when this code rolls out + * test if the consent object stored locally matches the current consent data. if they + * don't match or there is nothing stored locally, it means a refresh of the user id + * submodule is needed * @param storedConsentData * @param consentData * @returns {boolean} */ function storedConsentDataMatchesConsentData(storedConsentData, consentData) { return ( - typeof storedConsentData === 'undefined' || - storedConsentData === null || + typeof storedConsentData !== 'undefined' && + storedConsentData !== null && storedConsentData === makeStoredConsentDataHash(consentData) ); } diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 95bba079fa2..420d2ddce91 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -234,9 +234,10 @@ describe('User ID', function () { }); }); }); - // Because the cookie exists already, there should be no setCookie call by default; the only setCookie call is - // to store consent data - expect(coreStorage.setCookie.callCount).to.equal(1); + // Because the consent cookie doesn't exist yet, we'll have two setCookie calls: + // 1) for the consent cookie + // 2) from the getId() call that results in a new call to store the results + expect(coreStorage.setCookie.callCount).to.equal(2); }); it('Extend cookie', function () { @@ -2407,7 +2408,7 @@ describe('User ID', function () { }); // check MockId data was copied to bid expect(bid).to.have.deep.nested.property('userId.mid'); - expect(bid.userId.mid).to.equal('123456778'); + expect(bid.userId.mid).to.equal('1234'); // also check that intentIqId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.intentIqId'); expect(bid.userId.intentIqId).to.equal('testintentIqId'); @@ -2785,7 +2786,7 @@ describe('User ID', function () { sharedAfterFunction(); }); - it('does not call getId if no stored consent data and refresh is not needed', function () { + it('calls getId if no stored consent data and refresh is not needed', function () { coreStorage.setCookie(mockIdCookieName, JSON.stringify({id: '1234'}), expStr); coreStorage.setCookie(`${mockIdCookieName}_last`, (new Date(Date.now() - 1 * 1000).toUTCString()), expStr); @@ -2796,9 +2797,9 @@ describe('User ID', function () { innerAdUnits = config.adUnits }, {adUnits}); - sinon.assert.notCalled(mockGetId); + sinon.assert.calledOnce(mockGetId); sinon.assert.calledOnce(mockDecode); - sinon.assert.calledOnce(mockExtendId); + sinon.assert.notCalled(mockExtendId); }); it('calls getId if no stored consent data but refresh is needed', function () { From 649ea9917df275906d10936b1d5e6fa1db53c562 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Thu, 27 May 2021 20:58:03 +0200 Subject: [PATCH 16/63] Prebid 4.41.0 Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66f1976d4ce..d4c6cd24181 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.41.0-pre", + "version": "4.41.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f2befaab4211240897413082724fe6958984af53 Mon Sep 17 00:00:00 2001 From: Steffen Anders Date: Thu, 27 May 2021 22:06:05 +0200 Subject: [PATCH 17/63] Optimized AdUp Technology bid adapter (#6800) --- modules/aduptechBidAdapter.js | 374 ++++++--- modules/aduptechBidAdapter.md | 87 ++- test/spec/modules/aduptechBidAdapter_spec.js | 780 +++++++++++-------- 3 files changed, 780 insertions(+), 461 deletions(-) diff --git a/modules/aduptechBidAdapter.js b/modules/aduptechBidAdapter.js index d5b348ee29b..b70f7cf3ce6 100644 --- a/modules/aduptechBidAdapter.js +++ b/modules/aduptechBidAdapter.js @@ -1,20 +1,169 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js' +import { config } from '../src/config.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js' import * as utils from '../src/utils.js'; export const BIDDER_CODE = 'aduptech'; -export const PUBLISHER_PLACEHOLDER = '{PUBLISHER}'; -export const ENDPOINT_URL = 'https://rtb.d.adup-tech.com/prebid/' + PUBLISHER_PLACEHOLDER + '_bid'; +export const ENDPOINT_URL_PUBLISHER_PLACEHOLDER = '{PUBLISHER}'; +export const ENDPOINT_URL = 'https://rtb.d.adup-tech.com/prebid/' + ENDPOINT_URL_PUBLISHER_PLACEHOLDER + '_bid'; export const ENDPOINT_METHOD = 'POST'; +/** + * Internal utitlity functions + */ +export const internal = { + + /** + * Extracts the GDPR information from given bidderRequest + * + * @param {BidderRequest} bidderRequest + * @returns {null|Object.} + */ + extractGdpr: (bidderRequest) => { + if (bidderRequest && bidderRequest.gdprConsent) { + return { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true + }; + } + + return null; + }, + + /** + * Extracts the pageUrl from given bidderRequest.refererInfo or gobal "pageUrl" config or from (top) window location + * + * @param {BidderRequest} bidderRequest + * @returns {string} + */ + extractPageUrl: (bidderRequest) => { + if (bidderRequest && utils.deepAccess(bidderRequest, 'refererInfo.canonicalUrl')) { + return bidderRequest.refererInfo.canonicalUrl; + } + + if (config && config.getConfig('pageUrl')) { + return config.getConfig('pageUrl'); + } + + try { + return utils.getWindowTop().location.href; + } catch (e) { + return utils.getWindowSelf().location.href; + } + }, + + /** + * Extracts the referrer based on given bidderRequest.refererInfo or from (top) document referrer + * + * @param {BidderRequest} bidderRequest + * @returns {string} + */ + extractReferrer: (bidderRequest) => { + if (bidderRequest && utils.deepAccess(bidderRequest, 'refererInfo.referer')) { + return bidderRequest.refererInfo.referer; + } + + try { + return utils.getWindowTop().document.referrer; + } catch (e) { + return utils.getWindowSelf().document.referrer; + } + }, + + /** + * Extracts banner config from given bidRequest + * + * @param {BidRequest} bidRequest + * @returns {null|Object.} + */ + extractBannerConfig: (bidRequest) => { + const sizes = utils.getAdUnitSizes(bidRequest); + if (Array.isArray(sizes) && sizes.length > 0) { + return { sizes: sizes }; + } + + return null; + }, + + /** + * Extracts native config from given bidRequest + * + * @param {BidRequest} bidRequest + * @returns {null|Object.} + */ + extractNativeConfig: (bidRequest) => { + if (bidRequest && utils.deepAccess(bidRequest, 'mediaTypes.native')) { + return bidRequest.mediaTypes.native; + } + + return null; + }, + + /** + * Extracts the bidder params from given bidRequest + * + * @param {BidRequest} bidRequest + * @returns {null|Object.} + */ + extractParams: (bidRequest) => { + if (bidRequest && bidRequest.params) { + return bidRequest.params + } + + return null; + }, + + /** + * Group given array of bidRequests by params.publisher + * + * @param {BidRequest[]} bidRequests + * @returns {Object.} + */ + groupBidRequestsByPublisher: (bidRequests) => { + const groupedBidRequests = {}; + + if (!bidRequests || bidRequests.length === 0) { + return groupedBidRequests; + } + + bidRequests.forEach((bidRequest) => { + const publisher = internal.extractParams(bidRequest).publisher; + if (!publisher) { + return; + } + + if (!groupedBidRequests[publisher]) { + groupedBidRequests[publisher] = []; + } + + groupedBidRequests[publisher].push(bidRequest); + }); + + return groupedBidRequests; + }, + + /** + * Build ednpoint url based on given publisher code + * + * @param {string} publisher + * @returns {string} + */ + buildEndpointUrl: (publisher) => { + return ENDPOINT_URL.replace(ENDPOINT_URL_PUBLISHER_PLACEHOLDER, encodeURIComponent(publisher)); + }, +} + +/** + * The bid adapter definition + */ export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER], + supportedMediaTypes: [BANNER, NATIVE], /** * Validate given bid request * - * @param {*} bidRequest + * @param {BidRequest[]} bidRequest * @returns {boolean} */ isBidRequestValid: (bidRequest) => { @@ -22,12 +171,13 @@ export const spec = { return false; } - const sizes = extractSizesFromBidRequest(bidRequest); - if (!sizes || sizes.length === 0) { + // banner or native config has to be set + if (!internal.extractBannerConfig(bidRequest) && !internal.extractNativeConfig(bidRequest)) { return false; } - const params = extractParamsFromBidRequest(bidRequest); + // publisher and placement param has to be set + const params = internal.extractParams(bidRequest); if (!params || !params.publisher || !params.placement) { return false; } @@ -38,152 +188,124 @@ export const spec = { /** * Build real bid requests * - * @param {*} validBidRequests - * @param {*} bidderRequest - * @returns {*[]} + * @param {BidRequest[]} validBidRequests + * @param {BidderRequest} bidderRequest + * @returns {Object[]} */ buildRequests: (validBidRequests, bidderRequest) => { - const bidRequests = []; - const gdpr = extractGdprFromBidderRequest(bidderRequest); + const requests = []; + + // stop here on invalid or empty data + if (!bidderRequest || !validBidRequests || validBidRequests.length === 0) { + return requests; + } + + // collect required data + const auctionId = bidderRequest.auctionId; + const pageUrl = internal.extractPageUrl(bidderRequest); + const referrer = internal.extractReferrer(bidderRequest); + const gdpr = internal.extractGdpr(bidderRequest); - validBidRequests.forEach((bidRequest) => { - bidRequests.push({ - url: ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, encodeURIComponent(bidRequest.params.publisher)), + // group bid requests by publisher + const groupedBidRequests = internal.groupBidRequestsByPublisher(validBidRequests); + + // build requests + for (const publisher in groupedBidRequests) { + const request = { + url: internal.buildEndpointUrl(publisher), method: ENDPOINT_METHOD, data: { + auctionId: auctionId, + pageUrl: pageUrl, + referrer: referrer, + imp: [] + } + }; + + // add gdpr data + if (gdpr) { + request.data.gdpr = gdpr; + } + + // handle multiple bids per request + groupedBidRequests[publisher].forEach((bidRequest) => { + const bid = { bidId: bidRequest.bidId, - auctionId: bidRequest.auctionId, transactionId: bidRequest.transactionId, adUnitCode: bidRequest.adUnitCode, - pageUrl: extractTopWindowUrlFromBidRequest(bidRequest), - referrer: extractTopWindowReferrerFromBidRequest(bidRequest), - sizes: extractSizesFromBidRequest(bidRequest), - params: extractParamsFromBidRequest(bidRequest), - gdpr: gdpr + params: internal.extractParams(bidRequest) + }; + + // add banner config + const bannerConfig = internal.extractBannerConfig(bidRequest); + if (bannerConfig) { + bid.banner = bannerConfig; + } + + // add native config + const nativeConfig = internal.extractNativeConfig(bidRequest); + if (nativeConfig) { + bid.native = nativeConfig; } + + request.data.imp.push(bid); }); - }); - return bidRequests; + requests.push(request); + } + + return requests; }, /** * Handle bid response * - * @param {*} response - * @returns {*[]} + * @param {Object} response + * @returns {Object[]} */ interpretResponse: (response) => { const bidResponses = []; - if (!response.body || !response.body.bid || !response.body.creative) { + // stop here on invalid or empty data + if (!response || !utils.deepAccess(response, 'body.bids') || response.body.bids.length === 0) { return bidResponses; } - bidResponses.push({ - requestId: response.body.bid.bidId, - cpm: response.body.bid.price, - netRevenue: response.body.bid.net, - currency: response.body.bid.currency, - ttl: response.body.bid.ttl, - creativeId: response.body.creative.id, - width: response.body.creative.width, - height: response.body.creative.height, - ad: response.body.creative.html - }); - - return bidResponses; - } -}; - -/** - * Extracts the possible ad unit sizes from given bid request - * - * @param {*} bidRequest - * @returns {number[]} - */ -export function extractSizesFromBidRequest(bidRequest) { - // since pbjs 3.0 - if (bidRequest && utils.deepAccess(bidRequest, 'mediaTypes.banner.sizes')) { - return bidRequest.mediaTypes.banner.sizes; - - // for backward compatibility - } else if (bidRequest && bidRequest.sizes) { - return bidRequest.sizes; - - // fallback - } else { - return []; - } -} - -/** - * Extracts the custom params from given bid request - * - * @param {*} bidRequest - * @returns {*} - */ -export function extractParamsFromBidRequest(bidRequest) { - if (bidRequest && bidRequest.params) { - return bidRequest.params - } else { - return null; - } -} - -/** - * Extracts the GDPR information from given bidder request - * - * @param {*} bidderRequest - * @returns {*} - */ -export function extractGdprFromBidderRequest(bidderRequest) { - let gdpr = null; - - if (bidderRequest && bidderRequest.gdprConsent) { - gdpr = { - consentString: bidderRequest.gdprConsent.consentString, - consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') ? bidderRequest.gdprConsent.gdprApplies : true - }; - } + // parse multiple bids per response + response.body.bids.forEach((bid) => { + if (!bid || !bid.bid || !bid.creative) { + return; + } - return gdpr; -} + const bidResponse = { + requestId: bid.bid.bidId, + cpm: bid.bid.price, + netRevenue: bid.bid.net, + currency: bid.bid.currency, + ttl: bid.bid.ttl, + creativeId: bid.creative.id, + meta: { + advertiserDomains: bid.creative.advertiserDomains + } + } -/** - * Extracts the page url from given bid request or use the (top) window location as fallback - * - * @param {*} bidRequest - * @returns {string} - */ -export function extractTopWindowUrlFromBidRequest(bidRequest) { - if (bidRequest && utils.deepAccess(bidRequest, 'refererInfo.canonicalUrl')) { - return bidRequest.refererInfo.canonicalUrl; - } + if (bid.creative.html) { + bidResponse.mediaType = BANNER; + bidResponse.ad = bid.creative.html; + bidResponse.width = bid.creative.width; + bidResponse.height = bid.creative.height; + } - try { - return window.top.location.href; - } catch (e) { - return window.location.href; - } -} + if (bid.creative.native) { + bidResponse.mediaType = NATIVE; + bidResponse.native = bid.creative.native; + } -/** - * Extracts the referrer from given bid request or use the (top) document referrer as fallback - * - * @param {*} bidRequest - * @returns {string} - */ -export function extractTopWindowReferrerFromBidRequest(bidRequest) { - if (bidRequest && utils.deepAccess(bidRequest, 'refererInfo.referer')) { - return bidRequest.refererInfo.referer; - } + bidResponses.push(bidResponse); + }); - try { - return window.top.document.referrer; - } catch (e) { - return window.document.referrer; + return bidResponses; } -} +}; registerBidder(spec); diff --git a/modules/aduptechBidAdapter.md b/modules/aduptechBidAdapter.md index 281fe24cf9d..25034cecbe8 100644 --- a/modules/aduptechBidAdapter.md +++ b/modules/aduptechBidAdapter.md @@ -1,35 +1,86 @@ -# Overview -``` -Module Name: AdUp Technology Bid Adapter -Module Type: Bidder Adapter -Maintainers: - - steffen.anders@adup-tech.com - - sebastian.briesemeister@adup-tech.com - - marten.lietz@adup-tech.com -``` +# AdUp Technology Bid Adapter -# Description +## Description Connects to AdUp Technology demand sources to fetch bids. -Please use ```aduptech``` as bidder code. Only banner formats are supported. -The AdUp Technology bidding adapter requires setup and approval before beginning. -For more information visit [www.adup-tech.com](https://www.adup-tech.com/en) or contact [info@adup-tech.com](mailto:info@adup-tech.com). +**Note:** The bid adapter requires correct setup and approval, including an existing publisher account. For more information visit [www.adup-tech.com](https://www.adup-tech.com/en) or contact [info@adup-tech.com](mailto:info@adup-tech.com). + + +## Overview +- Module Name: AdUp Technology Bid Adapter +- Module Type: Bidder Adapter +- Maintainers: + - [steffen.anders@adup-tech.com](mailto:steffen.anders@adup-tech.com) + - [sebastian.briesemeister@adup-tech.com](mailto:sebastian.briesemeister@adup-tech.com) + - [marten.lietz@adup-tech.com](mailto:marten.lietz@adup-tech.com) +- Bidder code: `aduptech` +- Supported media types: `banner`, `native` + +## Paramters +| Name | Scope | Description | Example | +| :--- | :---- | :---------- | :------ | +| `publisher` | required | Unique publisher identifier | `'prebid'` | +| `placement` | required | Unique placement identifier per publisher | `'1234'` | +| `query` | optional | Semicolon separated list of keywords | `'urlaub;ibiza;mallorca'` | +| `adtest` | optional | Deactivates tracking of impressions and clicks. **Should only be used for testing purposes!** | `true` | + -# Test Parameters +## Examples + +### Banner ```js var adUnits = [ { - code: 'banner', + code: "example1", mediaTypes: { banner: { sizes: [[300, 250], [300, 600]], } }, bids: [{ - bidder: 'aduptech', + bidder: "aduptech", + params: { + publisher: "prebid", + placement: "12345" + } + }] + } +]; +``` + +### Native +```js +var adUnits = [ + { + code: "example2", + mediaTypes: { + native: { + image: { + required: true, + sizes: [150, 150] + }, + title: { + required: true + }, + body: { + required: true + }, + clickUrl: { + required: true + }, + displayUrl: { + required: true + }, + sponsoredBy: { + required: true + } + } + }, + bids: [{ + bidder: "aduptech", params: { - publisher: 'prebid', - placement: '12345' + publisher: "prebid", + placement: "12345" } }] } diff --git a/test/spec/modules/aduptechBidAdapter_spec.js b/test/spec/modules/aduptechBidAdapter_spec.js index 1e39e0cfc8b..362cd3e506a 100644 --- a/test/spec/modules/aduptechBidAdapter_spec.js +++ b/test/spec/modules/aduptechBidAdapter_spec.js @@ -1,180 +1,280 @@ import { expect } from 'chai'; import { BIDDER_CODE, - PUBLISHER_PLACEHOLDER, - ENDPOINT_URL, ENDPOINT_METHOD, - spec, - extractGdprFromBidderRequest, - extractParamsFromBidRequest, - extractSizesFromBidRequest, - extractTopWindowReferrerFromBidRequest, - extractTopWindowUrlFromBidRequest + internal, + spec } from '../../../modules/aduptechBidAdapter.js'; +import { config } from '../../../src/config.js'; +import * as utils from '../../../src/utils.js'; +import { BANNER, NATIVE } from '../../../src/mediaTypes.js' import { newBidder } from '../../../src/adapters/bidderFactory.js'; describe('AduptechBidAdapter', () => { - describe('extractGdprFromBidderRequest', () => { - it('should handle empty bidder request', () => { - const bidderRequest = null; - expect(extractGdprFromBidderRequest(bidderRequest)).to.be.null; - }); + describe('internal', () => { + describe('extractGdpr', () => { + it('should handle empty bidderRequest', () => { + expect(internal.extractGdpr(null)).to.be.null; + expect(internal.extractGdpr({})).to.be.null; + }); - it('should handle missing gdprConsent in bidder request', () => { - const bidderRequest = {}; - expect(extractGdprFromBidderRequest(bidderRequest)).to.be.null; - }); + it('should extract bidderRequest.gdprConsent', () => { + const bidderRequest = { + gdprConsent: { + consentString: 'consentString', + gdprApplies: false + } + }; - it('should handle gdprConsent in bidder request', () => { - const bidderRequest = { - gdprConsent: { - consentString: 'consentString', - gdprApplies: true - } - }; + expect(internal.extractGdpr(bidderRequest)).to.deep.equal({ + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: bidderRequest.gdprConsent.gdprApplies + }); + }); + + it('should handle missing bidderRequest.gdprConsent.gdprApplies', () => { + const bidderRequest = { + gdprConsent: { + consentString: 'consentString' + } + }; - expect(extractGdprFromBidderRequest(bidderRequest)).to.deep.equal({ - consentString: bidderRequest.gdprConsent.consentString, - consentRequired: true + expect(internal.extractGdpr(bidderRequest)).to.deep.equal({ + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: true + }); }); - }); - }); - describe('extractParamsFromBidRequest', () => { - it('should handle empty bid request', () => { - const bidRequest = null; - expect(extractParamsFromBidRequest(bidRequest)).to.be.null; - }); + it('should handle invalid bidderRequest.gdprConsent.gdprApplies', () => { + const bidderRequest = { + gdprConsent: { + consentString: 'consentString', + gdprApplies: 'foobar' + } + }; - it('should handle missing params in bid request', () => { - const bidRequest = {}; - expect(extractParamsFromBidRequest(bidRequest)).to.be.null; + expect(internal.extractGdpr(bidderRequest)).to.deep.equal({ + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: true + }); + }); }); - it('should handle params in bid request', () => { - const bidRequest = { - params: { - foo: '123', - bar: 456 - } - }; - expect(extractParamsFromBidRequest(bidRequest)).to.deep.equal(bidRequest.params); - }); - }); + describe('extractPageUrl', () => { + let origPageUrl; - describe('extractSizesFromBidRequest', () => { - it('should handle empty bid request', () => { - const bidRequest = null; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal([]); - }); + beforeEach(() => { + // remember original pageUrl in config + origPageUrl = config.getConfig('pageUrl'); - it('should handle missing sizes in bid request', () => { - const bidRequest = {}; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal([]); - }); + // unset pageUrl in config + config.setConfig({ pageUrl: null }); + }); + + afterEach(() => { + // set original pageUrl to config + config.setConfig({ pageUrl: origPageUrl }); + }); + + it('should handle empty or missing data', () => { + expect(internal.extractPageUrl(null)).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({})).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({ refererInfo: {} })).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({ refererInfo: { canonicalUrl: null } })).to.equal(utils.getWindowTop().location.href); + expect(internal.extractPageUrl({ refererInfo: { canonicalUrl: '' } })).to.equal(utils.getWindowTop().location.href); + }); + + it('should use "pageUrl" from config', () => { + config.setConfig({ pageUrl: 'http://page.url' }); - it('should handle sizes in bid request', () => { - const bidRequest = { - mediaTypes: { - banner: { - sizes: [[12, 34], [56, 78]] + expect(internal.extractPageUrl({})).to.equal(config.getConfig('pageUrl')); + }); + + it('should use bidderRequest.refererInfo.canonicalUrl', () => { + const bidderRequest = { + refererInfo: { + canonicalUrl: 'http://canonical.url' } - } - }; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal(bidRequest.mediaTypes.banner.sizes); - }); + }; - it('should handle sizes in bid request (backward compatibility)', () => { - const bidRequest = { - sizes: [[12, 34], [56, 78]] - }; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal(bidRequest.sizes); - }); + expect(internal.extractPageUrl(bidderRequest)).to.equal(bidderRequest.refererInfo.canonicalUrl); + }); - it('should prefer sizes in mediaTypes.banner', () => { - const bidRequest = { - sizes: [[12, 34]], - mediaTypes: { - banner: { - sizes: [[56, 78]] + it('should prefer bidderRequest.refererInfo.canonicalUrl over "pageUrl" from config', () => { + const bidderRequest = { + refererInfo: { + canonicalUrl: 'http://canonical.url' } - } - }; - expect(extractSizesFromBidRequest(bidRequest)).to.deep.equal(bidRequest.mediaTypes.banner.sizes); - }); - }); + }; - describe('extractTopWindowReferrerFromBidRequest', () => { - it('should use fallback if bid request is empty', () => { - const bidRequest = null; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); - }); + config.setConfig({ pageUrl: 'http://page.url' }); - it('should use fallback if refererInfo in bid request is missing', () => { - const bidRequest = {}; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); + expect(internal.extractPageUrl(bidderRequest)).to.equal(bidderRequest.refererInfo.canonicalUrl); + }); }); - it('should use fallback if refererInfo.referer in bid request is missing', () => { - const bidRequest = { - refererInfo: {} - }; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); - }); + describe('extractReferrer', () => { + it('should handle empty or missing data', () => { + expect(internal.extractReferrer(null)).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({})).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({ refererInfo: {} })).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({ refererInfo: { referer: null } })).to.equal(utils.getWindowTop().document.referrer); + expect(internal.extractReferrer({ refererInfo: { referer: '' } })).to.equal(utils.getWindowTop().document.referrer); + }); - it('should use fallback if refererInfo.referer in bid request is empty', () => { - const bidRequest = { - refererInfo: { - referer: '' - } - }; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(window.top.document.referrer); - }); + it('hould use bidderRequest.refererInfo.referer', () => { + const bidderRequest = { + refererInfo: { + referer: 'foobar' + } + }; - it('should use refererInfo.referer from bid request ', () => { - const bidRequest = { - refererInfo: { - referer: 'foobar' - } - }; - expect(extractTopWindowReferrerFromBidRequest(bidRequest)).to.equal(bidRequest.refererInfo.referer); + expect(internal.extractReferrer(bidderRequest)).to.equal(bidderRequest.refererInfo.referer); + }); }); - }); - describe('extractTopWindowUrlFromBidRequest', () => { - it('should use fallback if bid request is empty', () => { - const bidRequest = null; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('extractParams', () => { + it('should handle empty bidRequest', () => { + expect(internal.extractParams(null)).to.be.null; + expect(internal.extractParams({})).to.be.null; + }); + + it('should extract bidRequest.params', () => { + const bidRequest = { + params: { + foo: '123', + bar: 456 + } + }; + expect(internal.extractParams(bidRequest)).to.deep.equal(bidRequest.params); + }); }); - it('should use fallback if refererInfo in bid request is missing', () => { - const bidRequest = {}; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('extractBannerConfig', () => { + it('should handle empty bidRequest', () => { + expect(internal.extractBannerConfig(null)).to.be.null; + expect(internal.extractBannerConfig({})).to.be.null; + }); + + it('should extract bidRequest.mediaTypes.banner', () => { + const bidRequest = { + mediaTypes: { + banner: { + sizes: [[12, 34], [56, 78]] + } + } + }; + expect(internal.extractBannerConfig(bidRequest)).to.deep.equal(bidRequest.mediaTypes.banner); + }); + + it('should extract bidRequest.sizes (backward compatibility)', () => { + const bidRequest = { + sizes: [[12, 34], [56, 78]] + }; + + expect(internal.extractBannerConfig(bidRequest)).to.deep.equal({sizes: bidRequest.sizes}); + }); }); - it('should use fallback if refererInfo.canonicalUrl in bid request is missing', () => { - const bidRequest = { - refererInfo: {} - }; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('extractNativeConfig', () => { + it('should handle empty bidRequest', () => { + expect(internal.extractNativeConfig(null)).to.be.null; + expect(internal.extractNativeConfig({})).to.be.null; + }); + + it('should extract bidRequest.mediaTypes.native', () => { + const bidRequest = { + mediaTypes: { + native: { + image: { + required: true + }, + title: { + required: true + } + } + } + }; + + expect(internal.extractNativeConfig(bidRequest)).to.deep.equal(bidRequest.mediaTypes.native); + }); }); - it('should use fallback if refererInfo.canonicalUrl in bid request is empty', () => { - const bidRequest = { - refererInfo: { - canonicalUrl: '' - } - }; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(window.top.location.href); + describe('groupBidRequestsByPublisher', () => { + it('should handle empty bidRequests', () => { + expect(internal.groupBidRequestsByPublisher(null)).to.deep.equal({}); + expect(internal.groupBidRequestsByPublisher([])).to.deep.equal({}) + }); + + it('should group given bidRequests by params.publisher', () => { + const bidRequests = [ + { + mediaTypes: { + banner: { + sizes: [[100, 100]] + } + }, + params: { + publisher: 'publisher1', + placement: '1111' + } + }, + { + mediaTypes: { + banner: { + sizes: [[200, 200]] + } + }, + params: { + publisher: 'publisher2', + placement: '2222' + } + }, + { + mediaTypes: { + banner: { + sizes: [[300, 300]] + } + }, + params: { + publisher: 'publisher3', + placement: '3333' + } + }, + { + mediaTypes: { + banner: { + sizes: [[400, 400]] + } + }, + params: { + publisher: 'publisher1', + placement: '4444' + } + } + ]; + + expect(internal.groupBidRequestsByPublisher(bidRequests)).to.deep.equal({ + publisher1: [ + bidRequests[0], + bidRequests[3] + ], + publisher2: [ + bidRequests[1], + ], + publisher3: [ + bidRequests[2], + ], + }); + }); }); - it('should use refererInfo.canonicalUrl from bid request ', () => { - const bidRequest = { - refererInfo: { - canonicalUrl: 'foobar' - } - }; - expect(extractTopWindowUrlFromBidRequest(bidRequest)).to.equal(bidRequest.refererInfo.canonicalUrl); + describe('buildEndpointUrl', () => { + it('should build endpoint url based on given publisher code', () => { + expect(internal.buildEndpointUrl(1234)).to.be.equal('https://rtb.d.adup-tech.com/prebid/1234_bid'); + expect(internal.buildEndpointUrl('foobar')).to.be.equal('https://rtb.d.adup-tech.com/prebid/foobar_bid'); + expect(internal.buildEndpointUrl('foo bar')).to.be.equal('https://rtb.d.adup-tech.com/prebid/foo%20bar_bid'); + }); }); }); @@ -185,42 +285,31 @@ describe('AduptechBidAdapter', () => { adapter = newBidder(spec); }); - describe('inherited functions', () => { - it('exists and is a function', () => { - expect(adapter.callBids).to.exist.and.to.be.a('function'); + describe('code', () => { + it('should be correct', () => { + expect(adapter.getSpec().code).to.equal(BIDDER_CODE); }); }); - describe('isBidRequestValid', () => { - it('should return true when necessary information is given', () => { - expect(spec.isBidRequestValid({ - mediaTypes: { - banner: { - sizes: [[100, 200]] - } - }, - params: { - publisher: 'test', - placement: '1234' - } - })).to.be.true; + describe('supportedMediaTypes', () => { + it('should be correct', () => { + expect(adapter.getSpec().supportedMediaTypes).to.deep.equal([BANNER, NATIVE]); }); + }); - it('should return true when necessary information is given (backward compatibility)', () => { - expect(spec.isBidRequestValid({ - sizes: [[100, 200]], - params: { - publisher: 'test', - placement: '1234' - } - })).to.be.true; + describe('inherited functions', () => { + it('should exist and be a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); }); + }); - it('should return false on empty bid', () => { + describe('isBidRequestValid', () => { + it('should be false on empty bid', () => { + expect(spec.isBidRequestValid(null)).to.be.false; expect(spec.isBidRequestValid({})).to.be.false; }); - it('should return false on missing sizes', () => { + it('should be false if mediaTypes.banner and mediaTypes.native is missing', () => { expect(spec.isBidRequestValid({ params: { publisher: 'test', @@ -229,63 +318,65 @@ describe('AduptechBidAdapter', () => { })).to.be.false; }); - it('should return false on empty sizes', () => { + it('should be false if params missing', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { - sizes: [] + sizes: [[100, 200]] } }, - params: { - publisher: 'test', - placement: '1234' - } })).to.be.false; }); - it('should return false on empty sizes (backward compatibility)', () => { + it('should be false if params is invalid', () => { expect(spec.isBidRequestValid({ - sizes: [], - params: { - publisher: 'test', - placement: '1234' - } + mediaTypes: { + banner: { + sizes: [[100, 200]] + } + }, + params: 'bar' })).to.be.false; }); - it('should return false on missing params', () => { + it('should be false if params is empty', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { sizes: [[100, 200]] } }, + params: {} })).to.be.false; }); - it('should return false on invalid params', () => { + it('should be false if params.publisher is missing', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { sizes: [[100, 200]] } }, - params: 'bar' + params: { + placement: '1234' + } })).to.be.false; }); - it('should return false on empty params', () => { + it('should be false if params.placement is missing', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { sizes: [[100, 200]] } }, - params: {} + params: { + publisher: 'test' + } })).to.be.false; }); - it('should return false on missing publisher', () => { + it('should be true if mediaTypes.banner is given', () => { expect(spec.isBidRequestValid({ mediaTypes: { banner: { @@ -293,34 +384,62 @@ describe('AduptechBidAdapter', () => { } }, params: { + publisher: 'test', placement: '1234' } - })).to.be.false; + })).to.be.true; }); - it('should return false on missing placement', () => { + it('should be true if mediaTypes.native is given', () => { expect(spec.isBidRequestValid({ mediaTypes: { - banner: { - sizes: [[100, 200]] + native: { + image: { + required: true + }, + title: { + required: true + }, + clickUrl: { + required: true + }, + body: { + required: true + } } }, params: { - publisher: 'test' + publisher: 'test', + placement: '1234' } - })).to.be.false; + })).to.be.true; }); }); describe('buildRequests', () => { - it('should send one bid request per ad unit to the endpoint via POST', () => { - const bidRequests = [ + it('should handle empty validBidRequests', () => { + expect(spec.buildRequests(null)).to.deep.equal([]); + expect(spec.buildRequests([])).to.deep.equal([]); + }); + + it('should build one request per publisher', () => { + const bidderRequest = { + auctionId: 'auctionId123', + refererInfo: { + canonicalUrl: 'http://crazy.canonical.url', + referer: 'http://crazy.referer.url' + }, + gdprConsent: { + consentString: 'consentString123', + gdprApplies: true + } + }; + + const validBidRequests = [ { - bidder: BIDDER_CODE, bidId: 'bidId1', adUnitCode: 'adUnitCode1', transactionId: 'transactionId1', - auctionId: 'auctionId1', mediaTypes: { banner: { sizes: [[100, 200], [300, 400]] @@ -332,170 +451,197 @@ describe('AduptechBidAdapter', () => { } }, { - bidder: BIDDER_CODE, bidId: 'bidId2', adUnitCode: 'adUnitCode2', transactionId: 'transactionId2', - auctionId: 'auctionId2', mediaTypes: { banner: { - sizes: [[500, 600]] + sizes: [[100, 200]] } }, params: { - publisher: 'publisher2', + publisher: 'publisher1', placement: 'placement2' } - } - ]; - - const result = spec.buildRequests(bidRequests); - expect(result.length).to.equal(2); - - expect(result[0].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, bidRequests[0].params.publisher)); - expect(result[0].method).to.equal(ENDPOINT_METHOD); - expect(result[0].data).to.deep.equal({ - bidId: bidRequests[0].bidId, - auctionId: bidRequests[0].auctionId, - transactionId: bidRequests[0].transactionId, - adUnitCode: bidRequests[0].adUnitCode, - pageUrl: extractTopWindowUrlFromBidRequest(bidRequests[0]), - referrer: extractTopWindowReferrerFromBidRequest(bidRequests[0]), - sizes: extractSizesFromBidRequest(bidRequests[0]), - params: extractParamsFromBidRequest(bidRequests[0]), - gdpr: null - }); - - expect(result[1].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, bidRequests[1].params.publisher)); - expect(result[1].method).to.equal(ENDPOINT_METHOD); - expect(result[1].data).to.deep.equal({ - bidId: bidRequests[1].bidId, - auctionId: bidRequests[1].auctionId, - transactionId: bidRequests[1].transactionId, - adUnitCode: bidRequests[1].adUnitCode, - pageUrl: extractTopWindowUrlFromBidRequest(bidRequests[1]), - referrer: extractTopWindowReferrerFromBidRequest(bidRequests[1]), - sizes: extractSizesFromBidRequest(bidRequests[1]), - params: extractParamsFromBidRequest(bidRequests[1]), - gdpr: null - }); - }); - - it('should pass gdpr informations', () => { - const bidderRequest = { - gdprConsent: { - consentString: 'consentString', - gdprApplies: true - } - }; - - const bidRequests = [ + }, { - bidder: BIDDER_CODE, bidId: 'bidId3', adUnitCode: 'adUnitCode3', transactionId: 'transactionId3', - auctionId: 'auctionId3', mediaTypes: { - banner: { - sizes: [[100, 200], [300, 400]] + native: { + image: { + required: true + }, + title: { + required: true + }, + clickUrl: { + required: true + }, + body: { + required: true + } } }, params: { - publisher: 'publisher3', + publisher: 'publisher2', placement: 'placement3' } } ]; - const result = spec.buildRequests(bidRequests, bidderRequest); - expect(result.length).to.equal(1); - expect(result[0].data.gdpr).to.deep.equal(extractGdprFromBidderRequest(bidderRequest)); - }); - - it('should encode publisher param in endpoint url', () => { - const bidRequests = [ + expect(spec.buildRequests(validBidRequests, bidderRequest)).to.deep.equal([ { - bidder: BIDDER_CODE, - bidId: 'bidId1', - adUnitCode: 'adUnitCode1', - transactionId: 'transactionId1', - auctionId: 'auctionId1', - mediaTypes: { - banner: { - sizes: [[100, 200]] - } - }, - params: { - publisher: 'crazy publisher key äÖÜ', - placement: 'placement1' + url: internal.buildEndpointUrl(validBidRequests[0].params.publisher), + method: ENDPOINT_METHOD, + data: { + auctionId: bidderRequest.auctionId, + pageUrl: bidderRequest.refererInfo.canonicalUrl, + referrer: bidderRequest.refererInfo.referer, + gdpr: { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: bidderRequest.gdprConsent.gdprApplies + }, + imp: [ + { + bidId: validBidRequests[0].bidId, + transactionId: validBidRequests[0].transactionId, + adUnitCode: validBidRequests[0].adUnitCode, + params: validBidRequests[0].params, + banner: validBidRequests[0].mediaTypes.banner + }, + { + bidId: validBidRequests[1].bidId, + transactionId: validBidRequests[1].transactionId, + adUnitCode: validBidRequests[1].adUnitCode, + params: validBidRequests[1].params, + banner: validBidRequests[1].mediaTypes.banner + } + ] } }, - ]; - - const result = spec.buildRequests(bidRequests); - expect(result[0].url).to.equal(ENDPOINT_URL.replace(PUBLISHER_PLACEHOLDER, encodeURIComponent(bidRequests[0].params.publisher))); - }); - - it('should handle empty bidRequests', () => { - expect(spec.buildRequests([])).to.deep.equal([]); - }); - }); - - describe('interpretResponse', () => { - it('should correctly interpret the server response', () => { - const serverResponse = { - body: { - bid: { - bidId: 'bidId1', - price: 0.12, - net: true, - currency: 'EUR', - ttl: 123 - }, - creative: { - id: 'creativeId1', - width: 100, - height: 200, - html: '
Hello World
' - } - } - }; - - const result = spec.interpretResponse(serverResponse); - expect(result).to.deep.equal([ { - requestId: serverResponse.body.bid.bidId, - cpm: serverResponse.body.bid.price, - netRevenue: serverResponse.body.bid.net, - currency: serverResponse.body.bid.currency, - ttl: serverResponse.body.bid.ttl, - creativeId: serverResponse.body.creative.id, - width: serverResponse.body.creative.width, - height: serverResponse.body.creative.height, - ad: serverResponse.body.creative.html + url: internal.buildEndpointUrl(validBidRequests[2].params.publisher), + method: ENDPOINT_METHOD, + data: { + auctionId: bidderRequest.auctionId, + pageUrl: bidderRequest.refererInfo.canonicalUrl, + referrer: bidderRequest.refererInfo.referer, + gdpr: { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: bidderRequest.gdprConsent.gdprApplies + }, + imp: [ + { + bidId: validBidRequests[2].bidId, + transactionId: validBidRequests[2].transactionId, + adUnitCode: validBidRequests[2].adUnitCode, + params: validBidRequests[2].params, + native: validBidRequests[2].mediaTypes.native + } + ] + } } ]); }); + }); + describe('interpretResponse', () => { it('should handle empty serverResponse', () => { + expect(spec.interpretResponse(null)).to.deep.equal([]); expect(spec.interpretResponse({})).to.deep.equal([]); + expect(spec.interpretResponse({ body: {} })).to.deep.equal([]); + expect(spec.interpretResponse({ body: { bids: [] } })).to.deep.equal([]); }); - it('should handle missing bid', () => { - expect(spec.interpretResponse({ + it('should correctly interpret the server response', () => { + const serverResponse = { body: { - creative: {} + bids: [ + { + bid: { + bidId: 'bidId1', + price: 0.12, + net: true, + currency: 'EUR', + ttl: 123 + }, + creative: { + id: 'creativeId1', + advertiserDomains: ['advertiser1.com', 'advertiser2.org'], + width: 100, + height: 200, + html: '
Hello World
' + } + }, + { + bid: { + bidId: 'bidId2', + price: 0.99, + net: false, + currency: 'USD', + ttl: 465 + }, + creative: { + id: 'creativeId2', + advertiserDomains: ['advertiser3.com'], + native: { + title: 'Ad title', + body: 'Ad description', + displayUrl: 'Ad display url', + clickUrl: 'http://click.url/ad.html', + image: { + url: 'https://image.url/ad.png', + width: 123, + height: 456 + }, + sponsoredBy: 'Ad sponsored by', + impressionTrackers: [ + 'https://impression.tracking.url/1', + 'https://impression.tracking.url/2', + ], + privacyLink: 'https://example.com/privacy', + privacyIcon: 'https://example.com/icon.png' + } + } + }, + null, // should be skipped + {} // should be skipped + ] } - })).to.deep.equal([]); - }); + }; - it('should handle missing creative', () => { - expect(spec.interpretResponse({ - body: { - bid: {} + expect(spec.interpretResponse(serverResponse)).to.deep.equal([ + { + requestId: serverResponse.body.bids[0].bid.bidId, + cpm: serverResponse.body.bids[0].bid.price, + netRevenue: serverResponse.body.bids[0].bid.net, + currency: serverResponse.body.bids[0].bid.currency, + ttl: serverResponse.body.bids[0].bid.ttl, + creativeId: serverResponse.body.bids[0].creative.id, + meta: { + advertiserDomains: serverResponse.body.bids[0].creative.advertiserDomains + }, + mediaType: BANNER, + width: serverResponse.body.bids[0].creative.width, + height: serverResponse.body.bids[0].creative.height, + ad: serverResponse.body.bids[0].creative.html + }, + { + requestId: serverResponse.body.bids[1].bid.bidId, + cpm: serverResponse.body.bids[1].bid.price, + netRevenue: serverResponse.body.bids[1].bid.net, + currency: serverResponse.body.bids[1].bid.currency, + ttl: serverResponse.body.bids[1].bid.ttl, + creativeId: serverResponse.body.bids[1].creative.id, + meta: { + advertiserDomains: serverResponse.body.bids[1].creative.advertiserDomains + }, + mediaType: NATIVE, + native: serverResponse.body.bids[1].creative.native } - })).to.deep.equal([]); + ]); }); }); }); From c04b0248e90642588c6e98ba86190aa916f2c101 Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Fri, 28 May 2021 17:35:50 +0700 Subject: [PATCH 18/63] QwarryBidAdapter: added schain (#6864) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter * add referer JS detection * use bidderRequest.refererInfo * fix tests * GDPR consent string support * NPE fix * gdpr value added * merge master * gdpr value added * qwarry bid adapter: add tests * Qwarry bid adapter: remove gdpr field from request * qwarry bid adapter: add sizes * qwarry bid adapter: add sizes * added schain * added test for schain Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev Co-authored-by: Ekaterina Legostaeva --- modules/qwarryBidAdapter.js | 3 ++- test/spec/modules/qwarryBidAdapter_spec.js | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index 7ed5e5c984c..a410c2fe163 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -28,7 +28,8 @@ export const spec = { let payload = { requestId: bidderRequest.bidderRequestId, bids, - referer: bidderRequest.refererInfo.referer + referer: bidderRequest.refererInfo.referer, + schain: validBidRequests[0].schain } if (bidderRequest && bidderRequest.gdprConsent) { diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index 06d4af0756c..6dbb983ea23 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -9,6 +9,15 @@ const REQUEST = { 'params': { zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7 + }, + 'schain': { + ver: '1.0', + complete: 1, + nodes: [{ + asi: 'qwarry.com', + sid: '00001', + hp: 1 + }] } } @@ -86,6 +95,7 @@ describe('qwarryBidAdapter', function () { expect(bidderRequest.method).to.equal('POST') expect(bidderRequest.data.requestId).to.equal('123') expect(bidderRequest.data.referer).to.equal('http://test.com/path.html') + expect(bidderRequest.data.schain).to.deep.contains({ver: '1.0', complete: 1, nodes: [{asi: 'qwarry.com', sid: '00001', hp: 1}]}) expect(bidderRequest.data.bids).to.deep.contains({ bidId: '456', zoneToken: 'e64782a4-8e68-4c38-965b-80ccf115d46f', pos: 7, sizes: [{ width: 100, height: 200 }, { width: 300, height: 400 }] }) expect(bidderRequest.data.gdprConsent).to.deep.contains({ consentRequired: true, consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' }) expect(bidderRequest.options.customHeaders).to.deep.equal({ 'Rtb-Direct': true }) From 9c484c029a9b899f95d7adddcd0fb5adb494c77c Mon Sep 17 00:00:00 2001 From: Anand Venkatraman Date: Fri, 28 May 2021 08:41:35 -0400 Subject: [PATCH 19/63] PulsePoint Adapter: Fixing issues related to Prebid 5.0 (#6857) --- modules/pulsepointBidAdapter.js | 26 +++++++- modules/pulsepointBidAdapter.md | 35 +++++----- .../spec/modules/pulsepointBidAdapter_spec.js | 64 ++++++++++++++++++- 3 files changed, 100 insertions(+), 25 deletions(-) diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index f74d79a3dc5..adea33fc3b9 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -119,7 +119,8 @@ function bidResponseAvailable(request, response) { adId: id, ttl: idToBidMap[id].exp || DEFAULT_BID_TTL, netRevenue: DEFAULT_NET_REVENUE, - currency: bidResponse.cur || DEFAULT_CURRENCY + currency: bidResponse.cur || DEFAULT_CURRENCY, + meta: { advertiserDomains: idToBidMap[id].adomain || [] } }; if (idToImpMap[id].video) { // for outstream, a renderer is specified @@ -154,7 +155,7 @@ function impression(slot) { 'native': nativeImpression(slot), tagid: slot.params.ct.toString(), video: video(slot), - bidfloor: slot.params.bidfloor, + bidfloor: bidFloor(slot), ext: ext(slot), }; } @@ -192,7 +193,11 @@ function parseSizes(slot) { */ function video(slot) { if (slot.params.video) { - return Object.assign({}, slot.params.video, {battr: slot.params.battr}); + return Object.assign({}, + slot.params.video, // previously supported as bidder param + slot.mediaTypes && slot.mediaTypes.video ? slot.mediaTypes.video : {}, // params on mediaTypes.video + {battr: slot.params.battr} + ); } return null; } @@ -519,4 +524,19 @@ function nativeResponse(imp, bid) { return null; } +function bidFloor(slot) { + let floor = slot.params.bidfloor; + if (utils.isFn(slot.getFloor)) { + const floorData = slot.getFloor({ + mediaType: slot.mediaTypes.banner ? 'banner' : slot.mediaTypes.video ? 'video' : 'Native', + size: '*', + currency: DEFAULT_CURRENCY, + }); + if (floorData && floorData.floor) { + floor = floorData.floor; + } + } + return floor; +} + registerBidder(spec); diff --git a/modules/pulsepointBidAdapter.md b/modules/pulsepointBidAdapter.md index 36d4ef6cce5..7f4b7e6b611 100644 --- a/modules/pulsepointBidAdapter.md +++ b/modules/pulsepointBidAdapter.md @@ -45,23 +45,21 @@ Please use ```pulsepoint``` as the bidder code. mediaTypes: { video: { playerSize: [640, 480], - context: 'outstream' + context: 'outstream', + h: 300, + w: 400, + minduration: 1, + maxduration: 210, + linearity: 1, + mimes: ["video/mp4", "video/ogg", "video/webm"], + pos: 3 } }, bids: [{ bidder: 'pulsepoint', params: { cp: 512379, - ct: 505642, - video: { - h: 300, - w: 400, - minduration: 1, - maxduration: 210, - linearity: 1, - mimes: ["video/mp4", "video/ogg", "video/webm"], - pos: 3 - } + ct: 505642 } }], renderer: { @@ -74,7 +72,12 @@ Please use ```pulsepoint``` as the bidder code. mediaTypes: { video: { playerSize: [640, 480], - context: 'instream' + context: 'instream', + h: 300, + w: 400, + minduration: 1, + maxduration: 210, + protocols: [2,3,5] } }, bids: [{ @@ -82,14 +85,6 @@ Please use ```pulsepoint``` as the bidder code. params: { cp: 512379, ct: 694973, - video: { - battr: [1,3], - h: 300, - w: 400, - minduration: 1, - maxduration: 210, - protocols: [2,3,5] - } } }] }]; diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index c3830d5cb46..6630cb0907c 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -254,7 +254,7 @@ describe('PulsePoint Adapter Tests', function () { expect(bid.ttl).to.equal(20); }); - it('Verify ttl/currency applied to bid', function () { + it('Verify ttl/currency/adomain applied to bid', function () { const request = spec.buildRequests(slotConfigs, bidderRequest); const ortbRequest = request.data; const ortbResponse = { @@ -264,7 +264,8 @@ describe('PulsePoint Adapter Tests', function () { price: 1.25, adm: 'This is an Ad#1', crid: 'Creative#123', - exp: 50 + exp: 50, + adomain: ['advertiser.com'] }, { impid: ortbRequest.imp[1].id, price: 1.25, @@ -282,11 +283,15 @@ describe('PulsePoint Adapter Tests', function () { expect(bid.ad).to.equal('This is an Ad#1'); expect(bid.ttl).to.equal(50); expect(bid.currency).to.equal('GBP'); + expect(bid.meta).to.not.be.null; + expect(bid.meta.advertiserDomains).to.eql(['advertiser.com']); const secondBid = bids[1]; expect(secondBid.cpm).to.equal(1.25); expect(secondBid.ad).to.equal('This is an Ad#2'); expect(secondBid.ttl).to.equal(20); expect(secondBid.currency).to.equal('GBP'); + expect(secondBid.meta).to.not.be.null; + expect(secondBid.meta.advertiserDomains).to.eql([]); }); it('Verify full passback', function () { @@ -778,4 +783,59 @@ describe('PulsePoint Adapter Tests', function () { const secondBid = bids[1]; expect(secondBid.vastXml).to.equal(''); }); + it('Verify bid floor', function () { + const bidRequests = deepClone(slotConfigs); + bidRequests[0].params.bidfloor = 1.05; + let request = spec.buildRequests(bidRequests, bidderRequest); + let ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.05); + expect(ortbRequest.imp[1].bidfloor).to.be.undefined; + let floorArg = null; + // publisher uses the floor module + bidRequests[0].getFloor = (arg) => { + floorArg = arg; + return { floor: 1.25 }; + }; + bidRequests[1].getFloor = () => { + return { floor: 2.05 }; + }; + request = spec.buildRequests(bidRequests, bidderRequest); + ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp[0].bidfloor).to.equal(1.25); + expect(ortbRequest.imp[1].bidfloor).to.equal(2.05); + expect(floorArg).to.not.be.null; + expect(floorArg.mediaType).to.equal('banner'); + expect(floorArg.currency).to.equal('USD'); + expect(floorArg.size).to.equal('*'); + }); + it('Verify Video params on mediaTypes.video', function () { + const bidRequests = deepClone(videoSlotConfig); + bidRequests[0].mediaTypes = { + video: { + w: 600, + h: 400, + minduration: 15, + maxduration: 20, + startdelay: 10, + skip: 0, + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + const ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.be.null; + expect(ortbRequest.imp[0].native).to.be.null; + expect(ortbRequest.imp[0].banner).to.be.null; + expect(ortbRequest.imp[0].video.w).to.equal(600); + expect(ortbRequest.imp[0].video.h).to.equal(400); + expect(ortbRequest.imp[0].video.minduration).to.equal(15); + expect(ortbRequest.imp[0].video.maxduration).to.equal(20); + expect(ortbRequest.imp[0].video.startdelay).to.equal(10); + expect(ortbRequest.imp[0].video.skip).to.equal(0); + expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); + expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); + }); }); From 5b855a614842f6bb52ef676aaf6e326a1cd0a41d Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Fri, 28 May 2021 06:14:08 -0700 Subject: [PATCH 20/63] Bugfix: Parrable Cookie Length in Testing (#6869) --- test/spec/modules/parrableIdSystem_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index c18574aec7b..256be7340b7 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -12,7 +12,7 @@ import { server } from 'test/mocks/xhr.js'; const storage = newStorageManager(); const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; -const EXPIRE_COOKIE_TIME = 20000; +const EXPIRE_COOKIE_TIME = 864000000; const P_COOKIE_NAME = '_parrable_id'; const P_COOKIE_EID = '01.1563917337.test-eid'; const P_XHR_EID = '01.1588030911.test-new-eid' From 7ce7b09d43625499675b8511982e99fab9164502 Mon Sep 17 00:00:00 2001 From: onetag-dev <38786435+onetag-dev@users.noreply.github.com> Date: Fri, 28 May 2021 17:03:11 +0200 Subject: [PATCH 21/63] Add meta.advertiserDomains support (#6870) Co-authored-by: francesco --- modules/onetagBidAdapter.js | 3 ++- test/spec/modules/onetagBidAdapter_spec.js | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index 16b8096646f..0974f9db4cf 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -101,7 +101,8 @@ function interpretResponse(serverResponse, bidderRequest) { netRevenue: bid.netRevenue || false, mediaType: bid.mediaType, meta: { - mediaType: bid.mediaType + mediaType: bid.mediaType, + advertiserDomains: bid.adomain }, ttl: bid.ttl || 300 }; diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index c1462c3814d..5a1f30a0de8 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -240,6 +240,7 @@ describe('onetag', function () { expect(dataItem.creativeId).to.be.a('string'); expect(dataItem.netRevenue).to.be.a('boolean'); expect(dataItem.currency).to.be.a('string'); + expect(dataItem.meta.advertiserDomains).to.be.an('array'); } }); it('Returns an empty array if response is not valid', function () { @@ -323,6 +324,7 @@ function getBannerVideoResponse() { currency: 'USD', requestId: 'banner', mediaType: BANNER, + adomain: [] }, { cpm: 13, @@ -334,7 +336,8 @@ function getBannerVideoResponse() { requestId: 'videoInstream', vastUrl: 'https://videoinstream.org', videoCacheKey: 'key', - mediaType: VIDEO + mediaType: VIDEO, + adomain: ['test_domain'] }, { cpm: 13, @@ -347,7 +350,8 @@ function getBannerVideoResponse() { requestId: 'videoOutstream', ad: '', rendererUrl: 'https://testRenderer', - mediaType: VIDEO + mediaType: VIDEO, + adomain: [] } ] } From c73e59f20360c7ccc17835ea2be23c23026008ec Mon Sep 17 00:00:00 2001 From: Kajan Umakanthan Date: Fri, 28 May 2021 08:04:00 -0700 Subject: [PATCH 22/63] adding GAM ad unit code to impression ext object (#6861) Co-authored-by: punkiller --- modules/ixBidAdapter.js | 9 +++++++++ test/spec/modules/ixBidAdapter_spec.js | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 6970eefeac2..4837c9c061f 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -112,6 +112,11 @@ function bidToVideoImp(bid) { return imp; } +/** + * Converts an incoming PBJS bid to an IX Impression + * @param {object} bid PBJS bid object + * @returns {object} IX impression object + */ function bidToImp(bid) { const imp = {}; @@ -127,6 +132,10 @@ function bidToImp(bid) { imp.ext.sid = `${bid.params.size[0]}x${bid.params.size[1]}`; } + const dfpAdUnitCode = utils.deepAccess(bid, 'ortb2Imp.ext.data.adserver.adslot'); + if (dfpAdUnitCode) { + imp.ext.dfp_ad_unit_code = dfpAdUnitCode; + } return imp; } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index c026dfd3efc..48c8e48e6af 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -1251,6 +1251,32 @@ describe('IndexexchangeAdapter', function () { expect(query.nf).not.to.exist; }); + it('should send dfp_adunit_code in request if ortb2Imp.ext.data.adserver.adslot exists', function () { + const AD_UNIT_CODE = '/19968336/some-adunit-path'; + const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); + validBids[0].ortb2Imp = { + ext: { + data: { + adserver: { + name: 'gam', + adslot: AD_UNIT_CODE + } + } + } + }; + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const { dfp_ad_unit_code } = JSON.parse(requests[0].data.r).imp[0].ext; + expect(dfp_ad_unit_code).to.equal(AD_UNIT_CODE); + }); + + it('should not send dfp_adunit_code in request if ortb2Imp.ext.data.adserver.adslot does not exists', function () { + const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const { dfp_ad_unit_code } = JSON.parse(requests[0].data.r).imp[0].ext; + + expect(dfp_ad_unit_code).to.not.exist; + }); + it('payload should have correct format and value', function () { const payload = JSON.parse(query.r); expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId); From 6f81c1617aaa44ec68670887d8bd0fb032f720b1 Mon Sep 17 00:00:00 2001 From: asurovenko-zeta <80847074+asurovenko-zeta@users.noreply.github.com> Date: Fri, 28 May 2021 21:59:12 +0600 Subject: [PATCH 23/63] Zeta Ssp Bid Adapter: support for eids (#6819) --- modules/zetaSspBidAdapter.js | 9 +++++- test/spec/modules/zetaSspBidAdapter_spec.js | 34 +++++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/modules/zetaSspBidAdapter.js b/modules/zetaSspBidAdapter.js index 258478b0886..c956df55a1f 100644 --- a/modules/zetaSspBidAdapter.js +++ b/modules/zetaSspBidAdapter.js @@ -43,7 +43,7 @@ export const spec = { const secure = 1; // treat all requests as secure const request = validBidRequests[0]; const params = request.params; - let impData = { + const impData = { id: request.bidId, secure: secure, banner: buildBanner(request) @@ -84,6 +84,7 @@ export const spec = { } }; } + provideEids(request, payload); return { method: 'POST', url: ENDPOINT_URL, @@ -172,4 +173,10 @@ function buildBanner(request) { }; } +function provideEids(request, payload) { + if (Array.isArray(request.userIdAsEids) && request.userIdAsEids.length > 0) { + utils.deepSetValue(payload, 'user.ext.eids', request.userIdAsEids); + } +} + registerBidder(spec); diff --git a/test/spec/modules/zetaSspBidAdapter_spec.js b/test/spec/modules/zetaSspBidAdapter_spec.js index 2766632f707..4602e2d2b77 100644 --- a/test/spec/modules/zetaSspBidAdapter_spec.js +++ b/test/spec/modules/zetaSspBidAdapter_spec.js @@ -1,6 +1,29 @@ import {spec} from '../../../modules/zetaSspBidAdapter.js' -describe('Zeta Ssp Bid Adapter', function () { +describe('Zeta Ssp Bid Adapter', function() { + const eids = [ + { + 'source': 'example.com', + 'uids': [ + { + 'id': 'someId1', + 'atype': 1 + }, + { + 'id': 'someId2', + 'atype': 1 + }, + { + 'id': 'someId3', + 'atype': 2 + } + ], + 'ext': { + 'foo': 'bar' + } + } + ]; + const bannerRequest = [{ bidId: 12345, auctionId: 67890, @@ -23,7 +46,8 @@ describe('Zeta Ssp Bid Adapter', function () { sid: 'publisherId' }, test: 1 - } + }, + userIdAsEids: eids }]; it('Test the bid validation function', function () { @@ -34,6 +58,12 @@ describe('Zeta Ssp Bid Adapter', function () { expect(invalidBid).to.be.false; }); + it('Test provide eids', function () { + const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + const payload = JSON.parse(request.data); + expect(payload.user.ext.eids).to.eql(eids); + }); + it('Test the request processing function', function () { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); expect(request).to.not.be.empty; From 983d8a9d7aa315b5a4ca01ca5a744375e9b26d14 Mon Sep 17 00:00:00 2001 From: Andrzej Michnowski Date: Fri, 28 May 2021 18:38:11 +0200 Subject: [PATCH 24/63] Adocean Bid Adapter: add meta.advertiserDomains to bid response (#6872) * AdOcean adapter - support for multiple sizes * AdOcean adapter - tests - use normal functions instead of arrow ones for consistency * AdOcean adapter - support for multiple sizes - changed way of sending dimensions * AdOcean adapter - change separator between sizes group * AdOcean adapter - small fix in buildRequest * AdOcean adapter - support advertiserDomains * AdOcean adapter - add advertiserDomains default value Co-authored-by: Marcin Muras Co-authored-by: Andrzej Michnowski --- modules/adoceanBidAdapter.js | 5 ++++- test/spec/modules/adoceanBidAdapter_spec.js | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/modules/adoceanBidAdapter.js b/modules/adoceanBidAdapter.js index b75d1972f5a..7ce9c7ae8f2 100644 --- a/modules/adoceanBidAdapter.js +++ b/modules/adoceanBidAdapter.js @@ -80,7 +80,10 @@ function interpretResponse(placementResponse, bidRequest, bids) { width: parseInt(placementResponse.width, 10), netRevenue: false, ttl: parseInt(placementResponse.ttl), - creativeId: placementResponse.crid + creativeId: placementResponse.crid, + meta: { + advertiserDomains: placementResponse.adomain || [] + } }; bids.push(bid); diff --git a/test/spec/modules/adoceanBidAdapter_spec.js b/test/spec/modules/adoceanBidAdapter_spec.js index 2b4b7d711e1..43316cd7483 100644 --- a/test/spec/modules/adoceanBidAdapter_spec.js +++ b/test/spec/modules/adoceanBidAdapter_spec.js @@ -150,7 +150,8 @@ describe('AdoceanAdapter', function () { 'width': '300', 'height': '250', 'crid': '0af345b42983cc4bc0', - 'ttl': '300' + 'ttl': '300', + 'adomain': ['adocean.pl'] } ], 'headers': { @@ -186,7 +187,10 @@ describe('AdoceanAdapter', function () { 'ad': '', 'creativeId': '0af345b42983cc4bc0', 'ttl': 300, - 'netRevenue': false + 'netRevenue': false, + 'meta': { + 'advertiserDomains': ['adocean.pl'] + } } ]; @@ -197,6 +201,8 @@ describe('AdoceanAdapter', function () { resultKeys.forEach(function(k) { if (k === 'ad') { expect(result[0][k]).to.match(/$/); + } else if (k === 'meta') { + expect(result[0][k]).to.deep.equal(expectedResponse[0][k]); } else { expect(result[0][k]).to.equal(expectedResponse[0][k]); } From 00b1afac40647fd61d608032d37eaf8ee3160936 Mon Sep 17 00:00:00 2001 From: redaguermas Date: Fri, 28 May 2021 11:58:29 -0700 Subject: [PATCH 25/63] No Bid Adapter: added bidResponse.meta and advertiserDomains (#6862) * Enable supplyChain support * Added support for COPPA * rebuilt * Added support for Extended User IDs. * Added support for the "meta" attribute in bid response. * Removed IDE files. Co-authored-by: Reda Guermas --- modules/nobidBidAdapter.js | 5 ++- test/spec/modules/nobidBidAdapter_spec.js | 53 ++++++++++++++++++++--- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/modules/nobidBidAdapter.js b/modules/nobidBidAdapter.js index 051202cab97..3aabd8f0635 100644 --- a/modules/nobidBidAdapter.js +++ b/modules/nobidBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const storage = getStorageManager(); const BIDDER_CODE = 'nobid'; -window.nobidVersion = '1.2.9'; +window.nobidVersion = '1.3.0'; window.nobid = window.nobid || {}; window.nobid.bidResponses = window.nobid.bidResponses || {}; window.nobid.timeoutTotal = 0; @@ -298,6 +298,9 @@ function nobidInterpretResponse(response, bidRequest) { if (bid.videoCacheKey) { bidResponse.videoCacheKey = bid.videoCacheKey; } + if (bid.meta) { + bidResponse.meta = bid.meta; + } bidResponses.push(bidResponse); } diff --git a/test/spec/modules/nobidBidAdapter_spec.js b/test/spec/modules/nobidBidAdapter_spec.js index 8dc15fbc89e..c44b0ce3fc2 100644 --- a/test/spec/modules/nobidBidAdapter_spec.js +++ b/test/spec/modules/nobidBidAdapter_spec.js @@ -257,12 +257,12 @@ describe('Nobid Adapter', function () { 'uids': [ { 'id': 'ID5_ID', - 'atype': 1, - 'ext': { - 'linkType': 0 - } + 'atype': 1 } - ] + ], + 'ext': { + 'linkType': 0 + } }, { 'source': 'adserver.org', @@ -284,7 +284,7 @@ describe('Nobid Adapter', function () { refererInfo: {referer: REFERER} } - it('should get user ids from eids', function () { + it('should criteo eid', function () { const request = spec.buildRequests(bidRequests, bidderRequest); const payload = JSON.parse(request.data); expect(payload.sid).to.exist.and.to.equal(2); @@ -598,6 +598,47 @@ describe('Nobid Adapter', function () { }); }); + describe('interpretResponseWithMeta', function () { + const CREATIVE_ID_300x250 = 'CREATIVE-100'; + const ADUNIT_300x250 = 'ADUNIT-1'; + const ADMARKUP_300x250 = 'ADMARKUP-300x250'; + const PRICE_300x250 = 0.51; + const REQUEST_ID = '3db3773286ee59'; + const DEAL_ID = 'deal123'; + const ADOMAINS = ['adomain1', 'adomain2']; + let response = { + country: 'US', + ip: '68.83.15.75', + device: 'COMPUTER', + site: 2, + bids: [ + {id: 1, + bdrid: 101, + divid: ADUNIT_300x250, + dealid: DEAL_ID, + creativeid: CREATIVE_ID_300x250, + size: {'w': 300, 'h': 250}, + adm: ADMARKUP_300x250, + price: '' + PRICE_300x250, + meta: { + advertiserDomains: ADOMAINS + } + } + ] + }; + + it('should meta.advertiserDomains be respected', function () { + let bidderRequest = { + bids: [{ + bidId: REQUEST_ID, + adUnitCode: ADUNIT_300x250 + }] + } + let result = spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest}); + expect(result[0].meta.advertiserDomains).to.equal(ADOMAINS); + }); + }); + describe('buildRequestsWithSupplyChain', function () { const SITE_ID = 2; let bidRequests = [ From 2bd62fa41d91460ec3734ecc8a07f7a8fb4c6847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20=C5=A0t=C5=A1epelin?= Date: Sat, 29 May 2021 14:26:00 +0300 Subject: [PATCH 26/63] add meta.advertiserDomains and meta.mediaType to bid response (#6875) --- modules/cointrafficBidAdapter.js | 6 ++++- .../modules/cointrafficBidAdapter_spec.js | 24 +++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/modules/cointrafficBidAdapter.js b/modules/cointrafficBidAdapter.js index 43f5cc01ccf..a6241980e06 100644 --- a/modules/cointrafficBidAdapter.js +++ b/modules/cointrafficBidAdapter.js @@ -85,7 +85,11 @@ export const spec = { height: response.height, creativeId: response.creativeId, ttl: response.ttl, - ad: response.ad + ad: response.ad, + meta: { + advertiserDomains: response.adomain && response.adomain.length ? response.adomain : [], + mediaType: response.mediaType + } }; bidResponses.push(bidResponse); diff --git a/test/spec/modules/cointrafficBidAdapter_spec.js b/test/spec/modules/cointrafficBidAdapter_spec.js index a2ce4cedea0..3755ddc4c4a 100644 --- a/test/spec/modules/cointrafficBidAdapter_spec.js +++ b/test/spec/modules/cointrafficBidAdapter_spec.js @@ -142,7 +142,9 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + mediaType: 'banner', + adomain: ['test.com'] } }; @@ -155,7 +157,13 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + meta: { + mediaType: 'banner', + advertiserDomains: [ + 'test.com', + ] + } }]; let result = spec.interpretResponse(serverResponse, bidRequest[0]); @@ -186,7 +194,9 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + mediaType: 'banner', + adomain: ['test.com'] } }; @@ -199,7 +209,13 @@ describe('cointrafficBidAdapter', function () { height: 250, creativeId: 'creativeId12345', ttl: 90, - ad: '

I am an ad

' + ad: '

I am an ad

', + meta: { + mediaType: 'banner', + advertiserDomains: [ + 'test.com', + ] + } }]; const getConfigStub = sinon.stub(config, 'getConfig').returns('USD'); From bf610f6074137a16fa6e1b6131bf9bac1b3f4943 Mon Sep 17 00:00:00 2001 From: Scott Menzer Date: Mon, 31 May 2021 09:17:49 +0200 Subject: [PATCH 27/63] Increment pre version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d4c6cd24181..6df9993806c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.41.0", + "version": "4.42.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From e17f231032c31bd5fe1529c1e33e1398ecdf57aa Mon Sep 17 00:00:00 2001 From: arasaki-yuki <35788388+arasaki-yuki@users.noreply.github.com> Date: Mon, 31 May 2021 23:11:59 +0900 Subject: [PATCH 28/63] GMOSSP BidAdapter: add adomain support (#6877) --- modules/gmosspBidAdapter.js | 4 ++++ test/spec/modules/gmosspBidAdapter_spec.js | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/modules/gmosspBidAdapter.js b/modules/gmosspBidAdapter.js index 5eac4069b21..bf57e63d53c 100644 --- a/modules/gmosspBidAdapter.js +++ b/modules/gmosspBidAdapter.js @@ -94,6 +94,10 @@ export const spec = { ttl: res.ttl || 300 }; + if (res.adomains) { + utils.deepSetValue(bid, 'meta.advertiserDomains', Array.isArray(res.adomains) ? res.adomains : [res.adomains]); + } + return [bid]; }, diff --git a/test/spec/modules/gmosspBidAdapter_spec.js b/test/spec/modules/gmosspBidAdapter_spec.js index 52bb8460caa..d29e27554c2 100644 --- a/test/spec/modules/gmosspBidAdapter_spec.js +++ b/test/spec/modules/gmosspBidAdapter_spec.js @@ -106,6 +106,9 @@ describe('GmosspAdapter', function () { price: 20, w: 300, h: 250, + adomains: [ + 'test.com' + ], ad: '
', creativeId: '985ec572b32be309.76973017', cur: 'JPY', @@ -126,6 +129,11 @@ describe('GmosspAdapter', function () { currency: 'JPY', width: 300, height: 250, + meta: { + advertiserDomains: [ + 'test.com' + ] + }, ad: '
', creativeId: '985ec572b32be309.76973017', netRevenue: true, From 262e27c0ba0469ee415d75c2ffa54e81513eb15a Mon Sep 17 00:00:00 2001 From: Junus Date: Mon, 31 May 2021 20:13:58 +0600 Subject: [PATCH 29/63] A4G Bid Adapter: add meta adomain (#6878) * A4G adapter: add meta.advertiserDomains * added another test --- modules/a4gBidAdapter.js | 5 ++++- test/spec/modules/a4gBidAdapter_spec.js | 22 +++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/modules/a4gBidAdapter.js b/modules/a4gBidAdapter.js index 1961dba1f10..01c59616dc0 100644 --- a/modules/a4gBidAdapter.js +++ b/modules/a4gBidAdapter.js @@ -77,7 +77,10 @@ export const spec = { currency: A4G_CURRENCY, netRevenue: true, ttl: A4G_TTL, - ad: response.ad + ad: response.ad, + meta: { + advertiserDomains: response.adomain && response.adomain.length > 0 ? response.adomain : [] + } }; bidResponses.push(bidResponse); } diff --git a/test/spec/modules/a4gBidAdapter_spec.js b/test/spec/modules/a4gBidAdapter_spec.js index cb05fa62ab6..a9ead9fd021 100644 --- a/test/spec/modules/a4gBidAdapter_spec.js +++ b/test/spec/modules/a4gBidAdapter_spec.js @@ -161,7 +161,11 @@ describe('a4gAdapterTests', function () { ad: 'test ad', currency: 'USD', ttl: 120, - netRevenue: true + netRevenue: true, + meta: { + advertiserDomains: [] + } + } ]; const result = spec.interpretResponse(bidResponse, bidRequest); @@ -181,7 +185,10 @@ describe('a4gAdapterTests', function () { ad: 'test ad', currency: 'USD', ttl: 120, - netRevenue: true + netRevenue: true, + meta: { + advertiserDomains: [] + } } ]; const result = spec.interpretResponse(bidResponseWithoutCrid, bidRequest); @@ -201,7 +208,8 @@ describe('a4gAdapterTests', function () { 'currency', 'netRevenue', 'ttl', - 'ad' + 'ad', + 'meta' ]; let resultKeys = Object.keys(result[0]); @@ -215,5 +223,13 @@ describe('a4gAdapterTests', function () { expect(result[0].requestId).to.not.equal(result[0].adId); }) + + it('advertiserDomains is included when sent by server', function () { + bidResponse.body[0].adomain = ['test_adomain']; + let response = spec.interpretResponse(bidResponse, bidRequest); + expect(Object.keys(response[0].meta)).to.include.members(['advertiserDomains']); + expect(response[0].meta.advertiserDomains).to.deep.equal(['test_adomain']); + delete bidResponse.body[0].adomain; + }); }); }); From 8183e8500ee37c8445ae68949326741a4b3eb233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rok=20Su=C5=A1nik?= Date: Mon, 31 May 2021 16:17:38 +0200 Subject: [PATCH 30/63] add pbjs version to bid request (#6879) --- modules/outbrainBidAdapter.js | 8 ++++++++ test/spec/modules/outbrainBidAdapter_spec.js | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/modules/outbrainBidAdapter.js b/modules/outbrainBidAdapter.js index c0af09c2b50..052122e95f1 100644 --- a/modules/outbrainBidAdapter.js +++ b/modules/outbrainBidAdapter.js @@ -79,6 +79,14 @@ export const spec = { imp: imps, bcat: bcat, badv: badv, + ext: { + prebid: { + channel: { + name: 'pbjs', + version: '$prebid.version$' + } + } + } }; if (test) { diff --git a/test/spec/modules/outbrainBidAdapter_spec.js b/test/spec/modules/outbrainBidAdapter_spec.js index 4bdd657f419..a5f23240a7c 100644 --- a/test/spec/modules/outbrainBidAdapter_spec.js +++ b/test/spec/modules/outbrainBidAdapter_spec.js @@ -204,7 +204,14 @@ describe('Outbrain Adapter', function () { request: JSON.stringify(expectedNativeAssets) } } - ] + ], + ext: { + prebid: { + channel: { + name: 'pbjs', version: '$prebid.version$' + } + } + } } const res = spec.buildRequests([bidRequest], commonBidderRequest) expect(res.url).to.equal('https://bidder-url.com') @@ -244,7 +251,14 @@ describe('Outbrain Adapter', function () { ] } } - ] + ], + ext: { + prebid: { + channel: { + name: 'pbjs', version: '$prebid.version$' + } + } + } } const res = spec.buildRequests([bidRequest], commonBidderRequest) expect(res.url).to.equal('https://bidder-url.com') From 10fc2a4be963b63917eb5ccd04ebf96b80a0df74 Mon Sep 17 00:00:00 2001 From: PWyrembak Date: Mon, 31 May 2021 18:56:40 +0300 Subject: [PATCH 31/63] TrustX Bid Adapter: meta.advertiserDomains support (#6891) * Add trustx adapter and tests for it * update integration example * Update trustx adapter * Post-review fixes of Trustx adapter * Code improvement for trustx adapter: changed default price type from gross to net * Update TrustX adapter to support the 1.0 version * Make requested changes for TrustX adapter * Updated markdown file for TrustX adapter * Fix TrustX adapter and spec file * Update TrustX adapter: r parameter was added to ad request as cache buster * Add support of gdpr to Trustx Bid Adapter * Add wtimeout to ad request params for TrustX Bid Adapter * TrustX Bid Adapter: remove last ampersand in the ad request * Update TrustX Bid Adapter to support identical uids in parameters * Update TrustX Bid Adapter to ignore bids that sizes do not match the size of the request * Update TrustX Bid Adapter to support instream and outstream video * Added wrapperType and wrapperVersion parameters in ad request for TrustX Bid Adapter * Update TrustX Bid Adapter to use refererInfo instead depricated function utils.getTopWindowUrl * HOTFIX for referrer encodind in TrustX Bid Adapter * Fix test for TrustX Bid Adapter * TrustX Bid Adapter: added keywords passing support * TrustX Bid Adapter: added us_privacy parameter in bid request * TrustX Bid Adapter: fix us_privacy parameter in bid request * Fix alias error for TrustX Bid Adapter * TrustX Bid Adapter: added new request format * TrustX Bid adapter: fix new format endpoint * TrustX Bid Adapter: update md file to support useNewFormat parameter * TrustX Bid Adapter: added additional sync url * TrustX Bid Adapter: added check for enabled syncs number + added gdpr data to sync urls * TrustX Bid Adapter: added support of meta.advertiserDomains --- modules/trustxBidAdapter.js | 5 ++- test/spec/modules/trustxBidAdapter_spec.js | 44 +++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/modules/trustxBidAdapter.js b/modules/trustxBidAdapter.js index 9f8de30a0d4..247a1f6d048 100644 --- a/modules/trustxBidAdapter.js +++ b/modules/trustxBidAdapter.js @@ -179,7 +179,10 @@ function _addBidResponse(serverBid, bidRequest, bidResponses, RendererConst) { currency: 'USD', netRevenue: newFormat ? false : priceType !== 'gross', ttl: TIME_TO_LIVE, - dealId: serverBid.dealid + dealId: serverBid.dealid, + meta: { + advertiserDomains: serverBid.adomain ? serverBid.adomain : [] + }, }; if (serverBid.content_type === 'video') { bidResponse.vastXml = serverBid.adm; diff --git a/test/spec/modules/trustxBidAdapter_spec.js b/test/spec/modules/trustxBidAdapter_spec.js index c0155f87ab6..f53116f60a4 100644 --- a/test/spec/modules/trustxBidAdapter_spec.js +++ b/test/spec/modules/trustxBidAdapter_spec.js @@ -661,7 +661,7 @@ describe('TrustXAdapter', function () { describe('interpretResponse', function () { const responses = [ - {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300}], 'seat': '1'}, + {'bid': [{'price': 1.15, 'adm': '
test content 1
', 'auid': 43, 'h': 250, 'w': 300, 'adomain': ['somedomain.com']}], 'seat': '1'}, {'bid': [{'price': 0.5, 'adm': '
test content 2
', 'auid': 44, 'h': 600, 'w': 300}], 'seat': '1'}, {'bid': [{'price': 0.15, 'adm': '
test content 3
', 'auid': 43, 'h': 90, 'w': 728}], 'seat': '1'}, {'bid': [{'price': 0, 'auid': 45, 'h': 250, 'w': 300}], 'seat': '1'}, @@ -699,6 +699,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['somedomain.com'] + }, } ]; @@ -756,6 +759,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': ['somedomain.com'] + }, }, { 'requestId': '4dff80cc4ee346', @@ -769,6 +775,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '5703af74d0472a', @@ -782,6 +791,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -909,6 +921,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '4e111f1b66e4', @@ -922,6 +937,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '26d6f897b516', @@ -935,6 +953,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '326bde7fbf69', @@ -948,6 +969,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -1009,6 +1033,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, }, { 'requestId': '57b2ebe70e16', @@ -1022,6 +1049,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'banner', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, } ]; @@ -1082,6 +1112,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1161,6 +1194,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1178,6 +1214,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' @@ -1195,6 +1234,9 @@ describe('TrustXAdapter', function () { 'mediaType': 'video', 'netRevenue': true, 'ttl': 360, + 'meta': { + 'advertiserDomains': [] + }, 'vastXml': '\n<\/Ad>\n<\/VAST>', 'adResponse': { 'content': '\n<\/Ad>\n<\/VAST>' From 6be24d483e415dee4bdfd145803d2cb4510273dc Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Mon, 31 May 2021 19:17:47 +0300 Subject: [PATCH 32/63] Adkernel: denakop alias (#6886) --- modules/adkernelBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index e4b04a27d42..b18b2333bac 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -68,7 +68,8 @@ export const spec = { {code: 'bcm'}, {code: 'engageadx'}, {code: 'converge_digital', gvlid: 248}, - {code: 'adomega'} + {code: 'adomega'}, + {code: 'denakop'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], From 59b0fc9c033a4c97fc01e6859a0d6c2a9d69e77e Mon Sep 17 00:00:00 2001 From: mediaconsortium-develop <76139568+mediaconsortium-develop@users.noreply.github.com> Date: Tue, 1 Jun 2021 01:58:48 +0900 Subject: [PATCH 33/63] Digital Garage RTD module: new rtd submodule #6410 (#6847) * re-open pull request reference #6410 * delete Overview --- modules/.submodules.json | 1 + modules/dgkeywordRtdProvider.js | 132 +++++++ modules/dgkeywordRtdProvider.md | 29 ++ .../spec/modules/dgkeywordRtdProvider_spec.js | 326 ++++++++++++++++++ 4 files changed, 488 insertions(+) create mode 100644 modules/dgkeywordRtdProvider.js create mode 100644 modules/dgkeywordRtdProvider.md create mode 100644 test/spec/modules/dgkeywordRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 0a10044a78e..74ebc021a6d 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -36,6 +36,7 @@ ], "rtdModule": [ "browsiRtdProvider", + "dgkeywordRtdProvider", "geoedgeRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js new file mode 100644 index 00000000000..52fa9fd70c4 --- /dev/null +++ b/modules/dgkeywordRtdProvider.js @@ -0,0 +1,132 @@ +/** + * This module adds dgkeyword provider to the real time data module + * The {@link module:modules/realTimeData} module is required + * The module will get keywords from 1plux profile api. + * This module can work only with AppNexusBidAdapter. + * @module modules/dgkeywordProvider + * @requires module:modules/realTimeData + */ + +import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getGlobal } from '../src/prebidGlobal.js'; + +/** + * get keywords from api server. and set keywords. + * @param {Object} reqBidsConfigObj + * @param {function} callback + * @param {Object} moduleConfig + * @param {Object} userConsent + */ +export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, userConsent) { + const URL = 'https://mediaconsortium.profiles.tagger.opecloud.com/api/v1?url='; + const PROFILE_TIMEOUT_MS = 1000; + const timeout = (moduleConfig && moduleConfig.params && moduleConfig.params.timeout && Number(moduleConfig.params.timeout) > 0) ? Number(moduleConfig.params.timeout) : PROFILE_TIMEOUT_MS; + const url = (moduleConfig && moduleConfig.params && moduleConfig.params.url) ? moduleConfig.params.url : URL + encodeURIComponent(window.location.href); + const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; + let isFinish = false; + utils.logMessage('[dgkeyword sub module]', adUnits, timeout); + let setKeywordTargetBidders = getTargetBidderOfDgKeywords(adUnits); + if (setKeywordTargetBidders.length <= 0) { + utils.logMessage('[dgkeyword sub module] no dgkeyword targets.'); + callback(); + } else { + utils.logMessage('[dgkeyword sub module] dgkeyword targets:', setKeywordTargetBidders); + utils.logMessage('[dgkeyword sub module] get targets from profile api start.'); + ajax(url, { + success: function(response) { + const res = JSON.parse(response); + if (!isFinish) { + utils.logMessage('[dgkeyword sub module] get targets from profile api end.'); + if (res) { + let keywords = {}; + if (res['s'] != null && res['s'].length > 0) { + keywords['opeaud'] = res['s']; + } + if (res['t'] != null && res['t'].length > 0) { + keywords['opectx'] = res['t']; + } + if (Object.keys(keywords).length > 0) { + const targetBidKeys = {} + for (let bid of setKeywordTargetBidders) { + // set keywords to params + bid.params.keywords = keywords; + if (!targetBidKeys[bid.bidder]) { + targetBidKeys[bid.bidder] = true; + } + } + + // set keywrods to ortb2 + let addOrtb2 = {}; + utils.deepSetValue(addOrtb2, 'site.keywords', keywords); + utils.deepSetValue(addOrtb2, 'user.keywords', keywords); + const ortb2 = {ortb2: addOrtb2}; + getGlobal().setBidderConfig({ bidders: Object.keys(targetBidKeys), config: ortb2 }); + } + } + isFinish = true; + } + callback(); + }, + error: function(errorStatus) { + // error occur + utils.logError('[dgkeyword sub module] profile api access error.', errorStatus); + callback(); + } + }, null, { + withCredentials: true, + contentType: 'application/json', + }); + setTimeout(function () { + if (!isFinish) { + // profile api timeout + utils.logInfo('[dgkeyword sub module] profile api timeout. [timeout: ' + timeout + 'ms]'); + isFinish = true; + } + callback(); + }, timeout); + } +} + +/** + * get all bidder which hava {dgkeyword: true} in params + * @param {Object} adUnits + */ +export function getTargetBidderOfDgKeywords(adUnits) { + let setKeywordTargetBidders = []; + for (let adUnit of adUnits) { + for (let bid of adUnit.bids) { + if (bid.params && bid.params['dgkeyword'] === true) { + delete bid.params['dgkeyword']; + setKeywordTargetBidders.push(bid); + } + } + } + return setKeywordTargetBidders; +} + +/** @type {RtdSubmodule} */ +export const dgkeywordSubmodule = { + /** + * used to link submodule with realTimeData + * @type {string} + */ + name: 'dgkeyword', + /** + * get data and send back to realTimeData module + * @function + * @param {string[]} adUnitsCodes + */ + getBidRequestData: getDgKeywordsAndSet, + init: init, +}; + +function init(moduleConfig) { + return true; +} + +function registerSubModule() { + submodule('realTimeData', dgkeywordSubmodule); +} +registerSubModule(); diff --git a/modules/dgkeywordRtdProvider.md b/modules/dgkeywordRtdProvider.md new file mode 100644 index 00000000000..314475b45a8 --- /dev/null +++ b/modules/dgkeywordRtdProvider.md @@ -0,0 +1,29 @@ + ## Integration + +1) Compile the Digital Garage Keyword Module and Appnexus Bid Adapter into your Prebid build: + +``` +gulp build --modules="dgkeywordRtdProvider,appnexusBidAdapter,..." +``` + +2) Use `setConfig` to instruct Prebid.js to initilize the dgkeyword module, as specified below. + +## Configuration + +This module is configured as part of the `realTimeData.dataProviders` + +```javascript +var DGKEYWORD_TIMEOUT = 1000; +pbjs.setConfig({ + realTimeData: { + auctionDelay: DGKEYWORD_TIMEOUT, + dataProviders: [{ + name: 'dgkeyword', + waitForIt: true, + params: { + timeout: DGKEYWORD_TIMEOUT + } + }] + } +}); +``` diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js new file mode 100644 index 00000000000..0fac92e5118 --- /dev/null +++ b/test/spec/modules/dgkeywordRtdProvider_spec.js @@ -0,0 +1,326 @@ +import * as dgRtd from 'modules/dgkeywordRtdProvider.js'; +import { cloneDeep } from 'lodash'; +import { server } from 'test/mocks/xhr.js'; +import { getGlobal } from 'src/prebidGlobal.js'; +import { config } from 'src/config.js'; + +const DG_GET_KEYWORDS_TIMEOUT = 1950; +const DEF_CONFIG = { + name: 'dgkeyword', + waitForIt: true, + params: { + timeout: DG_GET_KEYWORDS_TIMEOUT, + }, +}; +const DUMMY_RESPONSE_HEADER = {'Content-Type': 'application/json'}; +const DUMMY_RESPONSE = { s: ['s1', 's2'], t: ['t1', 't2'] }; +const SUCCESS_RESULT = { opeaud: ['s1', 's2'], opectx: ['t1', 't2'] }; +const SUCCESS_ORTB2 = { ortb2: { site: { keywords: SUCCESS_RESULT }, user: { keywords: SUCCESS_RESULT } } }; + +describe('Digital Garage Keyword Module', function () { + it('should init and return always true', function () { + expect(dgRtd.dgkeywordSubmodule.init()).to.equal(true); + }); + + describe('dgkeyword target test', function () { + it('should have no target', function () { + const adUnits_no_target = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: false, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + }, + }, + ], + }, + ]; + expect(dgRtd.getTargetBidderOfDgKeywords(adUnits_no_target)).an('array') + .that.is.empty; + }); + it('should have targets', function () { + const adUnits_targets = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: true, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + dgkeyword: false, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + dgkeyword: true, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + dgkeyword: 'aa', + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + dgkeyword: true, + }, + }, + ], + }, + ]; + const targets = dgRtd.getTargetBidderOfDgKeywords(adUnits_targets); + expect(targets[0].bidder).to.be.equal('dg2'); + expect(targets[0].params.placementId).to.be.equal(99999998); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].bidder).to.be.equal('dg'); + expect(targets[1].params.placementId).to.be.equal(99999996); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + }); + }); + + describe('get profile.', function () { + const AD_UNITS = [ + { + code: 'code1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999999, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999998, + dgkeyword: true, + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999997, + dgkeyword: false, + }, + }, + ], + }, + { + code: 'code2', + mediaTypes: { + banner: { + sizes: [[300, 250]], + }, + }, + bids: [ + { + bidder: 'dg', + params: { + placementId: 99999996, + dgkeyword: true, + }, + }, + { + bidder: 'dg2', + params: { + placementId: 99999995, + dgkeyword: 'aa', + }, + }, + { + bidder: 'dg3', + params: { + placementId: 99999994, + }, + }, + ], + }, + ]; + it('should get profiles error(404).', function (done) { + let pdjs = getGlobal(); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + dgRtd.getDgKeywordsAndSet( + pdjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + expect(config.getBidderConfig()).to.be.deep.equal({}); + + done(); + }, + moduleConfig, + null + ); + const request = server.requests[0]; + request.respond(404); + }); + it('should get profiles timeout.', function (done) { + let pdjs = getGlobal(); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + moduleConfig.params.timeout = 10; + dgRtd.getDgKeywordsAndSet( + pdjs, + () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.an('undefined'); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.an('undefined'); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + expect(config.getBidderConfig()).to.be.deep.equal({}); + + done(); + }, + moduleConfig, + null + ); + setTimeout(() => { + const request = server.requests[0]; + if (request) { + request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); + } + }, 1000) + }); + it('should get profiles ok(200).', function (done) { + let pdjs = getGlobal(); + pbjs.adUnits = cloneDeep(AD_UNITS); + let moduleConfig = cloneDeep(DEF_CONFIG); + dgRtd.getDgKeywordsAndSet(pdjs, () => { + let targets = pbjs.adUnits[0].bids; + expect(targets[1].bidder).to.be.equal('dg2'); + expect(targets[1].params.placementId).to.be.equal(99999998); + expect(targets[1].params.dgkeyword).to.be.an('undefined'); + expect(targets[1].params.keywords).to.be.deep.equal(SUCCESS_RESULT); + targets = pbjs.adUnits[1].bids; + expect(targets[0].bidder).to.be.equal('dg'); + expect(targets[0].params.placementId).to.be.equal(99999996); + expect(targets[0].params.dgkeyword).to.be.an('undefined'); + expect(targets[0].params.keywords).to.be.deep.equal(SUCCESS_RESULT); + expect(targets[2].bidder).to.be.equal('dg3'); + expect(targets[2].params.placementId).to.be.equal(99999994); + expect(targets[2].params.dgkeyword).to.be.an('undefined'); + expect(targets[2].params.keywords).to.be.an('undefined'); + + // expect(config.getBidderConfig()).to.be.deep.equal({ dg2: SUCCESS_ORTB2, dg: SUCCESS_ORTB2 }); + + done(); + }, moduleConfig, null); + const request = server.requests[0]; + request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); + }); + }); +}); From b5f15cf6a4e9e9f12d4b1a58a6a14635e9495755 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Mon, 31 May 2021 12:26:04 -0700 Subject: [PATCH 34/63] Revert "Digital Garage RTD module: new rtd submodule #6410 (#6847)" (#6895) This reverts commit 59b0fc9c033a4c97fc01e6859a0d6c2a9d69e77e. --- modules/.submodules.json | 1 - modules/dgkeywordRtdProvider.js | 132 ------- modules/dgkeywordRtdProvider.md | 29 -- .../spec/modules/dgkeywordRtdProvider_spec.js | 326 ------------------ 4 files changed, 488 deletions(-) delete mode 100644 modules/dgkeywordRtdProvider.js delete mode 100644 modules/dgkeywordRtdProvider.md delete mode 100644 test/spec/modules/dgkeywordRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index 74ebc021a6d..0a10044a78e 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -36,7 +36,6 @@ ], "rtdModule": [ "browsiRtdProvider", - "dgkeywordRtdProvider", "geoedgeRtdProvider", "haloRtdProvider", "jwplayerRtdProvider", diff --git a/modules/dgkeywordRtdProvider.js b/modules/dgkeywordRtdProvider.js deleted file mode 100644 index 52fa9fd70c4..00000000000 --- a/modules/dgkeywordRtdProvider.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * This module adds dgkeyword provider to the real time data module - * The {@link module:modules/realTimeData} module is required - * The module will get keywords from 1plux profile api. - * This module can work only with AppNexusBidAdapter. - * @module modules/dgkeywordProvider - * @requires module:modules/realTimeData - */ - -import * as utils from '../src/utils.js'; -import { ajax } from '../src/ajax.js'; -import { submodule } from '../src/hook.js'; -import { getGlobal } from '../src/prebidGlobal.js'; - -/** - * get keywords from api server. and set keywords. - * @param {Object} reqBidsConfigObj - * @param {function} callback - * @param {Object} moduleConfig - * @param {Object} userConsent - */ -export function getDgKeywordsAndSet(reqBidsConfigObj, callback, moduleConfig, userConsent) { - const URL = 'https://mediaconsortium.profiles.tagger.opecloud.com/api/v1?url='; - const PROFILE_TIMEOUT_MS = 1000; - const timeout = (moduleConfig && moduleConfig.params && moduleConfig.params.timeout && Number(moduleConfig.params.timeout) > 0) ? Number(moduleConfig.params.timeout) : PROFILE_TIMEOUT_MS; - const url = (moduleConfig && moduleConfig.params && moduleConfig.params.url) ? moduleConfig.params.url : URL + encodeURIComponent(window.location.href); - const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits; - let isFinish = false; - utils.logMessage('[dgkeyword sub module]', adUnits, timeout); - let setKeywordTargetBidders = getTargetBidderOfDgKeywords(adUnits); - if (setKeywordTargetBidders.length <= 0) { - utils.logMessage('[dgkeyword sub module] no dgkeyword targets.'); - callback(); - } else { - utils.logMessage('[dgkeyword sub module] dgkeyword targets:', setKeywordTargetBidders); - utils.logMessage('[dgkeyword sub module] get targets from profile api start.'); - ajax(url, { - success: function(response) { - const res = JSON.parse(response); - if (!isFinish) { - utils.logMessage('[dgkeyword sub module] get targets from profile api end.'); - if (res) { - let keywords = {}; - if (res['s'] != null && res['s'].length > 0) { - keywords['opeaud'] = res['s']; - } - if (res['t'] != null && res['t'].length > 0) { - keywords['opectx'] = res['t']; - } - if (Object.keys(keywords).length > 0) { - const targetBidKeys = {} - for (let bid of setKeywordTargetBidders) { - // set keywords to params - bid.params.keywords = keywords; - if (!targetBidKeys[bid.bidder]) { - targetBidKeys[bid.bidder] = true; - } - } - - // set keywrods to ortb2 - let addOrtb2 = {}; - utils.deepSetValue(addOrtb2, 'site.keywords', keywords); - utils.deepSetValue(addOrtb2, 'user.keywords', keywords); - const ortb2 = {ortb2: addOrtb2}; - getGlobal().setBidderConfig({ bidders: Object.keys(targetBidKeys), config: ortb2 }); - } - } - isFinish = true; - } - callback(); - }, - error: function(errorStatus) { - // error occur - utils.logError('[dgkeyword sub module] profile api access error.', errorStatus); - callback(); - } - }, null, { - withCredentials: true, - contentType: 'application/json', - }); - setTimeout(function () { - if (!isFinish) { - // profile api timeout - utils.logInfo('[dgkeyword sub module] profile api timeout. [timeout: ' + timeout + 'ms]'); - isFinish = true; - } - callback(); - }, timeout); - } -} - -/** - * get all bidder which hava {dgkeyword: true} in params - * @param {Object} adUnits - */ -export function getTargetBidderOfDgKeywords(adUnits) { - let setKeywordTargetBidders = []; - for (let adUnit of adUnits) { - for (let bid of adUnit.bids) { - if (bid.params && bid.params['dgkeyword'] === true) { - delete bid.params['dgkeyword']; - setKeywordTargetBidders.push(bid); - } - } - } - return setKeywordTargetBidders; -} - -/** @type {RtdSubmodule} */ -export const dgkeywordSubmodule = { - /** - * used to link submodule with realTimeData - * @type {string} - */ - name: 'dgkeyword', - /** - * get data and send back to realTimeData module - * @function - * @param {string[]} adUnitsCodes - */ - getBidRequestData: getDgKeywordsAndSet, - init: init, -}; - -function init(moduleConfig) { - return true; -} - -function registerSubModule() { - submodule('realTimeData', dgkeywordSubmodule); -} -registerSubModule(); diff --git a/modules/dgkeywordRtdProvider.md b/modules/dgkeywordRtdProvider.md deleted file mode 100644 index 314475b45a8..00000000000 --- a/modules/dgkeywordRtdProvider.md +++ /dev/null @@ -1,29 +0,0 @@ - ## Integration - -1) Compile the Digital Garage Keyword Module and Appnexus Bid Adapter into your Prebid build: - -``` -gulp build --modules="dgkeywordRtdProvider,appnexusBidAdapter,..." -``` - -2) Use `setConfig` to instruct Prebid.js to initilize the dgkeyword module, as specified below. - -## Configuration - -This module is configured as part of the `realTimeData.dataProviders` - -```javascript -var DGKEYWORD_TIMEOUT = 1000; -pbjs.setConfig({ - realTimeData: { - auctionDelay: DGKEYWORD_TIMEOUT, - dataProviders: [{ - name: 'dgkeyword', - waitForIt: true, - params: { - timeout: DGKEYWORD_TIMEOUT - } - }] - } -}); -``` diff --git a/test/spec/modules/dgkeywordRtdProvider_spec.js b/test/spec/modules/dgkeywordRtdProvider_spec.js deleted file mode 100644 index 0fac92e5118..00000000000 --- a/test/spec/modules/dgkeywordRtdProvider_spec.js +++ /dev/null @@ -1,326 +0,0 @@ -import * as dgRtd from 'modules/dgkeywordRtdProvider.js'; -import { cloneDeep } from 'lodash'; -import { server } from 'test/mocks/xhr.js'; -import { getGlobal } from 'src/prebidGlobal.js'; -import { config } from 'src/config.js'; - -const DG_GET_KEYWORDS_TIMEOUT = 1950; -const DEF_CONFIG = { - name: 'dgkeyword', - waitForIt: true, - params: { - timeout: DG_GET_KEYWORDS_TIMEOUT, - }, -}; -const DUMMY_RESPONSE_HEADER = {'Content-Type': 'application/json'}; -const DUMMY_RESPONSE = { s: ['s1', 's2'], t: ['t1', 't2'] }; -const SUCCESS_RESULT = { opeaud: ['s1', 's2'], opectx: ['t1', 't2'] }; -const SUCCESS_ORTB2 = { ortb2: { site: { keywords: SUCCESS_RESULT }, user: { keywords: SUCCESS_RESULT } } }; - -describe('Digital Garage Keyword Module', function () { - it('should init and return always true', function () { - expect(dgRtd.dgkeywordSubmodule.init()).to.equal(true); - }); - - describe('dgkeyword target test', function () { - it('should have no target', function () { - const adUnits_no_target = [ - { - code: 'code1', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999999, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999998, - dgkeyword: false, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999997, - }, - }, - ], - }, - { - code: 'code2', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999996, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999995, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999994, - }, - }, - ], - }, - ]; - expect(dgRtd.getTargetBidderOfDgKeywords(adUnits_no_target)).an('array') - .that.is.empty; - }); - it('should have targets', function () { - const adUnits_targets = [ - { - code: 'code1', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999999, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999998, - dgkeyword: true, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999997, - dgkeyword: false, - }, - }, - ], - }, - { - code: 'code2', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999996, - dgkeyword: true, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999995, - dgkeyword: 'aa', - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999994, - dgkeyword: true, - }, - }, - ], - }, - ]; - const targets = dgRtd.getTargetBidderOfDgKeywords(adUnits_targets); - expect(targets[0].bidder).to.be.equal('dg2'); - expect(targets[0].params.placementId).to.be.equal(99999998); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].bidder).to.be.equal('dg'); - expect(targets[1].params.placementId).to.be.equal(99999996); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - }); - }); - - describe('get profile.', function () { - const AD_UNITS = [ - { - code: 'code1', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999999, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999998, - dgkeyword: true, - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999997, - dgkeyword: false, - }, - }, - ], - }, - { - code: 'code2', - mediaTypes: { - banner: { - sizes: [[300, 250]], - }, - }, - bids: [ - { - bidder: 'dg', - params: { - placementId: 99999996, - dgkeyword: true, - }, - }, - { - bidder: 'dg2', - params: { - placementId: 99999995, - dgkeyword: 'aa', - }, - }, - { - bidder: 'dg3', - params: { - placementId: 99999994, - }, - }, - ], - }, - ]; - it('should get profiles error(404).', function (done) { - let pdjs = getGlobal(); - pbjs.adUnits = cloneDeep(AD_UNITS); - let moduleConfig = cloneDeep(DEF_CONFIG); - dgRtd.getDgKeywordsAndSet( - pdjs, - () => { - let targets = pbjs.adUnits[0].bids; - expect(targets[1].bidder).to.be.equal('dg2'); - expect(targets[1].params.placementId).to.be.equal(99999998); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.keywords).to.be.an('undefined'); - targets = pbjs.adUnits[1].bids; - expect(targets[0].bidder).to.be.equal('dg'); - expect(targets[0].params.placementId).to.be.equal(99999996); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.keywords).to.be.an('undefined'); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.keywords).to.be.an('undefined'); - - expect(config.getBidderConfig()).to.be.deep.equal({}); - - done(); - }, - moduleConfig, - null - ); - const request = server.requests[0]; - request.respond(404); - }); - it('should get profiles timeout.', function (done) { - let pdjs = getGlobal(); - pbjs.adUnits = cloneDeep(AD_UNITS); - let moduleConfig = cloneDeep(DEF_CONFIG); - moduleConfig.params.timeout = 10; - dgRtd.getDgKeywordsAndSet( - pdjs, - () => { - let targets = pbjs.adUnits[0].bids; - expect(targets[1].bidder).to.be.equal('dg2'); - expect(targets[1].params.placementId).to.be.equal(99999998); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.keywords).to.be.an('undefined'); - targets = pbjs.adUnits[1].bids; - expect(targets[0].bidder).to.be.equal('dg'); - expect(targets[0].params.placementId).to.be.equal(99999996); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.keywords).to.be.an('undefined'); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.keywords).to.be.an('undefined'); - - expect(config.getBidderConfig()).to.be.deep.equal({}); - - done(); - }, - moduleConfig, - null - ); - setTimeout(() => { - const request = server.requests[0]; - if (request) { - request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); - } - }, 1000) - }); - it('should get profiles ok(200).', function (done) { - let pdjs = getGlobal(); - pbjs.adUnits = cloneDeep(AD_UNITS); - let moduleConfig = cloneDeep(DEF_CONFIG); - dgRtd.getDgKeywordsAndSet(pdjs, () => { - let targets = pbjs.adUnits[0].bids; - expect(targets[1].bidder).to.be.equal('dg2'); - expect(targets[1].params.placementId).to.be.equal(99999998); - expect(targets[1].params.dgkeyword).to.be.an('undefined'); - expect(targets[1].params.keywords).to.be.deep.equal(SUCCESS_RESULT); - targets = pbjs.adUnits[1].bids; - expect(targets[0].bidder).to.be.equal('dg'); - expect(targets[0].params.placementId).to.be.equal(99999996); - expect(targets[0].params.dgkeyword).to.be.an('undefined'); - expect(targets[0].params.keywords).to.be.deep.equal(SUCCESS_RESULT); - expect(targets[2].bidder).to.be.equal('dg3'); - expect(targets[2].params.placementId).to.be.equal(99999994); - expect(targets[2].params.dgkeyword).to.be.an('undefined'); - expect(targets[2].params.keywords).to.be.an('undefined'); - - // expect(config.getBidderConfig()).to.be.deep.equal({ dg2: SUCCESS_ORTB2, dg: SUCCESS_ORTB2 }); - - done(); - }, moduleConfig, null); - const request = server.requests[0]; - request.respond(200, DUMMY_RESPONSE_HEADER, JSON.stringify(DUMMY_RESPONSE)); - }); - }); -}); From 1fe48accb649eb0296c060a647a87b5e260b2b17 Mon Sep 17 00:00:00 2001 From: pro-nsk <32703851+pro-nsk@users.noreply.github.com> Date: Tue, 1 Jun 2021 02:30:44 +0700 Subject: [PATCH 35/63] Qwarry Bid Adapter: meta.advertiserDomains added (#6883) * qwarry bid adapter * formatting fixes * fix tests for qwarry * qwarry bid adapter * add header for qwarry bid adapter * bid requests fix * fix tests * response fix * fix tests for Qwarry bid adapter * add pos parameter to qwarry bid adapter * qwarryBidAdapter onBidWon hotfix * Change bidder endpoint url for Qwarry adapter * add referer JS detection * use bidderRequest.refererInfo * fix tests * GDPR consent string support * NPE fix * gdpr value added * merge master * gdpr value added * qwarry bid adapter: add tests * Qwarry bid adapter: remove gdpr field from request * qwarry bid adapter: add sizes * qwarry bid adapter: add sizes * added schain * added test for schain * added supporting of advertiserDomains * qwarry bid adapter: added meta.advertiserDomains Co-authored-by: Artem Kostritsa Co-authored-by: Alexander Kascheev Co-authored-by: Ekaterina Legostaeva --- modules/qwarryBidAdapter.js | 3 +++ test/spec/modules/qwarryBidAdapter_spec.js | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/modules/qwarryBidAdapter.js b/modules/qwarryBidAdapter.js index a410c2fe163..c9a86f73910 100644 --- a/modules/qwarryBidAdapter.js +++ b/modules/qwarryBidAdapter.js @@ -75,6 +75,9 @@ export const spec = { bid.vastXml = bid.ad; } + bid.meta = {}; + bid.meta.advertiserDomains = bid.adomain || []; + bids.push(bid); }) diff --git a/test/spec/modules/qwarryBidAdapter_spec.js b/test/spec/modules/qwarryBidAdapter_spec.js index 6dbb983ea23..560206681ee 100644 --- a/test/spec/modules/qwarryBidAdapter_spec.js +++ b/test/spec/modules/qwarryBidAdapter_spec.js @@ -33,7 +33,8 @@ const BIDDER_BANNER_RESPONSE = { 'creativeId': 1, 'netRevenue': true, 'winUrl': 'http://test.com', - 'format': 'banner' + 'format': 'banner', + 'adomain': ['test.com'] }] } @@ -49,7 +50,8 @@ const BIDDER_VIDEO_RESPONSE = { 'creativeId': 2, 'netRevenue': true, 'winUrl': 'http://test.com', - 'format': 'video' + 'format': 'video', + 'adomain': ['test.com'] }] } @@ -119,6 +121,8 @@ describe('qwarryBidAdapter', function () { expect(result[0]).to.have.property('netRevenue').equal(true) expect(result[0]).to.have.property('winUrl').equal('http://test.com') expect(result[0]).to.have.property('format').equal('banner') + expect(result[0].meta).to.exist.property('advertiserDomains') + expect(result[0].meta).to.have.property('advertiserDomains').lengthOf(1) }) it('handles video request : should get correct bid response', function () { @@ -136,6 +140,8 @@ describe('qwarryBidAdapter', function () { expect(result[0]).to.have.property('winUrl').equal('http://test.com') expect(result[0]).to.have.property('format').equal('video') expect(result[0]).to.have.property('vastXml').equal('vast') + expect(result[0].meta).to.exist.property('advertiserDomains') + expect(result[0].meta).to.have.property('advertiserDomains').lengthOf(1) }) it('handles no bid response : should get empty array', function () { From 9859d199887501c4fccca1ff3b7d036b02466127 Mon Sep 17 00:00:00 2001 From: Rich Audience Date: Tue, 1 Jun 2021 11:43:17 +0200 Subject: [PATCH 36/63] Richaudience Bid Adapter: add adomain support (#6880) * RichaudienceBidAdapter Add adomine for Prebid 5.0 * new pull * new pull 2 * New Pull 3 * Add unit test * New push 1 * New push 2 Co-authored-by: sgimenez --- modules/richaudienceBidAdapter.js | 3 ++- test/spec/modules/richaudienceBidAdapter_spec.js | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/richaudienceBidAdapter.js b/modules/richaudienceBidAdapter.js index 5e2a5e1bff5..4b556e83236 100755 --- a/modules/richaudienceBidAdapter.js +++ b/modules/richaudienceBidAdapter.js @@ -97,7 +97,8 @@ export const spec = { netRevenue: response.netRevenue, currency: response.currency, ttl: response.ttl, - dealId: response.dealId, + meta: response.adomain, + dealId: response.dealId }; if (response.media_type === 'video') { diff --git a/test/spec/modules/richaudienceBidAdapter_spec.js b/test/spec/modules/richaudienceBidAdapter_spec.js index 72410b71fb2..00ae55823b0 100644 --- a/test/spec/modules/richaudienceBidAdapter_spec.js +++ b/test/spec/modules/richaudienceBidAdapter_spec.js @@ -149,8 +149,8 @@ describe('Richaudience adapter tests', function () { netRevenue: true, currency: 'USD', ttl: 300, - dealId: 'dealId' - + dealId: 'dealId', + adomain: 'richaudience.com' } }; @@ -165,7 +165,8 @@ describe('Richaudience adapter tests', function () { currency: 'USD', ttl: 300, vastXML: '', - dealId: 'dealId' + dealId: 'dealId', + adomain: 'richaudience.com' } }; @@ -600,6 +601,7 @@ describe('Richaudience adapter tests', function () { expect(bid.currency).to.equal('USD'); expect(bid.ttl).to.equal(300); expect(bid.dealId).to.equal('dealId'); + expect(bid.meta).to.equal('richaudience.com'); }); it('no banner media response inestream', function () { @@ -628,6 +630,7 @@ describe('Richaudience adapter tests', function () { expect(bid.currency).to.equal('USD'); expect(bid.ttl).to.equal(300); expect(bid.dealId).to.equal('dealId'); + expect(bid.meta).to.equal('richaudience.com'); }); it('no banner media response outstream', function () { From 7d02e2f7d596fcb26186c4bdf185defbf5589e31 Mon Sep 17 00:00:00 2001 From: hybrid-ai <58724131+hybrid-ai@users.noreply.github.com> Date: Tue, 1 Jun 2021 13:18:13 +0300 Subject: [PATCH 37/63] Hybrid Bid Adapter: add placeholder for advertiserDomains support (#6885) * Add adomains stub. * Hybrid Bid Adapter: add unit test for advertiserDomains. Co-authored-by: Petrov Denis --- modules/hybridBidAdapter.js | 5 ++++- test/spec/modules/hybridBidAdapter_spec.js | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/hybridBidAdapter.js b/modules/hybridBidAdapter.js index e7281086a92..15f8acd824f 100644 --- a/modules/hybridBidAdapter.js +++ b/modules/hybridBidAdapter.js @@ -78,7 +78,10 @@ function buildBid(bidData) { creativeId: bidData.bidId, currency: bidData.currency, netRevenue: true, - ttl: TTL + ttl: TTL, + meta: { + advertiserDomains: bidData.advertiserDomains || [], + } }; if (bidData.placement === PLACEMENT_TYPE_VIDEO) { diff --git a/test/spec/modules/hybridBidAdapter_spec.js b/test/spec/modules/hybridBidAdapter_spec.js index d1899ef3d81..ffbc27293fb 100644 --- a/test/spec/modules/hybridBidAdapter_spec.js +++ b/test/spec/modules/hybridBidAdapter_spec.js @@ -255,7 +255,8 @@ describe('Hybrid.ai Adapter', function() { currency: 'USD', content: 'html', width: 100, - height: 100 + height: 100, + advertiserDomains: ['hybrid.ai'] } ] } @@ -269,6 +270,7 @@ describe('Hybrid.ai Adapter', function() { expect(bids[0].height).to.equal(100) expect(bids[0].currency).to.equal('USD') expect(bids[0].netRevenue).to.equal(true) + expect(bids[0].meta.advertiserDomains).to.deep.equal(['hybrid.ai']) expect(typeof bids[0].ad).to.equal('string') }) it('should return a In-Image bid', function() { From d7d5f62f9e3f14d0bf6aaed31c6f5c549fb30faa Mon Sep 17 00:00:00 2001 From: hybrid-ai <58724131+hybrid-ai@users.noreply.github.com> Date: Tue, 1 Jun 2021 13:27:07 +0300 Subject: [PATCH 38/63] Vox Bid Adapter: Add placeholder to pass advertiser domains (#6884) * Add stub to pass adomains. * Vox Bid Adapter: add unit test for advertiserDomains. Co-authored-by: Petrov Denis --- modules/voxBidAdapter.js | 5 ++++- test/spec/modules/voxBidAdapter_spec.js | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index 73df9bb8b9b..ba469c3c7ed 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -70,7 +70,10 @@ function buildBid(bidData) { netRevenue: true, mediaType: BANNER, ttl: TTL, - content: bidData.content + content: bidData.content, + meta: { + advertiserDomains: bidData.advertiserDomains || [], + } }; if (bidData.placement === 'video') { diff --git a/test/spec/modules/voxBidAdapter_spec.js b/test/spec/modules/voxBidAdapter_spec.js index c6221cba9e5..6906c7dbba4 100644 --- a/test/spec/modules/voxBidAdapter_spec.js +++ b/test/spec/modules/voxBidAdapter_spec.js @@ -243,7 +243,8 @@ describe('VOX Adapter', function() { content: 'html', width: 100, height: 100 - } + }, + advertiserDomains: ['voxexchange.io'] } ] } @@ -257,6 +258,7 @@ describe('VOX Adapter', function() { expect(bids[0].height).to.equal(100) expect(bids[0].currency).to.equal('USD') expect(bids[0].netRevenue).to.equal(true) + expect(bids[0].meta.advertiserDomains).to.deep.equal(['voxexchange.io']) expect(typeof bids[0].ad).to.equal('string') }) it('should return a In-Image bid', function() { From 5c9e149ecc83ccbda53673c0a1e4cf680766e56d Mon Sep 17 00:00:00 2001 From: rtuschkany <35923908+rtuschkany@users.noreply.github.com> Date: Tue, 1 Jun 2021 14:20:06 +0200 Subject: [PATCH 39/63] ConnectAd Bid-Adapter: Add adomain support (#6859) * ConnectAd bid adapter: Add adomain support Add adomain support * ConnectAd Bid-Adapter: Add adomain support fix * ConnectAd Bid-Adapter: Add adomain support * ConnectAd Bid-Adapter: Add adomain support test update --- modules/connectadBidAdapter.js | 1 + test/spec/modules/connectadBidAdapter_spec.js | 39 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/modules/connectadBidAdapter.js b/modules/connectadBidAdapter.js index 4fa2a56a004..111b6ac10e8 100644 --- a/modules/connectadBidAdapter.js +++ b/modules/connectadBidAdapter.js @@ -124,6 +124,7 @@ export const spec = { bid.width = decision.width; bid.height = decision.height; bid.dealid = decision.dealid || null; + bid.meta = { advertiserDomains: decision && decision.adomain ? decision.adomain : [] }; bid.ad = retrieveAd(decision); bid.currency = 'USD'; bid.creativeId = decision.adId; diff --git a/test/spec/modules/connectadBidAdapter_spec.js b/test/spec/modules/connectadBidAdapter_spec.js index dbac3c0dc7c..657bc432d06 100644 --- a/test/spec/modules/connectadBidAdapter_spec.js +++ b/test/spec/modules/connectadBidAdapter_spec.js @@ -303,7 +303,43 @@ describe('ConnectAd Adapter', function () { }); describe('bid responses', function () { - it('should return complete bid response', function () { + it('should return complete bid response with adomain', function () { + const ADOMAINS = ['connectad.io']; + + let serverResponse = { + body: { + decisions: { + '2f95c00074b931': { + adId: '0', + adomain: ['connectad.io'], + contents: [ + { + body: '<<<---- Creative --->>>' + } + ], + height: '250', + width: '300', + pricing: { + clearPrice: 11.899999999999999 + } + } + } + } + }; + const request = spec.buildRequests(bidRequests, bidderRequest); + const bids = spec.interpretResponse(serverResponse, request); + + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.equal(11.899999999999999); + expect(bids[0].width).to.equal('300'); + expect(bids[0].height).to.equal('250'); + expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].meta.advertiserDomains).to.deep.equal(ADOMAINS); + }); + + it('should return complete bid response with empty adomain', function () { + const ADOMAINS = []; + let serverResponse = { body: { decisions: { @@ -331,6 +367,7 @@ describe('ConnectAd Adapter', function () { expect(bids[0].width).to.equal('300'); expect(bids[0].height).to.equal('250'); expect(bids[0].ad).to.have.length.above(1); + expect(bids[0].meta.advertiserDomains).to.deep.equal(ADOMAINS); }); it('should return empty bid response', function () { From 4827f16e224813c338523b2f8e323e57abf28e27 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Tue, 1 Jun 2021 15:26:25 +0300 Subject: [PATCH 40/63] Aniview Bid Adapter: added meta.advertiserDomains to bidResponse and extended cookie sync logic (#6858) * Support new aniview bid adapter * Support new aniview bid adapter * Support new aniview bid adapter * Support new aniview bid adapter * Fix Consent parameters * Update aniviewBidAdapter.js V3 support * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js Update refererInfo * Update aniviewBidAdapter.js Fix tabs and spaces * Update aniviewBidAdapter.js fixes * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js Add ccpa support * Update aniviewBidAdapter.js Typo * Update aniviewBidAdapter.js * Update aniviewBidAdapter.js * Fix size and sample Fixed sizes from playerSize Updated md sample * Fix tabs * Fix sizes * Recheck * Add tgt parameter * Update sample * Add support for cookie sync + tests * Add support for cookie sync + tests * Add support for cookie sync + tests * Support aliases Support aliases * Update Update * Fix lint Fix lint * Update spec Update spec * Aniview Bid Adapter: Added the new alias * Aniview Bid Adapter: Added the new configs for the renderer * Aniview Bid Adapter: Added unit tests for the renderer * Aniview Bid Adapter: Have added gvlid * Aniview Bid Adapter: added meta.advertiserDomains to bidResponse and extended cookie sync logic Co-authored-by: Itay Nave Co-authored-by: Itay Nave <38345760+itaynave@users.noreply.github.com> --- modules/aniviewBidAdapter.js | 9 ++++++- test/spec/modules/aniviewBidAdapter_spec.js | 27 ++++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/modules/aniviewBidAdapter.js b/modules/aniviewBidAdapter.js index 6b83c40897e..b37d105da1a 100644 --- a/modules/aniviewBidAdapter.js +++ b/modules/aniviewBidAdapter.js @@ -201,6 +201,10 @@ function interpretResponse(serverResponse, bidRequest) { bidResponse.vastUrl = window.URL.createObjectURL(blob); bidResponse.vastXml = xmlStr; bidResponse.mediaType = VIDEO; + bidResponse.meta = { + advertiserDomains: [] + }; + if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && bidRequest.bidRequest.mediaTypes.video && bidRequest.bidRequest.mediaTypes.video.context === 'outstream') { bidResponse.renderer = newRenderer(bidRequest); } bidResponses.push(bidResponse); @@ -230,7 +234,10 @@ function getSyncData(xml, options) { if (data && data.trackers && data.trackers.length) { data = data.trackers; for (var j = 0; j < data.length; j++) { - if (typeof data[j] === 'object' && typeof data[j].url === 'string' && data[j].e === 'inventory') { + if (typeof data[j] === 'object' && + typeof data[j].url === 'string' && + (data[j].e === 'inventory' || data[j].e === 'sync') + ) { if (data[j].t == 1 && options.pixelEnabled) { ret.push({url: data[j].url, type: 'image'}); } else { diff --git a/test/spec/modules/aniviewBidAdapter_spec.js b/test/spec/modules/aniviewBidAdapter_spec.js index 2e1fdb56201..b6d63fc2a8e 100644 --- a/test/spec/modules/aniviewBidAdapter_spec.js +++ b/test/spec/modules/aniviewBidAdapter_spec.js @@ -160,6 +160,7 @@ describe('ANIVIEW Bid Adapter Test', function () { expect(bidResponse.currency).to.equal('USD'); expect(bidResponse.netRevenue).to.equal(true); expect(bidResponse.mediaType).to.equal('video'); + expect(bidResponse.meta.advertiserDomains).to.be.an('array').that.is.empty; }); it('safely handles XML parsing failure from invalid bid response', function () { @@ -207,12 +208,16 @@ describe('ANIVIEW Bid Adapter Test', function () { }); describe('getUserSyncs', function () { - it('Check get sync pixels from response', function () { - let pixelUrl = 'https://sync.pixel.url/sync'; + let pixelUrl = 'https://sync.pixel.url/sync'; + function createBidResponse (pixelEvent, pixelType) { + let pixelStr = '{"url":"' + pixelUrl + '", "e":"' + pixelEvent + '", "t":' + pixelType + '}'; + return 'FORDFORD00:00:15'; + } + + it('Check get iframe sync pixels from response on inventory', function () { let pixelEvent = 'inventory'; let pixelType = '3'; - let pixelStr = '{"url":"' + pixelUrl + '", "e":"' + pixelEvent + '", "t":' + pixelType + '}'; - let bidResponse = 'FORDFORD00:00:15'; + let bidResponse = createBidResponse(pixelEvent, pixelType); let serverResponse = [ {body: bidResponse} ]; @@ -222,5 +227,19 @@ describe('ANIVIEW Bid Adapter Test', function () { expect(pixel.url).to.equal(pixelUrl); expect(pixel.type).to.equal('iframe'); }); + + it('Check get image sync pixels from response on sync', function () { + let pixelEvent = 'sync'; + let pixelType = '1'; + let bidResponse = createBidResponse(pixelEvent, pixelType); + let serverResponse = [ + {body: bidResponse} + ]; + let syncPixels = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: true}, serverResponse); + expect(syncPixels.length).to.equal(1); + let pixel = syncPixels[0]; + expect(pixel.url).to.equal(pixelUrl); + expect(pixel.type).to.equal('image'); + }); }); }); From d7a1418092fc087ecc8bd9679c257560ed338e4f Mon Sep 17 00:00:00 2001 From: hendrikiseke1979 <53309111+hendrikiseke1979@users.noreply.github.com> Date: Tue, 1 Jun 2021 15:02:02 +0200 Subject: [PATCH 41/63] OR Bid Adapter: add support for advertiserDomains and floors module (#6890) * orbidder adapter: add withCredentials:true header to BidRequest and onBidWon Requests * add blank in order to trigger build again * remove blank to trigger build ... again * adding extra line to trigger build ... again * add prebid version to request * add unit test for version parameter * add version parameter to win requests * fix comment * trigger rebuild * trigger rebuild * remove onBidWon callback from adapter * fix retrieving orbidder endpoint url from local storage * fix unit tests * use getBidFloor function for prebidv5 compatibility * fill meta.advertiserDomains from serverResponse.adomain for prebidv5 compatibility * drop eslint-disable * switch misleading expect and actual in unit test * cr: rename adomain to advertiserDomains * trigger build pipeline * trigger build pipeline Co-authored-by: Volk, Rainer Co-authored-by: RainerVolk4014 <53347752+RainerVolk4014@users.noreply.github.com> Co-authored-by: siggi-otto <57615762+siggi-otto@users.noreply.github.com> Co-authored-by: Hendrik Iseke <39734979+hiseke@users.noreply.github.com> Co-authored-by: Hendrik Iseke Co-authored-by: rvolk <> Co-authored-by: Arne Schulz --- modules/orbidderBidAdapter.js | 31 ++++++++++++++ test/spec/modules/orbidderBidAdapter_spec.js | 43 +++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/modules/orbidderBidAdapter.js b/modules/orbidderBidAdapter.js index e01746af487..4a7d686a7bc 100644 --- a/modules/orbidderBidAdapter.js +++ b/modules/orbidderBidAdapter.js @@ -1,5 +1,6 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { getStorageManager } from '../src/storageManager.js'; +import * as utils from '../src/utils.js'; const storageManager = getStorageManager(); @@ -32,6 +33,8 @@ export const spec = { referer = bidderRequest.refererInfo.referer || ''; } + bidRequest.params.bidfloor = getBidFloor(bidRequest); + const ret = { url: `${hostname}/bid`, method: 'POST', @@ -70,6 +73,13 @@ export const spec = { } bidResponse[requiredKey] = bid[requiredKey]; } + + if (Array.isArray(bid.advertiserDomains)) { + bidResponse.meta = { + advertiserDomains: bid.advertiserDomains + } + } + bidResponses.push(bidResponse); }); } @@ -77,4 +87,25 @@ export const spec = { }, }; +/** + * Get bid floor from Price Floors Module + * @param {Object} bid + * @returns {float||undefined} + */ +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return bid.params.bidfloor; + } + + const floor = bid.getFloor({ + currency: 'EUR', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'EUR') { + return floor.floor; + } + return undefined; +} + registerBidder(spec); diff --git a/test/spec/modules/orbidderBidAdapter_spec.js b/test/spec/modules/orbidderBidAdapter_spec.js index df551311c0b..42cc25ae04f 100644 --- a/test/spec/modules/orbidderBidAdapter_spec.js +++ b/test/spec/modules/orbidderBidAdapter_spec.js @@ -104,7 +104,7 @@ describe('orbidderBidAdapter', () => { expect(request.data.pageUrl).to.equal('https://localhost:9876/'); // expect(request.data.referrer).to.equal(''); Object.keys(defaultBidRequest).forEach((key) => { - expect(defaultBidRequest[key]).to.equal(request.data[key]); + expect(request.data[key]).to.deep.equal(defaultBidRequest[key]); }); }); @@ -189,6 +189,47 @@ describe('orbidderBidAdapter', () => { }); }); + it('should get correct bid response with advertiserDomains', () => { + const serverResponse = [ + { + 'width': 300, + 'height': 250, + 'creativeId': '29681110', + 'ad': '', + 'cpm': 0.5, + 'requestId': '30b31c1838de1e', + 'ttl': 60, + 'netRevenue': true, + 'currency': 'EUR', + 'advertiserDomains': ['cm.tavira.pt'] + } + ]; + + const expectedResponse = [ + { + 'requestId': '30b31c1838de1e', + 'cpm': 0.5, + 'creativeId': '29681110', + 'width': 300, + 'height': 250, + 'ttl': 60, + 'currency': 'EUR', + 'ad': '', + 'netRevenue': true, + 'meta': { + 'advertiserDomains': ['cm.tavira.pt'] + } + } + ]; + + const result = spec.interpretResponse({body: serverResponse}); + + expect(result.length).to.equal(expectedResponse.length); + Object.keys(expectedResponse[0]).forEach((key) => { + expect(result[0][key]).to.deep.equal(expectedResponse[0][key]); + }); + }); + it('handles broken server response', () => { const serverResponse = [ { From d779cdc9a6a8baf43b559d740429d5b2da77f992 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Tue, 1 Jun 2021 16:11:28 +0300 Subject: [PATCH 42/63] AdkernelAdn: meta fields support (#6899) --- modules/adkernelAdnBidAdapter.js | 30 +++++++++++++++++-- .../modules/adkernelAdnBidAdapter_spec.js | 19 ++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index 0de750c773f..dc56ed6abbb 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -103,13 +103,17 @@ function buildBid(tag) { requestId: tag.impid, bidderCode: spec.code, cpm: tag.bid, - width: tag.w, - height: tag.h, creativeId: tag.crid, currency: 'USD', ttl: 720, netRevenue: true }; + if (tag.w) { + bid.width = tag.w; + } + if (tag.h) { + bid.height = tag.h; + } if (tag.tag) { bid.ad = tag.tag; bid.mediaType = BANNER; @@ -117,9 +121,31 @@ function buildBid(tag) { bid.vastUrl = tag.vast_url; bid.mediaType = VIDEO; } + fillBidMeta(bid, tag); return bid; } +function fillBidMeta(bid, tag) { + if (utils.isStr(tag.agencyName)) { + utils.deepSetValue(bid, 'meta.agencyName', tag.agencyName); + } + if (utils.isNumber(tag.advertiserId)) { + utils.deepSetValue(bid, 'meta.advertiserId', tag.advertiserId); + } + if (utils.isStr(tag.advertiserName)) { + utils.deepSetValue(bid, 'meta.advertiserName', tag.advertiserName); + } + if (utils.isArray(tag.advertiserDomains)) { + utils.deepSetValue(bid, 'meta.advertiserDomains', tag.advertiserDomains); + } + if (utils.isStr(tag.primaryCatId)) { + utils.deepSetValue(bid, 'meta.primaryCatId', tag.primaryCatId); + } + if (utils.isArray(tag.secondaryCatIds)) { + utils.deepSetValue(bid, 'meta.secondaryCatIds', tag.secondaryCatIds); + } +} + function getBidFloor(bid, mediaType, sizes) { var floor; var size = sizes.length === 1 ? sizes[0] : '*'; diff --git a/test/spec/modules/adkernelAdnBidAdapter_spec.js b/test/spec/modules/adkernelAdnBidAdapter_spec.js index d3c9a2cf816..c4ad134711a 100644 --- a/test/spec/modules/adkernelAdnBidAdapter_spec.js +++ b/test/spec/modules/adkernelAdnBidAdapter_spec.js @@ -107,7 +107,12 @@ describe('AdkernelAdn adapter', function () { bid: 5.0, tag: '', w: 300, - h: 250 + h: 250, + advertiserId: 777, + advertiserName: 'advertiser', + agencyName: 'agency', + advertiserDomains: ['example.com'], + primaryCatId: 'IAB1-1', }, { id: 'ad-unit-2', impid: '31d798477126c4', @@ -115,7 +120,12 @@ describe('AdkernelAdn adapter', function () { bid: 2.5, tag: '', w: 300, - h: 250 + h: 250, + advertiserId: 777, + advertiserName: 'advertiser', + agencyName: 'agency', + advertiserDomains: ['example.com'], + secondaryCatIds: ['IAB1-4', 'IAB8-16', 'IAB25-5'] }, { id: 'video_wrapper', impid: '57d602ad1c9545', @@ -375,6 +385,11 @@ describe('AdkernelAdn adapter', function () { expect(resp).to.have.property('mediaType', 'banner'); expect(resp).to.have.property('ad'); expect(resp.ad).to.have.string(''); + expect(resp.meta.advertiserId).to.be.eql(777); + expect(resp.meta.advertiserName).to.be.eql('advertiser'); + expect(resp.meta.agencyName).to.be.eql('agency'); + expect(resp.meta.advertiserDomains).to.be.eql(['example.com']); + expect(resp.meta.primaryCatId).to.be.eql('IAB1-1'); }); it('should return fully-initialized video bid-response', function () { From 1bc1427cb62cd3057eeb1e621107f70e148cad51 Mon Sep 17 00:00:00 2001 From: BrightMountainMedia <69471268+BrightMountainMediaInc@users.noreply.github.com> Date: Tue, 1 Jun 2021 18:44:12 +0530 Subject: [PATCH 43/63] BrightMountainMedia Bid Adapter: add floors module support (#6833) * Update BrightMountainMedia cookie sync URL * Bright Mountain Media: Update bidder code * Bright Mountain Media: Add brightmountainmedia as alias * Bright Mountain Media: Update Bid Endpoint * BrightMountainMedia Bid Adapter: add floors module support * BrightMountainMedia Bid Adapter: support advertiserDomains --- modules/brightMountainMediaBidAdapter.js | 36 +++++++++++++- .../brightMountainMediaBidAdapter_spec.js | 49 ++++++++++++++++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/modules/brightMountainMediaBidAdapter.js b/modules/brightMountainMediaBidAdapter.js index 5539004bdcd..531238c7400 100644 --- a/modules/brightMountainMediaBidAdapter.js +++ b/modules/brightMountainMediaBidAdapter.js @@ -58,6 +58,7 @@ export const spec = { let placement = { placementId: bid.params.placement_id, bidId: bid.bidId, + floor: {}, }; if (bid.mediaTypes.hasOwnProperty(BANNER)) { @@ -87,6 +88,23 @@ export const spec = { placement['playbackmethod'] = bid.mediaTypes.video.playbackmethod; } } + + if (typeof bid.getFloor === 'function') { + let floorInfo = {}; + + for (let size of placement['sizes']) { + floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: placement['traffic'], + size: size, + }); + + if (typeof floorInfo === 'object' && floorInfo.currency === 'USD') { + placement.floor[`${size[0]}x${size[1]}`] = parseFloat(floorInfo.floor); + } + } + } + if (bid.schain) { placement.schain = bid.schain; } @@ -105,10 +123,26 @@ export const spec = { if (response && Array.isArray(response) && response.length > 0) { for (let i = 0; i < response.length; i++) { if (response[i].cpm > 0) { + const tempResponse = { + requestId: response[i].requestId, + width: response[i].width, + height: response[i].height, + cpm: response[i].cpm, + mediaType: response[i].mediaType, + creativeId: response[i].creativeId, + currency: response[i].currency, + netRevenue: response[i].netRevenue, + ttl: response[i].ttl, + ad: response[i].ad, + meta: { + advertiserDomains: response[i].adomain && response[i].adomain.length ? response[i].adomain : [], + } + }; + if (response[i].mediaType && response[i].mediaType === 'video') { response[i].vastXml = response[i].ad; } - bidResponse.push(response[i]); + bidResponse.push(tempResponse); } } } diff --git a/test/spec/modules/brightMountainMediaBidAdapter_spec.js b/test/spec/modules/brightMountainMediaBidAdapter_spec.js index 6c7ef816f4f..17f23c5acd3 100644 --- a/test/spec/modules/brightMountainMediaBidAdapter_spec.js +++ b/test/spec/modules/brightMountainMediaBidAdapter_spec.js @@ -103,6 +103,48 @@ describe('brightMountainMediaBidAdapter_spec', function () { let serverRequest = spec.buildRequests([bidBanner], bidderRequest); testServerRequestBody(serverRequest); + it('sends bidfloor param if present', function () { + bidBanner.getFloor = function () { + return { + currency: 'USD', + floor: 0.5, + } + }; + const request = spec.buildRequests([bidBanner], bidderRequest); + expect(request.data.placements[0].floor['300x250']).to.equal(0.5); + }); + + it('sends gdpr info if exists', function () { + const gdprConsent = { + consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==', + gdprApplies: true + }; + + bidderRequest['gdprConsent'] = gdprConsent; + const request = spec.buildRequests([bidBanner], bidderRequest); + + expect(request.data.gdpr_require).to.exist.and.to.be.a('number'); + expect(request.data.gdpr_consent).to.exist.and.to.be.a('string'); + }); + + it('sends schain info if exists', function () { + const schain = { + ver: '1.0', + complete: 1, + nodes: [ + { + asi: 'directseller.com', + sid: '00001', + rid: 'BidRequest1', + hp: 1 + } + ] + }; + bidBanner.schain = schain; + const request = spec.buildRequests([bidBanner], bidderRequest); + expect(request.data.placements[0].schain).to.be.an('object'); + }); + bidderRequest['bids'] = [bidVideo]; serverRequest = spec.buildRequests([bidVideo], bidderRequest); testServerRequestBody(serverRequest); @@ -129,6 +171,7 @@ describe('brightMountainMediaBidAdapter_spec', function () { expect(dataItem.netRevenue).to.be.a('boolean'); expect(dataItem.currency).to.be.a('string'); expect(dataItem.mediaType).to.be.a('string'); + expect(dataItem.meta.advertiserDomains[0]).to.be.a('string'); } }); } @@ -145,7 +188,8 @@ describe('brightMountainMediaBidAdapter_spec', function () { ttl: 1000, creativeId: '123asd', netRevenue: true, - currency: 'USD' + currency: 'USD', + adomain: ['adomain.com'], }] }; @@ -160,7 +204,8 @@ describe('brightMountainMediaBidAdapter_spec', function () { ttl: 1000, creativeId: '123asd', netRevenue: true, - currency: 'USD' + currency: 'USD', + adomain: ['adomain.com'], }] }; let serverResponses = spec.interpretResponse(resObjectBanner); From 7647b97f7a07412fc49f4cca5572efa741db8462 Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 1 Jun 2021 15:18:09 +0200 Subject: [PATCH 44/63] AdagioBidAdapter: support priceFloors module (#6867) --- modules/adagioBidAdapter.js | 57 ++++++++++++++++- test/spec/modules/adagioBidAdapter_spec.js | 71 ++++++++++++++++++++++ 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index aed1a1682dc..5598dc5d224 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -24,6 +24,8 @@ export const RENDERER_URL = 'https://script.4dex.io/outstream-player.js'; const MAX_SESS_DURATION = 30 * 60 * 1000; const ADAGIO_PUBKEY = 'AL16XT44Sfp+8SHVF1UdC7hydPSMVLMhsYknKDdwqq+0ToDSJrP0+Qh0ki9JJI2uYm/6VEYo8TJED9WfMkiJ4vf02CW3RvSWwc35bif2SK1L8Nn/GfFYr/2/GG/Rm0vUsv+vBHky6nuuYls20Og0HDhMgaOlXoQ/cxMuiy5QSktp'; const ADAGIO_PUBKEY_E = 65537; +const CURRENCY = 'USD'; +const DEFAULT_FLOOR = 0.1; // This provide a whitelist and a basic validation // of OpenRTB 2.5 options used by the Adagio SSP. @@ -819,6 +821,48 @@ function _parseNativeBidResponse(bid) { bid.native = native } +function _getFloors(bidRequest) { + if (!utils.isFn(bidRequest.getFloor)) { + return false; + } + + const floors = []; + + const getAndPush = (mediaType, size) => { + const info = bidRequest.getFloor({ + currency: CURRENCY, + mediaType, + size: [] + }); + + floors.push(utils.cleanObj({ + mt: mediaType, + s: utils.isArray(size) ? `${size[0]}x${size[1]}` : undefined, + f: (!isNaN(info.floor) && info.currency === CURRENCY) ? info.floor : DEFAULT_FLOOR + })); + } + + Object.keys(bidRequest.mediaTypes).forEach(mediaType => { + if (SUPPORTED_MEDIA_TYPES.indexOf(mediaType) !== -1) { + const sizeProp = mediaType === VIDEO ? 'playerSize' : 'sizes'; + + if (bidRequest.mediaTypes[mediaType][sizeProp] && bidRequest.mediaTypes[mediaType][sizeProp].length) { + if (utils.isArray(bidRequest.mediaTypes[mediaType][sizeProp][0])) { + bidRequest.mediaTypes[mediaType][sizeProp].forEach(size => { + getAndPush(mediaType, [size[0], size[1]]); + }); + } else { + getAndPush(mediaType, [bidRequest.mediaTypes[mediaType][sizeProp][0], bidRequest.mediaTypes[mediaType][sizeProp][1]]); + } + } else { + getAndPush(mediaType, '*'); + } + } + }); + + return floors; +} + export const spec = { code: BIDDER_CODE, gvlid: GVLID, @@ -914,6 +958,9 @@ export const spec = { const adUnits = utils._map(validBidRequests, (bidRequest) => { bidRequest.features = internal.getFeatures(bidRequest, bidderRequest); + // Handle priceFloors module + bidRequest.floors = _getFloors(bidRequest); + if (utils.deepAccess(bidRequest, 'mediaTypes.video')) { _buildVideoBidRequest(bidRequest); } @@ -923,10 +970,14 @@ export const spec = { // Group ad units by organizationId const groupedAdUnits = adUnits.reduce((groupedAdUnits, adUnit) => { - adUnit.params.organizationId = adUnit.params.organizationId.toString(); + const adUnitCopy = utils.deepClone(adUnit); + adUnitCopy.params.organizationId = adUnitCopy.params.organizationId.toString(); + + // remove useless props + delete adUnitCopy.floorData; - groupedAdUnits[adUnit.params.organizationId] = groupedAdUnits[adUnit.params.organizationId] || []; - groupedAdUnits[adUnit.params.organizationId].push(adUnit); + groupedAdUnits[adUnitCopy.params.organizationId] = groupedAdUnits[adUnitCopy.params.organizationId] || []; + groupedAdUnits[adUnitCopy.params.organizationId].push(adUnitCopy); return groupedAdUnits; }, {}); diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 6e0b82cbab8..4b66a96be16 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -740,6 +740,77 @@ describe('Adagio bid adapter', () => { expect(requests[0].data.user.eids).to.be.empty; }); }); + + describe('with priceFloors module', function() { + it('should get and set floor by mediatype and sizes', function() { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + playerSize: [600, 480] + } + } + }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); + + // delete the computed `sizes` prop as we are based on mediaTypes only. + delete bid01.sizes + + bid01.getFloor = () => { + return { floor: 1, currency: 'USD' } + } + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.adUnits[0].floors.length).to.equal(3); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 1, mt: 'banner', s: '300x250'}); + expect(requests[0].data.adUnits[0].floors[1]).to.deep.equal({f: 1, mt: 'banner', s: '300x600'}); + expect(requests[0].data.adUnits[0].floors[2]).to.deep.equal({f: 1, mt: 'video', s: '600x480'}); + }); + + it('should get and set floor by mediatype if no size provided (ex native, video)', function() { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + }, + native: { + body: { required: true } + } + } + }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); + bid01.getFloor = () => { + return { floor: 1, currency: 'USD' } + } + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.adUnits[0].floors.length).to.equal(2); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 1, mt: 'video'}); + expect(requests[0].data.adUnits[0].floors[1]).to.deep.equal({f: 1, mt: 'native'}); + }); + + it('should get and set floor with default value if no floors found', function() { + const bid01 = new BidRequestBuilder({ + mediaTypes: { + video: { + context: 'outstream', + mimes: ['video/mp4'] + } + } + }).withParams().build(); + const bidderRequest = new BidderRequestBuilder().build(); + bid01.getFloor = () => { + return { floor: NaN, currency: 'USD' } + } + const requests = spec.buildRequests([bid01], bidderRequest); + + expect(requests[0].data.adUnits[0].floors.length).to.equal(1); + expect(requests[0].data.adUnits[0].floors[0]).to.deep.equal({f: 0.1, mt: 'video'}); + }); + }); }); describe('interpretResponse()', function() { From 17e446deb5e22efa3898b24e5f37d829af4377cc Mon Sep 17 00:00:00 2001 From: etargetse <40423120+etargetse@users.noreply.github.com> Date: Tue, 1 Jun 2021 16:00:59 +0200 Subject: [PATCH 45/63] eTarget Bid Adapter: add "getMetaData" function to adapter, support for advertiserDomains (#6901) --- modules/etargetBidAdapter.js | 39 +++++++++++++++++++-- test/spec/modules/etargetBidAdapter_spec.js | 5 +++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/modules/etargetBidAdapter.js b/modules/etargetBidAdapter.js index 42c991a17a4..8a1b25cec70 100644 --- a/modules/etargetBidAdapter.js +++ b/modules/etargetBidAdapter.js @@ -1,5 +1,5 @@ -'use strict'; - +import * as utils from '../src/utils.js'; +import {config} from '../src/config.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; @@ -57,10 +57,40 @@ export const spec = { data: bidderRequest, bids: validBidRequests, netRevenue: netRevenue, + metaData: getMetaData(), bidder: 'etarget', gdpr: gdprObject }; + function getMetaData() { + var mts = {}; + var hmetas = document.getElementsByTagName('meta'); + var wnames = ['title', 'og:title', 'description', 'og:description', 'og:url', 'base', 'keywords']; + try { + for (var k in hmetas) { + if (typeof hmetas[k] == 'object') { + var mname = hmetas[k].name || hmetas[k].getAttribute('property'); + var mcont = hmetas[k].content; + if (!!mname && mname != 'null' && !!mcont) { + if (wnames.indexOf(mname) >= 0) { + if (!mts[mname]) { + mts[mname] = []; + } + mts[mname].push(mcont); + } + } + } + } + mts['title'] = [(document.getElementsByTagName('title')[0] || []).innerHTML]; + mts['base'] = [(document.getElementsByTagName('base')[0] || {}).href]; + mts['referer'] = [document.location.href]; + mts['ortb2'] = (config.getConfig('ortb2') || {}); + } catch (e) { + mts.error = e; + } + return mts; + } + function formRequestUrl(reqData) { var key; var url = []; @@ -107,9 +137,14 @@ export const spec = { bidObject.gdpr = bidRequest.gdpr.gdpr; bidObject.gdpr_consent = bidRequest.gdpr.gdpr_consent; } + + if (bid.adomain) { + utils.deepSetValue(bidObject, 'meta.advertiserDomains', Array.isArray(bid.adomain) ? bid.adomain : [bid.adomain]); + } bidRespones.push(bidObject); } } + return bidRespones; function verifySize(adItem, validSizes) { diff --git a/test/spec/modules/etargetBidAdapter_spec.js b/test/spec/modules/etargetBidAdapter_spec.js index 2dbf6cd68c5..e2310aee1fb 100644 --- a/test/spec/modules/etargetBidAdapter_spec.js +++ b/test/spec/modules/etargetBidAdapter_spec.js @@ -26,6 +26,11 @@ describe('etarget adapter', function () { assert.lengthOf(parsedUrl.items, 7); }); + it('should be an object', function () { + let request = spec.buildRequests(bids); + assert.isNotNull(request.metaData); + }); + it('should handle global request parameters', function () { let parsedUrl = parseUrl(spec.buildRequests([bids[0]]).url); assert.equal(parsedUrl.path, 'https://sk.search.etargetnet.com/hb'); From 296b926b2c0aa88407716e7bf8793d24f3090d1a Mon Sep 17 00:00:00 2001 From: jackhsiehucf <77815341+jackhsiehucf@users.noreply.github.com> Date: Tue, 1 Jun 2021 23:15:39 +0800 Subject: [PATCH 46/63] ucfunnel Bid Adapter: add support Price Floors Module (#6806) * Add a new ucfunnel Adapter and test page * Add a new ucfunnel Adapter and test page * 1. Use prebid lib in the repo to keep updated 2. Replace var with let 3. Put JSON.parse(JSON.stringify()) into try catch block * utils.getTopWindowLocation is a function * Change to modules from adapters * Migrate to module design * [Dev Fix] Remove width and height which can be got from ad unit id * Update ucfunnelBidAdapter to fit into new spec * Correct the endpoint. Fix the error of query string * Add test case for ucfunnelBidAdapter * Fix lint error * Update version number * Combine all checks on bid request * Add GDPR support for ucfunnel adapter * Add in-stream video and native support for ucfunnel adapter * Remove demo page. Add more test cases. * Change request method from POST to GET * Remove unnecessary comment * Support vastXml and vastUrl for video request * update TTL to 30 mins * Avoid using arrow function which is not discuraged in mocha * ucfunnel tdid support * ucfunnel fix error message in debug mode * ucfunnel adapter add bidfloor parameter * ucfunnel adapter support CCPA * ucfunnel adapter native support clicktrackers * ucfunnel adapter change cookie sync setting * ucfunnel adapter update request parameter * Update ucfunnelBidAdapter * ucfunnel adapter add currency in ad response * ucfunnel adapter support uid2 * ucfunnel Bid Adapter: add support for FLoC and Verizon Media ConnectID * ucfunnel Bid Adapter: add support Price Floors Module Co-authored-by: root Co-authored-by: Ryan Chou Co-authored-by: ucfunnel Co-authored-by: jack.hsieh --- modules/ucfunnelBidAdapter.js | 38 ++++++++++++++++++-- test/spec/modules/ucfunnelBidAdapter_spec.js | 38 ++++++++++++++++++-- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index 734aba97789..685ffdb42b7 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -8,6 +8,7 @@ const COOKIE_NAME = 'ucf_uid'; const VER = 'ADGENT_PREBID-2018011501'; const BIDDER_CODE = 'ucfunnel'; const GVLID = 607; +const CURRENCY = 'USD'; const VIDEO_CONTEXT = { INSTREAM: 0, OUSTREAM: 2 @@ -210,12 +211,41 @@ function getSupplyChain(schain) { return supplyChain; } +function getMediaType(mediaTypes) { + if (mediaTypes != null && mediaTypes.banner) { + return 'banner'; + } else if (mediaTypes != null && mediaTypes.video) { + return 'video'; + } else if (mediaTypes != null && mediaTypes.native) { + return 'native' + } + return 'banner'; +} + +function getFloor(bid, size, mediaTypes) { + if (bid.params.bidfloor) { + return bid.params.bidfloor; + } + if (typeof bid.getFloor === 'function') { + var bidFloor = bid.getFloor({ + currency: CURRENCY, + mediaType: getMediaType(mediaTypes), + size: (size) ? [ size[0], size[1] ] : '*', + }); + if (bidFloor.currency === CURRENCY) { + return bidFloor.floor; + } + } + return undefined; +} + function getRequestData(bid, bidderRequest) { const size = parseSizes(bid); const language = navigator.language; const dnt = (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0; const userIdTdid = (bid.userId && bid.userId.tdid) ? bid.userId.tdid : ''; const supplyChain = getSupplyChain(bid.schain); + const bidFloor = getFloor(bid, size, bid.mediaTypes); // general bid data let bidData = { ver: VER, @@ -225,9 +255,13 @@ function getRequestData(bid, bidderRequest) { dnt: dnt, adid: bid.params.adid, tdid: userIdTdid, - schain: supplyChain, - fp: bid.params.bidfloor + schain: supplyChain }; + + if (bidFloor) { + bidData.fp = bidFloor; + } + addUserId(bidData, bid.userId); try { bidData.host = window.top.location.hostname; diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index bee420f40d4..5899554244b 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -25,8 +25,7 @@ const userId = { const validBannerBidReq = { bidder: BIDDER_CODE, params: { - adid: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D', - bidfloor: 1.0 + adid: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D' }, sizes: [[300, 250]], bidId: '263be71e91dd9d', @@ -175,6 +174,41 @@ describe('ucfunnel Adapter', function () { expect(data.w).to.equal(width); expect(data.h).to.equal(height); }); + + it('should set bidfloor if configured', function() { + let bid = Object.assign({}, validBannerBidReq); + bid.getFloor = function() { + return { + currency: 'USD', + floor: 2.02 + } + }; + const requests = spec.buildRequests([ bid ]); + const data = requests[0].data; + expect(data.fp).to.equal(2.02); + }); + + it('should set bidfloor if configured', function() { + let bid = Object.assign({}, validBannerBidReq); + bid.params.bidfloor = 2.01; + const requests = spec.buildRequests([ bid ]); + const data = requests[0].data; + expect(data.fp).to.equal(2.01); + }); + + it('should set bidfloor if configured', function() { + let bid = Object.assign({}, validBannerBidReq); + bid.getFloor = function() { + return { + currency: 'USD', + floor: 2.02 + } + }; + bid.params.bidfloor = 2.01; + const requests = spec.buildRequests([ bid ]); + const data = requests[0].data; + expect(data.fp).to.equal(2.01); + }); }); describe('interpretResponse', function () { From 667e2683533c546f08b0809b8dd84de9b81d0c3e Mon Sep 17 00:00:00 2001 From: Alexandru Date: Tue, 1 Jun 2021 19:30:11 +0300 Subject: [PATCH 47/63] Brightcom Bid Adapter: handle meta.advertiserDomains (#6905) --- modules/brightcomBidAdapter.js | 5 ++++- test/spec/modules/brightcomBidAdapter_spec.js | 13 ++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/brightcomBidAdapter.js b/modules/brightcomBidAdapter.js index 2aad211b186..7d6d77356f1 100644 --- a/modules/brightcomBidAdapter.js +++ b/modules/brightcomBidAdapter.js @@ -122,7 +122,10 @@ function interpretResponse(serverResponse) { netRevenue: true, mediaType: BANNER, ad: _getAdMarkup(brightcomBid), - ttl: 60 + ttl: 60, + meta: { + advertiserDomains: brightcomBid && brightcomBid.adomain ? brightcomBid.adomain : [] + } }); }); } diff --git a/test/spec/modules/brightcomBidAdapter_spec.js b/test/spec/modules/brightcomBidAdapter_spec.js index a89391d681e..b7d52c9f7d5 100644 --- a/test/spec/modules/brightcomBidAdapter_spec.js +++ b/test/spec/modules/brightcomBidAdapter_spec.js @@ -247,7 +247,8 @@ describe('brightcomBidAdapter', function() { 'nurl': '', 'adm': '', 'w': 300, - 'h': 250 + 'h': 250, + 'adomain': ['example.com'] }] }] } @@ -265,7 +266,10 @@ describe('brightcomBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); @@ -283,7 +287,10 @@ describe('brightcomBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); From 4fe2be1d85b398be2f22912a769beb4a9931d6be Mon Sep 17 00:00:00 2001 From: Alexandru Date: Tue, 1 Jun 2021 19:59:36 +0300 Subject: [PATCH 48/63] Onomagic Bid Adapter: handle meta.advertiserDomains (#6906) * Onomagic Bid Adapter: handle meta.advertiserDomains * Onomagic Bid Adapter: fix lint issues --- modules/onomagicBidAdapter.js | 5 ++++- test/spec/modules/onomagicBidAdapter_spec.js | 13 ++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/onomagicBidAdapter.js b/modules/onomagicBidAdapter.js index 55fca29fbf3..eba62cfe1d4 100644 --- a/modules/onomagicBidAdapter.js +++ b/modules/onomagicBidAdapter.js @@ -117,7 +117,10 @@ function interpretResponse(serverResponse) { netRevenue: true, mediaType: BANNER, ad: _getAdMarkup(onomagicBid), - ttl: 60 + ttl: 60, + meta: { + advertiserDomains: onomagicBid && onomagicBid.adomain ? onomagicBid.adomain : [] + } }); }); } diff --git a/test/spec/modules/onomagicBidAdapter_spec.js b/test/spec/modules/onomagicBidAdapter_spec.js index 7c71c3e5764..6ddc0edd477 100644 --- a/test/spec/modules/onomagicBidAdapter_spec.js +++ b/test/spec/modules/onomagicBidAdapter_spec.js @@ -222,7 +222,8 @@ describe('onomagicBidAdapter', function() { 'nurl': '', 'adm': '', 'w': 300, - 'h': 250 + 'h': 250, + 'adomain': ['example.com'] }] }] } @@ -240,7 +241,10 @@ describe('onomagicBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); @@ -258,7 +262,10 @@ describe('onomagicBidAdapter', function() { 'netRevenue': true, 'mediaType': 'banner', 'ad': `
`, - 'ttl': 60 + 'ttl': 60, + 'meta': { + 'advertiserDomains': ['example.com'] + } }]; let result = spec.interpretResponse(response); From 5cca29ed0a744000fa652ed9e91bc1306f0ed965 Mon Sep 17 00:00:00 2001 From: Jake Miller Date: Tue, 1 Jun 2021 10:03:57 -0700 Subject: [PATCH 49/63] add support for advertiser domains (#6908) --- modules/underdogmediaBidAdapter.js | 3 +++ test/spec/modules/underdogmediaBidAdapter_spec.js | 2 ++ 2 files changed, 5 insertions(+) diff --git a/modules/underdogmediaBidAdapter.js b/modules/underdogmediaBidAdapter.js index 8368077a627..6268774bc12 100644 --- a/modules/underdogmediaBidAdapter.js +++ b/modules/underdogmediaBidAdapter.js @@ -120,6 +120,9 @@ export const spec = { currency: 'USD', netRevenue: false, ttl: mid.ttl || 60, + meta: { + advertiserDomains: mid.advertiser_domains || [] + } }; if (bidResponse.cpm <= 0) { diff --git a/test/spec/modules/underdogmediaBidAdapter_spec.js b/test/spec/modules/underdogmediaBidAdapter_spec.js index aeb9f56c851..70d09513f27 100644 --- a/test/spec/modules/underdogmediaBidAdapter_spec.js +++ b/test/spec/modules/underdogmediaBidAdapter_spec.js @@ -276,6 +276,7 @@ describe('UnderdogMedia adapter', function () { mids: [ { ad_code_html: 'ad_code_html', + advertiser_domains: ['domain1'], cpm: 2.5, height: '600', mid: '32634', @@ -300,6 +301,7 @@ describe('UnderdogMedia adapter', function () { expect(bids).to.be.lengthOf(2); + expect(bids[0].meta.advertiserDomains).to.deep.equal(['domain1']) expect(bids[0].bidderCode).to.equal('underdogmedia'); expect(bids[0].cpm).to.equal(2.5); expect(bids[0].width).to.equal('160'); From f3302af630ec83260d57ecab02ab32c9215607da Mon Sep 17 00:00:00 2001 From: Skylinar <53079123+Skylinar@users.noreply.github.com> Date: Tue, 1 Jun 2021 19:50:16 +0200 Subject: [PATCH 50/63] smartx Bid Adapter: Add support for Floors Module (#6902) * Add smartclipBidAdapter * smartxBidAdapter.js - removed unused variables, removed debug, added window before the outstream related functions * - made outstream player configurable * remove wrong named files * camelcase * fix * Out-Stream render update to SmartPlay 5.2 * ESlint fix * ESlint fix * ESlint fix * adjust tests, fixes * ESlint * adjusted desired bitrate examples * added bid.meta.advertiserDomains support * bug fix for numeric elementID outstream render * fix renderer url * support for floors module Co-authored-by: smartclip AdTechnology Co-authored-by: Gino Cirlini --- modules/smartxBidAdapter.js | 42 +++++--- test/spec/modules/smartxBidAdapter_spec.js | 108 +++++++++++++++++++-- 2 files changed, 133 insertions(+), 17 deletions(-) diff --git a/modules/smartxBidAdapter.js b/modules/smartxBidAdapter.js index b2c48b58f82..46e2055c8f0 100644 --- a/modules/smartxBidAdapter.js +++ b/modules/smartxBidAdapter.js @@ -46,14 +46,6 @@ export const spec = { utils.logError(BIDDER_CODE + ': siteId is not present in bidder params'); return false; } - if (!utils.getBidIdParameter('bidfloor', bid.params)) { - utils.logError(BIDDER_CODE + ': bidfloor is not present in bidder params'); - return false; - } - if (!utils.getBidIdParameter('bidfloorcur', bid.params)) { - utils.logError(BIDDER_CODE + ': bidfloorcur is not present in bidder params'); - return false; - } if (utils.deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { if (!utils.getBidIdParameter('outstream_options', bid.params)) { utils.logError(BIDDER_CODE + ': outstream_options parameter is not defined'); @@ -85,8 +77,8 @@ export const spec = { const smartxRequests = bidRequests.map(function (bid) { const tagId = utils.getBidIdParameter('tagId', bid.params); const publisherId = utils.getBidIdParameter('publisherId', bid.params); - const bidfloor = utils.getBidIdParameter('bidfloor', bid.params); - const bidfloorcur = utils.getBidIdParameter('bidfloorcur', bid.params); + const bidfloor = getBidFloor(bid) || 0; + const bidfloorcur = utils.getBidIdParameter('bidfloorcur', bid.params) || 'EUR'; const siteId = utils.getBidIdParameter('siteId', bid.params); const domain = utils.getBidIdParameter('domain', bid.params); const cat = utils.getBidIdParameter('cat', bid.params); @@ -161,7 +153,7 @@ export const spec = { const at = utils.getBidIdParameter('at', bid.params) || 2; - const cur = utils.getBidIdParameter('cur', bid.params) || ['EUR']; + const cur = utils.getBidIdParameter('cur', bid.params) || 'EUR'; const requestPayload = { id: utils.generateUUID(), @@ -226,6 +218,8 @@ export const spec = { } } + // Todo: USER ID MODULE + requestPayload.user = { ext: userExt, data: targetingarr @@ -412,4 +406,30 @@ function outstreamRender(bid) { } } } + +/** + * Get the floor price from bid.params for backward compatibility. + * If not found, then check floor module. + * @param bid A valid bid object + * @returns {*|number} floor price + */ +function getBidFloor(bid) { + let floor = utils.getBidIdParameter('bidfloor', bid.params); + let floorcur = utils.getBidIdParameter('bidfloorcur', bid.params) || 'EUR'; + + if (!floor && utils.isFn(bid.getFloor)) { + const floorObj = bid.getFloor({ + currency: floorcur, + mediaType: '*', + size: '*' + }); + + if (utils.isPlainObject(floorObj) && !isNaN(floorObj.floor) && floorObj.currency === floorcur) { + floor = floorObj.floor; + } + } + + return floor; +} + registerBidder(spec); diff --git a/test/spec/modules/smartxBidAdapter_spec.js b/test/spec/modules/smartxBidAdapter_spec.js index 3c871c6f88b..d0659865afa 100644 --- a/test/spec/modules/smartxBidAdapter_spec.js +++ b/test/spec/modules/smartxBidAdapter_spec.js @@ -108,14 +108,13 @@ describe('The smartx adapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(false); }); - it('should fail without bidfloor', function () { + it('should succeed with floor Module set', function () { delete bid.params.bidfloor; - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - - it('should fail without bidfloorcur', function () { delete bid.params.bidfloorcur; - expect(spec.isBidRequestValid(bid)).to.equal(false); + bid.floors = { + currency: 'EUR' + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); }); it('should fail with context outstream but no options set for outstream', function () { @@ -514,4 +513,101 @@ describe('The smartx adapter', function () { window.document.getElementById.restore(); }); }); + + describe('price floor module', function () { + var bid, + bidRequestObj; + + beforeEach(function () { + bid = getValidBidObject(); + bidRequestObj = { + refererInfo: { + referer: 'prebid.js' + } + }; + delete bid.params.bidfloor; + }); + + it('obtain floor from getFloor', function () { + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 3.21 + }; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 3.21); + }); + + it('obtain floor from params', function() { + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 3.21 + }; + }; + bid.params.bidfloor = 0.64; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0.64); + }); + + it('check currency USD', function() { + bid.getFloor = () => { + return { + currency: 'USD', + floor: 1.23 + }; + }; + bid.params.bidfloorcur = 'USD' + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloorcur', 'USD'); + expect(payload.data.imp).to.have.property('bidfloor', 1.23); + }); + + it('check defaut currency EUR', function() { + delete bid.params.bidfloorcur; + + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 4.56 + }; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloorcur', 'EUR'); + expect(payload.data.imp).to.have.property('bidfloor', 4.56); + }); + + it('bad floor value', function() { + bid.getFloor = () => { + return { + currency: 'EUR', + floor: 'bad' + }; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0); + }); + + it('empty floor object', function() { + bid.getFloor = () => { + return {}; + }; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0); + }); + + it('undefined floor result', function() { + bid.getFloor = () => {}; + + const payload = spec.buildRequests([bid], bidRequestObj)[0]; + expect(payload.data.imp).to.have.property('bidfloor', 0); + }); + }); }) From e414946e31eb6a822ef69edbb27395d0b058d669 Mon Sep 17 00:00:00 2001 From: ym-dlabuzov <81709888+ym-dlabuzov@users.noreply.github.com> Date: Tue, 1 Jun 2021 21:28:06 +0300 Subject: [PATCH 51/63] Yieldmo Bid Adapter: read video parameters from the ad unit (#6873) --- modules/yieldmoBidAdapter.js | 90 +++++++++++++-------- modules/yieldmoBidAdapter.md | 5 ++ test/spec/modules/yieldmoBidAdapter_spec.js | 71 +++++++++++++++- 3 files changed, 130 insertions(+), 36 deletions(-) diff --git a/modules/yieldmoBidAdapter.js b/modules/yieldmoBidAdapter.js index 36f93f60c9e..fa1ab3a90b3 100644 --- a/modules/yieldmoBidAdapter.js +++ b/modules/yieldmoBidAdapter.js @@ -12,8 +12,8 @@ const NET_REVENUE = true; const BANNER_SERVER_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebid'; const VIDEO_SERVER_ENDPOINT = 'https://ads.yieldmo.com/exchange/prebidvideo'; const OUTSTREAM_VIDEO_PLAYER_URL = 'https://prebid-outstream.yieldmo.com/bundle.js'; -const OPENRTB_VIDEO_BIDPARAMS = ['placement', 'startdelay', 'skipafter', - 'protocols', 'api', 'playbackmethod', 'maxduration', 'minduration', 'pos']; +const OPENRTB_VIDEO_BIDPARAMS = ['mimes', 'startdelay', 'placement', 'startdelay', 'skipafter', 'protocols', 'api', + 'playbackmethod', 'maxduration', 'minduration', 'pos', 'skip', 'skippable']; const OPENRTB_VIDEO_SITEPARAMS = ['name', 'domain', 'cat', 'keywords']; const LOCAL_WINDOW = utils.getWindowTop(); const DEFAULT_PLAYBACK_METHOD = 2; @@ -335,7 +335,6 @@ function openRtbRequest(bidRequests, bidderRequest) { * @return Object OpenRTB's 'imp' (impression) object */ function openRtbImpression(bidRequest) { - const videoReq = utils.deepAccess(bidRequest, 'mediaTypes.video'); const size = extractPlayerSize(bidRequest); const imp = { id: bidRequest.bidId, @@ -347,23 +346,27 @@ function openRtbImpression(bidRequest) { video: { w: size[0], h: size[1], - mimes: videoReq.mimes, linearity: 1 } }; + const mediaTypesParams = utils.deepAccess(bidRequest, 'mediaTypes.video'); + Object.keys(mediaTypesParams) + .filter(param => includes(OPENRTB_VIDEO_BIDPARAMS, param)) + .forEach(param => imp.video[param] = mediaTypesParams[param]); + const videoParams = utils.deepAccess(bidRequest, 'params.video'); Object.keys(videoParams) .filter(param => includes(OPENRTB_VIDEO_BIDPARAMS, param)) .forEach(param => imp.video[param] = videoParams[param]); - if (videoParams.skippable) imp.video.skip = 1; - if (videoParams.placement !== 1) { - imp.video = { - ...imp.video, - startdelay: DEFAULT_START_DELAY, - playbackmethod: [ DEFAULT_PLAYBACK_METHOD ] - } + if (imp.video.skippable) { + imp.video.skip = 1; + delete imp.video.skippable; + } + if (imp.video.placement !== 1) { + imp.video.startdelay = DEFAULT_START_DELAY; + imp.video.playbackmethod = [ DEFAULT_PLAYBACK_METHOD ]; } return imp; } @@ -476,51 +479,68 @@ function validateVideoParams(bid) { const isDefined = val => typeof val !== 'undefined'; const validate = (fieldPath, validateCb, errorCb, errorCbParam) => { - const value = utils.deepAccess(bid, fieldPath); - if (!validateCb(value)) { - errorCb(fieldPath, value, errorCbParam); + if (fieldPath.indexOf('video') === 0) { + const valueFieldPath = 'params.' + fieldPath; + const mediaFieldPath = 'mediaTypes.' + fieldPath; + const valueParams = utils.deepAccess(bid, valueFieldPath); + const mediaTypesParams = utils.deepAccess(bid, mediaFieldPath); + const hasValidValueParams = validateCb(valueParams); + const hasValidMediaTypesParams = validateCb(mediaTypesParams); + + if (hasValidValueParams) return valueParams; + else if (hasValidMediaTypesParams) return hasValidMediaTypesParams; + else { + if (!hasValidValueParams) errorCb(valueFieldPath, valueParams, errorCbParam); + else if (!hasValidMediaTypesParams) errorCb(mediaFieldPath, mediaTypesParams, errorCbParam); + } + return valueParams || mediaTypesParams; + } else { + const value = utils.deepAccess(bid, fieldPath); + if (!validateCb(value)) { + errorCb(fieldPath, value, errorCbParam); + } + return value; } - return value; } try { + validate('video.context', val => !utils.isEmpty(val), paramRequired); + validate('params.placementId', val => !utils.isEmpty(val), paramRequired); - validate('mediaTypes.video.playerSize', val => utils.isArrayOfNums(val, 2) || + validate('video.playerSize', val => utils.isArrayOfNums(val, 2) || (utils.isArray(val) && val.every(v => utils.isArrayOfNums(v, 2))), paramInvalid, 'array of 2 integers, ex: [640,480] or [[640,480]]'); - validate('mediaTypes.video.mimes', val => isDefined(val), paramRequired); - validate('mediaTypes.video.mimes', val => utils.isArray(val) && val.every(v => utils.isStr(v)), paramInvalid, + validate('video.mimes', val => isDefined(val), paramRequired); + validate('video.mimes', val => utils.isArray(val) && val.every(v => utils.isStr(v)), paramInvalid, 'array of strings, ex: ["video/mp4"]'); - validate('params.video', val => !utils.isEmpty(val), paramRequired); - - const placement = validate('params.video.placement', val => isDefined(val), paramRequired); - validate('params.video.placement', val => val >= 1 && val <= 5, paramInvalid); + const placement = validate('video.placement', val => isDefined(val), paramRequired); + validate('video.placement', val => val >= 1 && val <= 5, paramInvalid); if (placement === 1) { - validate('params.video.startdelay', val => isDefined(val), + validate('video.startdelay', val => isDefined(val), (field, v) => paramRequired(field, v, 'placement == 1')); - validate('params.video.startdelay', val => utils.isNumber(val), paramInvalid, 'number, ex: 5'); + validate('video.startdelay', val => utils.isNumber(val), paramInvalid, 'number, ex: 5'); } - validate('params.video.protocols', val => isDefined(val), paramRequired); - validate('params.video.protocols', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), + validate('video.protocols', val => isDefined(val), paramRequired); + validate('video.protocols', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), paramInvalid, 'array of numbers, ex: [2,3]'); - validate('params.video.api', val => isDefined(val), paramRequired); - validate('params.video.api', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), + validate('video.api', val => isDefined(val), paramRequired); + validate('video.api', val => utils.isArrayOfNums(val) && val.every(v => (v >= 1 && v <= 6)), paramInvalid, 'array of numbers, ex: [2,3]'); - validate('params.video.playbackmethod', val => !isDefined(val) || utils.isArrayOfNums(val), paramInvalid, + validate('video.playbackmethod', val => !isDefined(val) || utils.isArrayOfNums(val), paramInvalid, 'array of integers, ex: [2,6]'); - validate('params.video.maxduration', val => isDefined(val), paramRequired); - validate('params.video.maxduration', val => utils.isInteger(val), paramInvalid); - validate('params.video.minduration', val => !isDefined(val) || utils.isNumber(val), paramInvalid); - validate('params.video.skippable', val => !isDefined(val) || utils.isBoolean(val), paramInvalid); - validate('params.video.skipafter', val => !isDefined(val) || utils.isNumber(val), paramInvalid); - validate('params.video.pos', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('video.maxduration', val => isDefined(val), paramRequired); + validate('video.maxduration', val => utils.isInteger(val), paramInvalid); + validate('video.minduration', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('video.skippable', val => !isDefined(val) || utils.isBoolean(val), paramInvalid); + validate('video.skipafter', val => !isDefined(val) || utils.isNumber(val), paramInvalid); + validate('video.pos', val => !isDefined(val) || utils.isNumber(val), paramInvalid); validate('params.badv', val => !isDefined(val) || utils.isArray(val), paramInvalid, 'array of strings, ex: ["ford.com","pepsi.com"]'); validate('params.bcat', val => !isDefined(val) || utils.isArray(val), paramInvalid, diff --git a/modules/yieldmoBidAdapter.md b/modules/yieldmoBidAdapter.md index 54be295a1a1..040fbbec486 100644 --- a/modules/yieldmoBidAdapter.md +++ b/modules/yieldmoBidAdapter.md @@ -96,3 +96,8 @@ var videoAdUnit = [{ }] }]; ``` + +Please also note, that we support the following OpenRTB params: +'mimes', 'startdelay', 'placement', 'startdelay', 'skipafter', 'protocols', 'api', +'playbackmethod', 'maxduration', 'minduration', 'pos', 'skip', 'skippable'. +They can be specified in `mediaTypes.video` or in `bids[].params.video`. diff --git a/test/spec/modules/yieldmoBidAdapter_spec.js b/test/spec/modules/yieldmoBidAdapter_spec.js index 3b317f88dc6..77542480c6c 100644 --- a/test/spec/modules/yieldmoBidAdapter_spec.js +++ b/test/spec/modules/yieldmoBidAdapter_spec.js @@ -331,13 +331,82 @@ describe('YieldmoAdapter', function () { }); describe('Instream video:', function () { + let videoBid; + const buildVideoBidAndGetVideoParam = () => build([videoBid])[0].data.imp[0].video; + + beforeEach(() => { + videoBid = mockVideoBid(); + }); + it('should attempt to send video bid requests to the endpoint via POST', function () { - const requests = build([mockVideoBid()]); + const requests = build([videoBid]); expect(requests.length).to.equal(1); expect(requests[0].method).to.equal('POST'); expect(requests[0].url).to.be.equal(VIDEO_ENDPOINT); }); + it('should add mediaTypes.video prop to the imp.video prop', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['minduration'] = 40; + expect(buildVideoBidAndGetVideoParam().minduration).to.equal(40); + }); + + it('should override mediaTypes.video prop if params.video prop is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['minduration'] = 50; + utils.deepAccess(videoBid, 'params.video')['minduration'] = 40; + expect(buildVideoBidAndGetVideoParam().minduration).to.equal(40); + }); + + it('should add mediaTypes.video.mimes prop to the imp.video', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['minduration'] = ['video/mp4']; + expect(buildVideoBidAndGetVideoParam().minduration).to.deep.equal(['video/mp4']); + }); + + it('should override mediaTypes.video.mimes prop if params.video.mimes is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['mimes'] = ['video/mp4']; + utils.deepAccess(videoBid, 'params.video')['mimes'] = ['video/mkv']; + expect(buildVideoBidAndGetVideoParam().mimes).to.deep.equal(['video/mkv']); + }); + + describe('video.skip state check', () => { + it('should not set video.skip if neither *.video.skip nor *.video.skippable is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = false; + utils.deepAccess(videoBid, 'params.video')['skippable'] = false; + expect(buildVideoBidAndGetVideoParam().skip).to.undefined; + }); + + it('should set video.skip=1 if mediaTypes.video.skip is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skip'] = 1; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if params.video.skip is present', function () { + utils.deepAccess(videoBid, 'params.video')['skip'] = 1; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if mediaTypes.video.skippable is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = true; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if mediaTypes.video.skippable is present', function () { + utils.deepAccess(videoBid, 'params.video')['skippable'] = true; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should set video.skip=1 if mediaTypes.video.skippable is present', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = false; + utils.deepAccess(videoBid, 'params.video')['skippable'] = true; + expect(buildVideoBidAndGetVideoParam().skip).to.equal(1); + }); + + it('should not set video.skip if params.video.skippable is false', function () { + utils.deepAccess(videoBid, 'mediaTypes.video')['skippable'] = true; + utils.deepAccess(videoBid, 'params.video')['skippable'] = false; + expect(buildVideoBidAndGetVideoParam().skip).to.undefined; + }); + }); + it('should process floors module if available', function () { const requests = build([ mockVideoBid({...mockGetFloor(3.99)}), From 6e203ea6924b196cdcb93aff82416c48dd0978d6 Mon Sep 17 00:00:00 2001 From: hnkhandev <57697206+hnkhandev@users.noreply.github.com> Date: Tue, 1 Jun 2021 12:30:31 -0600 Subject: [PATCH 52/63] Accept outstream renderers defined in mediatype for PBS (#6896) --- modules/prebidServerBidAdapter/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/prebidServerBidAdapter/index.js b/modules/prebidServerBidAdapter/index.js index ec5d05f0fe0..4f13bab05eb 100644 --- a/modules/prebidServerBidAdapter/index.js +++ b/modules/prebidServerBidAdapter/index.js @@ -594,7 +594,7 @@ const OPEN_RTB_PROTOCOL = { } if (!utils.isEmpty(videoParams)) { - if (videoParams.context === 'outstream' && !adUnit.renderer) { + if (videoParams.context === 'outstream' && (!videoParams.renderer || !adUnit.renderer)) { // Don't push oustream w/o renderer to request object. utils.logError('Outstream bid without renderer cannot be sent to Prebid Server.'); } else { From 1c3247c6beae0119bfabc43a7cee4a61629374b6 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Tue, 1 Jun 2021 13:58:39 -0700 Subject: [PATCH 53/63] Sharethrough Bid Adapter: Use getFloor module for Prebid 5.0 compliance (#6874) * Fix Prebid 5.0 compliance: leverage floors module * Upgrade adapter version --- modules/sharethroughBidAdapter.js | 21 ++++++++++++++++--- .../modules/sharethroughBidAdapter_spec.js | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/modules/sharethroughBidAdapter.js b/modules/sharethroughBidAdapter.js index 68ccde0da46..59cf2a3a035 100644 --- a/modules/sharethroughBidAdapter.js +++ b/modules/sharethroughBidAdapter.js @@ -2,7 +2,7 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import * as utils from '../src/utils.js'; import { config } from '../src/config.js'; -const VERSION = '3.3.2'; +const VERSION = '3.4.0'; const BIDDER_CODE = 'sharethrough'; const STR_ENDPOINT = 'https://btlr.sharethrough.com/WYu2BXv1/v1'; const DEFAULT_SIZE = [1, 1]; @@ -57,8 +57,9 @@ export const sharethroughAdapterSpec = { query.schain = JSON.stringify(bidRequest.schain); } - if (bidRequest.bidfloor) { - query.bidfloor = parseFloat(bidRequest.bidfloor); + const floor = getFloor(bidRequest); + if (floor) { + query.bidfloor = floor; } if (bidRequest.params.badv) { @@ -292,4 +293,18 @@ function getProtocol() { return document.location.protocol; } +function getFloor(bid) { + if (utils.isFn(bid.getFloor)) { + const floorInfo = bid.getFloor({ + currency: 'USD', + mediaType: 'banner', + size: bid.sizes.map(size => ({ w: size[0], h: size[1] })) + }); + if (utils.isPlainObject(floorInfo) && !isNaN(floorInfo.floor) && floorInfo.currency === 'USD') { + return parseFloat(floorInfo.floor); + } + } + return null; +} + registerBidder(sharethroughAdapterSpec); diff --git a/test/spec/modules/sharethroughBidAdapter_spec.js b/test/spec/modules/sharethroughBidAdapter_spec.js index 5c8e01536dd..b8d91249ec3 100644 --- a/test/spec/modules/sharethroughBidAdapter_spec.js +++ b/test/spec/modules/sharethroughBidAdapter_spec.js @@ -436,7 +436,7 @@ describe('sharethrough adapter spec', function() { it('should include the bidfloor parameter if it is present in the bid request', function() { const bidRequest = Object.assign({}, bidRequests[0]); - bidRequest['bidfloor'] = 0.50; + bidRequest['getFloor'] = () => ({ currency: 'USD', floor: 0.5 }); const builtBidRequest = spec.buildRequests([bidRequest])[0]; expect(builtBidRequest.data.bidfloor).to.eq(0.5); }); From a259700fc1e152d67672a95170605506fa241939 Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 2 Jun 2021 13:21:09 +0300 Subject: [PATCH 54/63] Onomagic Bid Adapter: use getFloor function (#6907) * Onomagic Bid Adapter: use getFloor function * Onomagic Bid Adapter: fix issues --- modules/onomagicBidAdapter.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/onomagicBidAdapter.js b/modules/onomagicBidAdapter.js index eba62cfe1d4..548c0170c05 100644 --- a/modules/onomagicBidAdapter.js +++ b/modules/onomagicBidAdapter.js @@ -46,7 +46,7 @@ function buildRequests(bidReqs, bidderRequest) { }, tagid: String(bid.adUnitCode) }; - const bidFloor = utils.getBidIdParameter('bidFloor', bid.params); + const bidFloor = _getBidFloor(bid); if (bidFloor) { imp.bidfloor = bidFloor; } @@ -246,4 +246,20 @@ function _getPercentInView(element, topWin, { w, h } = {}) { return 0; } +function _getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return bid.params.bidFloor ? bid.params.bidFloor : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); From 481b94f8c27d535d7b9105a69e76d01bad49a64b Mon Sep 17 00:00:00 2001 From: jxdeveloper1 <71084096+jxdeveloper1@users.noreply.github.com> Date: Wed, 2 Jun 2021 19:11:28 +0800 Subject: [PATCH 55/63] Jixie Bid Adapter: add support for advertiserDomains (#6898) * Adapter does not seem capable of supporting advertiserDomains #6650 added response comment and some trivial code. * removed a blank line at the end of file added a space behind the // in comments * in response to comment from reviewer. add the aspect of advertiserdomain in unit tests --- modules/jixieBidAdapter.js | 10 ++++++++++ test/spec/modules/jixieBidAdapter_spec.js | 9 ++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/modules/jixieBidAdapter.js b/modules/jixieBidAdapter.js index 7c6e0027482..db6c9032e65 100644 --- a/modules/jixieBidAdapter.js +++ b/modules/jixieBidAdapter.js @@ -240,6 +240,16 @@ export const spec = { let rendererScript = (oneBid.osparams.script ? oneBid.osparams.script : JX_OUTSTREAM_RENDERER_URL); bnd.renderer = createRenderer_(oneBid, rendererScript, jxOutstreamRender_); } + // a note on advertiserDomains: our adserver is not responding in + // openRTB-type json. so there is no need to copy from 'adomain' over + // to meta: advertiserDomains + // However, we will just make sure the property is there. + if (!bnd.meta) { + bnd.meta = {}; + } + if (!bnd.meta.advertiserDomains) { + bnd.meta.advertiserDomains = []; + } bidResponses.push(bnd); }); if (response.body.setids) { diff --git a/test/spec/modules/jixieBidAdapter_spec.js b/test/spec/modules/jixieBidAdapter_spec.js index 842f9e0ed30..ae58da30f64 100644 --- a/test/spec/modules/jixieBidAdapter_spec.js +++ b/test/spec/modules/jixieBidAdapter_spec.js @@ -281,7 +281,8 @@ describe('jixie Adapter', function () { }, 'vastUrl': 'https://ad.jixie.io/v1/video?creativeid=522' }, - // display ad returned here: + // display ad returned here: This one there is advertiserDomains + // in the response . Will be checked in the unit tests below { 'trackingUrlBase': 'https://tr.jixie.io/sync/ad?', 'jxBidId': '600c9ae6fda1acb-028d5dee-2c83-44e3-bed1-b75002475cdf', @@ -411,6 +412,9 @@ describe('jixie Adapter', function () { expect(result[0].ttl).to.equal(300) expect(result[0].vastUrl).to.include('https://ad.jixie.io/v1/video?creativeid=') expect(result[0].trackingUrlBase).to.include('sync') + // We will always make sure the meta->advertiserDomains property is there + // If no info it is an empty array. + expect(result[0].meta.advertiserDomains.length).to.equal(0) // display ad expect(result[1].requestId).to.equal('600c9ae6fda1acb') @@ -422,6 +426,7 @@ describe('jixie Adapter', function () { expect(result[1].netRevenue).to.equal(true) expect(result[1].ttl).to.equal(300) expect(result[1].ad).to.include('jxoutstream') + expect(result[1].meta.advertiserDomains.length).to.equal(3) expect(result[1].trackingUrlBase).to.include('sync') // should pick up about using alternative outstream renderer @@ -436,6 +441,7 @@ describe('jixie Adapter', function () { expect(result[2].vastXml).to.include('') expect(result[2].trackingUrlBase).to.include('sync'); expect(result[2].renderer.id).to.equal('demoslot4-div') + expect(result[2].meta.advertiserDomains.length).to.equal(0) expect(result[2].renderer.url).to.equal(JX_OTHER_OUTSTREAM_RENDERER_URL); // should know to use default outstream renderer @@ -450,6 +456,7 @@ describe('jixie Adapter', function () { expect(result[3].vastXml).to.include('') expect(result[3].trackingUrlBase).to.include('sync'); expect(result[3].renderer.id).to.equal('demoslot2-div') + expect(result[3].meta.advertiserDomains.length).to.equal(0) expect(result[3].renderer.url).to.equal(JX_OUTSTREAM_RENDERER_URL) setLocalStorageSpy.restore(); From fbc44aa6c658f3be93f5dbb94e0f549061873902 Mon Sep 17 00:00:00 2001 From: relaido <63339139+relaido@users.noreply.github.com> Date: Wed, 2 Jun 2021 20:20:56 +0900 Subject: [PATCH 56/63] relaido Bid Adapter: Add meta OBJ to BidResponse (#6914) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * add response meda OBJ update version and test code Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun --- modules/relaidoBidAdapter.js | 6 +++++- test/spec/modules/relaidoBidAdapter_spec.js | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index 92709b7c047..f69f8c5c6e2 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -6,7 +6,7 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'relaido'; const BIDDER_DOMAIN = 'api.relaido.jp'; -const ADAPTER_VERSION = '1.0.3'; +const ADAPTER_VERSION = '1.0.4'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; @@ -112,6 +112,10 @@ function interpretResponse(serverResponse, bidRequest) { ttl: body.ttl || DEFAULT_TTL, netRevenue: true, mediaType: mediaType, + meta: { + advertiserDomains: body.adomain || [], + mediaType: VIDEO + } }; if (mediaType === VIDEO) { bidResponse.vastXml = body.vast; diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index 91aa6b05e6e..c2082eb1e91 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/relaidoBidAdapter.js'; import * as utils from 'src/utils.js'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { getStorageManager } from '../../../src/storageManager.js'; const UUID_KEY = 'relaido_uuid'; @@ -59,7 +60,8 @@ describe('RelaidoAdapter', function () { uuid: relaido_uuid, vast: '', playerUrl: 'https://relaido/player.js', - syncUrl: 'https://relaido/sync.html' + syncUrl: 'https://relaido/sync.html', + adomain: ['relaido.co.jp', 'www.cmertv.co.jp'] } }; serverRequest = { @@ -276,6 +278,8 @@ describe('RelaidoAdapter', function () { expect(response.currency).to.equal(serverResponse.body.currency); expect(response.creativeId).to.equal(serverResponse.body.creativeId); expect(response.vastXml).to.equal(serverResponse.body.vast); + expect(response.meta.advertiserDomains).to.equal(serverResponse.body.adomain); + expect(response.meta.mediaType).to.equal(VIDEO); expect(response.ad).to.be.undefined; }); From f957aa779be7fbea758932b43644a3d3a95d41c8 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 2 Jun 2021 04:28:08 -0700 Subject: [PATCH 57/63] AdYouLike Bidder: Handle advertiser domains (#6916) * AdYouLike Bidder: Handle advertiser domains * fix linting * update tests * fix linting * fix error --- modules/adyoulikeBidAdapter.js | 3 +- test/spec/modules/adyoulikeBidAdapter_spec.js | 34 +++++++++++++++---- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 44d8e2e3016..74ce62950f8 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -449,7 +449,8 @@ function createBid(response, bidRequests) { creativeId: response.CreativeID, cpm: response.Price, netRevenue: true, - currency: CURRENCY + currency: CURRENCY, + meta: response.Meta || { advertiserDomains: [] } }; if (request && request.Native) { diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index abf8793865c..e6e95ea5423 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -290,6 +290,19 @@ describe('Adyoulike Adapter', function () { 'Placement': 'placement_0' } ]; + + const testMetaObject = { + 'networkId': 123, + 'advertiserId': '3', + 'advertiserName': 'foobar', + 'advertiserDomains': ['foobar.com'], + 'brandId': '345', + 'brandName': 'Foo', + 'primaryCatId': '34', + 'secondaryCatIds': ['IAB-222', 'IAB-333'], + 'mediaType': 'banner' + }; + const admSample = "\u003cscript id=\"ayl-prebid-a11a121205932e75e622af275681965d\"\u003e\n(function(){\n\twindow.isPrebid = true\n\tvar prebidResults = /*PREBID*/{\"OnEvents\":{\"CLICK\":[{\"Kind\":\"PIXEL_URL\",\"Url\":\"https://testPixelCLICK.com/fake\"}],\"IMPRESSION\":[{\"Kind\":\"PIXEL_URL\",\"Url\":\"https://testPixelIMP.com/fake\"},{\"Kind\":\"JAVASCRIPT_URL\",\"Url\":\"https://testJsIMP.com/fake.js\"}]},\"Disabled\":false,\"Attempt\":\"a11a121205932e75e622af275681965d\",\"ApiPrefix\":\"https://fo-api.omnitagjs.com/fo-api\",\"TrackingPrefix\":\"https://tracking.omnitagjs.com/tracking\",\"DynamicPrefix\":\"https://tag-dyn.omnitagjs.com/fo-dyn\",\"StaticPrefix\":\"https://fo-static.omnitagjs.com/fo-static\",\"BlobPrefix\":\"https://fo-api.omnitagjs.com/fo-api/blobs\",\"SspPrefix\":\"https://fo-ssp.omnitagjs.com/fo-ssp\",\"VisitorPrefix\":\"https://visitor.omnitagjs.com/visitor\",\"Trusted\":true,\"Placement\":\"e622af275681965d3095808561a1e510\",\"PlacementAccess\":\"ALL\",\"Site\":\"6e2df7a92203c3c7a25561ed63f25a27\",\"Lang\":\"EN\",\"SiteLogo\":null,\"HasSponsorImage\":false,\"ResizeIframe\":true,\"IntegrationConfig\":{\"Kind\":\"WIDGET\",\"Widget\":{\"ExtraStyleSheet\":\"\",\"Placeholders\":{\"Body\":{\"Color\":{\"R\":77,\"G\":21,\"B\":82,\"A\":100},\"BackgroundColor\":{\"R\":255,\"G\":255,\"B\":255,\"A\":100},\"FontFamily\":\"Lato\",\"Width\":\"100%\",\"Align\":\"\",\"BoxShadow\":true},\"CallToAction\":{\"Color\":{\"R\":26,\"G\":157,\"B\":212,\"A\":100}},\"Description\":{\"Length\":130},\"Image\":{\"Width\":600,\"Height\":600,\"Lowres\":false,\"Raw\":false},\"Size\":{\"Height\":\"250px\",\"Width\":\"300px\"},\"Sponsor\":{\"Color\":{\"R\":35,\"G\":35,\"B\":35,\"A\":100},\"Label\":true,\"WithoutLogo\":false},\"Title\":{\"Color\":{\"R\":219,\"G\":181,\"B\":255,\"A\":100}}},\"Selector\":{\"Kind\":\"CSS\",\"Css\":\"#ayl-prebid-a11a121205932e75e622af275681965d\"},\"Insertion\":\"AFTER\",\"ClickFormat\":true,\"Creative20\":true,\"WidgetKind\":\"CREATIVE_TEMPLATE_4\"}},\"Legal\":\"Sponsored\",\"ForcedCampaign\":\"f1c80d4bb5643c222ae8de75e9b2f991\",\"ForcedTrack\":\"\",\"ForcedCreative\":\"\",\"ForcedSource\":\"\",\"DisplayMode\":\"DEFAULT\",\"Campaign\":\"f1c80d4bb5643c222ae8de75e9b2f991\",\"CampaignAccess\":\"ALL\",\"CampaignKind\":\"AD_TRAFFIC\",\"DataSource\":\"LOCAL\",\"DataSourceUrl\":\"\",\"DataSourceOnEventsIsolated\":false,\"DataSourceWithoutCookie\":false,\"Content\":{\"Preview\":{\"Thumbnail\":{\"Image\":{\"Kind\":\"EXTERNAL\",\"Data\":{\"External\":{\"Url\":\"https://tag-dyn.omnitagjs.com/fo-dyn/native/preview/image?key=fd4362d35bb174d6f1c80d4bb5643c22\\u0026kind=INTERNAL\\u0026ztop=0.000000\\u0026zleft=0.000000\\u0026zwidth=0.333333\\u0026zheight=1.000000\\u0026width=[width]\\u0026height=[height]\"}},\"ZoneTop\":0,\"ZoneLeft\":0,\"ZoneWidth\":1,\"ZoneHeight\":1,\"Smart\":false,\"NoTransform\":false,\"Quality\":\"NORMAL\"}},\"Text\":{\"CALLTOACTION\":\"Click here to learn more\",\"DESCRIPTION\":\"Considérant l'extrémité conjoncturelle, il serait bon d'anticiper toutes les voies de bon sens.\",\"SPONSOR\":\"Tested by\",\"TITLE\":\"Adserver Traffic Redirect Internal\"},\"Sponsor\":{\"Name\":\"QA Team\"},\"Credit\":{\"Logo\":{\"Resource\":{\"Kind\":\"EXTERNAL\",\"Data\":{\"External\":{\"Url\":\"https://fo-static.omnitagjs.com/fo-static/native/images/info-ayl.png\"}},\"ZoneTop\":0,\"ZoneLeft\":0,\"ZoneWidth\":1,\"ZoneHeight\":1,\"Smart\":false,\"NoTransform\":false,\"Quality\":\"NORMAL\"}},\"Url\":\"https://blobs.omnitagjs.com/adchoice/\"}},\"Landing\":{\"Url\":\"https://www.w3.org/People/mimasa/test/xhtml/entities/entities-11.xhtml#lat1\",\"LegacyTracking\":false},\"ViewButtons\":{\"Close\":{\"Skip\":6000}},\"InternalContentFields\":{\"AnimatedImage\":false}},\"AdDomain\":\"adyoulike.com\",\"Opener\":\"REDIRECT\",\"PerformUITriggers\":[\"CLICK\"],\"RedirectionTarget\":\"TAB\"}/*PREBID*/;\n\tvar insertAds = function insertAds() {\insertAds();\n\t}\n})();\n\u003c/script\u003e"; const responseWithSinglePlacement = [ { @@ -298,6 +311,7 @@ describe('Adyoulike Adapter', function () { 'Ad': admSample, 'Price': 0.5, 'Height': 600, + 'Meta': testMetaObject } ]; @@ -365,7 +379,8 @@ describe('Adyoulike Adapter', function () { privacyLink: 'https://blobs.omnitagjs.com/adchoice/', sponsoredBy: 'QA Team', title: 'Adserver Traffic Redirect Internal', - } + }, + meta: testMetaObject }]; const responseWithMultiplePlacements = [ @@ -617,6 +632,7 @@ describe('Adyoulike Adapter', function () { expect(result[0].ad).to.equal(admSample); expect(result[0].width).to.equal(300); expect(result[0].height).to.equal(600); + expect(result[0].meta).to.deep.equal(testMetaObject); }); it('receive reponse with multiple placement', function () { @@ -636,8 +652,8 @@ describe('Adyoulike Adapter', function () { expect(result[1].height).to.equal(250); }); - it('receive reponse with Native ad', function () { - serverResponse.body = responseWithSingleNative; + it('receive reponse with Native from ad markup', function () { + serverResponse.body = responseWithSinglePlacement; let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); expect(result.length).to.equal(1); @@ -645,13 +661,19 @@ describe('Adyoulike Adapter', function () { expect(result).to.deep.equal(nativeResult); }); - it('receive reponse with Native from ad markup', function () { - serverResponse.body = responseWithSinglePlacement; + it('receive reponse with Native ad', function () { + serverResponse.body = responseWithSingleNative; let result = spec.interpretResponse(serverResponse, {data: '{"Bids":' + JSON.stringify(sentBidNative) + '}'}); expect(result.length).to.equal(1); - expect(result).to.deep.equal(nativeResult); + const noMeta = [...nativeResult]; + const metaBackup = noMeta[0].meta; + + // this test should return default meta object + noMeta[0].meta = { advertiserDomains: [] }; + + expect(result).to.deep.equal(noMeta); }); }); }); From 7e67b4e3b8b89dae7c04bd5cd68041bfd41fa031 Mon Sep 17 00:00:00 2001 From: Alexandru Date: Wed, 2 Jun 2021 16:19:17 +0300 Subject: [PATCH 58/63] Brightcom Bid Adapter: use getFloor function (#6918) --- modules/brightcomBidAdapter.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/brightcomBidAdapter.js b/modules/brightcomBidAdapter.js index 7d6d77356f1..8299a2331cb 100644 --- a/modules/brightcomBidAdapter.js +++ b/modules/brightcomBidAdapter.js @@ -46,7 +46,7 @@ function buildRequests(bidReqs, bidderRequest) { }, tagid: String(bid.adUnitCode) }; - const bidFloor = utils.getBidIdParameter('bidFloor', bid.params); + const bidFloor = _getBidFloor(bid); if (bidFloor) { imp.bidfloor = bidFloor; } @@ -251,4 +251,20 @@ function _getPercentInView(element, topWin, { w, h } = {}) { return 0; } +function _getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return bid.params.bidFloor ? bid.params.bidFloor : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + registerBidder(spec); From b105b753472e0b59690ad13289b7169983d9b605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= Date: Wed, 2 Jun 2021 15:39:24 +0200 Subject: [PATCH 59/63] Sublime Bid Adapter : Add support for meta.advertiserDomains (#6920) * Add advertiserDomains stub * Use utils from require * Update version * Replace let to const and fix version --- modules/sublimeBidAdapter.js | 8 ++- test/spec/modules/sublimeBidAdapter_spec.js | 77 +++++++++++++-------- 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/modules/sublimeBidAdapter.js b/modules/sublimeBidAdapter.js index 41fdb72e76e..854f58df8fe 100644 --- a/modules/sublimeBidAdapter.js +++ b/modules/sublimeBidAdapter.js @@ -9,7 +9,7 @@ const DEFAULT_CURRENCY = 'EUR'; const DEFAULT_PROTOCOL = 'https'; const DEFAULT_TTL = 600; const SUBLIME_ANTENNA = 'antenna.ayads.co'; -const SUBLIME_VERSION = '0.7.1'; +const SUBLIME_VERSION = '0.7.2'; /** * Identify the current device type @@ -207,6 +207,12 @@ function interpretResponse(serverResponse, bidRequest) { sspname: response.sspname || null }; + // We don't support advertiserDomains atm + if (response.advertiserDomains) { + // Creating a stub for Prebid.js 5.0 compliance + bidResponse.meta = Object.assign({}, bidResponse.meta, { advertiserDomains: [] }); + } + bidResponses.push(bidResponse); } diff --git a/test/spec/modules/sublimeBidAdapter_spec.js b/test/spec/modules/sublimeBidAdapter_spec.js index 2e1d65f0533..fab487cb65f 100644 --- a/test/spec/modules/sublimeBidAdapter_spec.js +++ b/test/spec/modules/sublimeBidAdapter_spec.js @@ -47,7 +47,7 @@ describe('Sublime Adapter', function() { }); describe('isBidRequestValid', function() { - let bid = { + const bid = { bidder: 'sublime', params: { zoneId: 24549, @@ -60,14 +60,14 @@ describe('Sublime Adapter', function() { }); it('should return false when required params are not passed', function() { - let bid = Object.assign({}, bid); + const bid = Object.assign({}, bid); bid.params = {}; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); describe('buildRequests', function() { - let bidRequests = [ + const bidRequests = [ { bidder: 'sublime', adUnitCode: 'sublime_code', @@ -90,7 +90,7 @@ describe('Sublime Adapter', function() { } ]; - let bidderRequest = { + const bidderRequest = { gdprConsent: { consentString: 'EOHEIRCOUCOUIEHZIOEIU-TEST', gdprApplies: true @@ -101,7 +101,7 @@ describe('Sublime Adapter', function() { } }; - let request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, bidderRequest); it('should have a post method', function() { expect(request[0].method).to.equal('POST'); @@ -121,7 +121,7 @@ describe('Sublime Adapter', function() { }); describe('buildRequests: default arguments', function() { - let bidRequests = [{ + const bidRequests = [{ bidder: 'sublime', adUnitCode: 'sublime_code', bidId: 'abc1234', @@ -132,7 +132,7 @@ describe('Sublime Adapter', function() { } }]; - let request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests); it('should have an url that match the default endpoint', function() { expect(request[0].url).to.equal('https://pbjs.sskzlabs.com/bid'); @@ -140,7 +140,7 @@ describe('Sublime Adapter', function() { }); describe('interpretResponse', function() { - let serverResponse = { + const serverResponse = { 'request_id': '3db3773286ee59', 'sspname': 'foo', 'cpm': 0.5, @@ -155,7 +155,7 @@ describe('Sublime Adapter', function() { } }; - let expectedResponse = [ + const expectedResponse = [ { requestId: '', cpm: 0.5, @@ -167,23 +167,23 @@ describe('Sublime Adapter', function() { sspname: 'foo', netRevenue: true, ttl: 600, - pbav: '0.7.1', + pbav: '0.7.2', ad: '', }, ]; - let result = spec.interpretResponse({body: serverResponse}); + const result = spec.interpretResponse({body: serverResponse}); expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); }); it('should get correct default size for 1x1', function() { - let serverResponse = { + const serverResponse = { 'requestId': 'xyz654_2', 'sspname': 'sublime', 'cpm': 0.5, 'ad': '', }; - let bidRequest = { + const bidRequest = { bidder: 'sublime', adUnitCode: 'sublime_code_2', bidId: 'abc1234_2', @@ -197,9 +197,9 @@ describe('Sublime Adapter', function() { } }; - let result = spec.interpretResponse({body: serverResponse}, bidRequest); + const result = spec.interpretResponse({body: serverResponse}, bidRequest); - let expectedResponse = { + const expectedResponse = { requestId: 'xyz654_2', cpm: 0.5, width: 1, @@ -210,7 +210,7 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.7.1', + pbav: '0.7.2', sspname: 'sublime' }; @@ -218,24 +218,24 @@ describe('Sublime Adapter', function() { }); it('should return bid empty response', function () { - let serverResponse = ''; - let bidRequest = {}; + const serverResponse = ''; + const bidRequest = {}; - let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + const result = spec.interpretResponse({ body: serverResponse }, bidRequest); - let expectedResponse = []; + const expectedResponse = []; expect(result).to.deep.equal(expectedResponse); }); it('should return bid with default value in response', function () { - let serverResponse = { + const serverResponse = { 'requestId': 'xyz654_2', 'sspname': 'sublime', 'ad': '', }; - let bidRequest = { + const bidRequest = { bidder: 'sublime', adUnitCode: 'sublime_code_2', bidId: 'abc1234_2', @@ -249,9 +249,9 @@ describe('Sublime Adapter', function() { } }; - let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + const result = spec.interpretResponse({ body: serverResponse }, bidRequest); - let expectedResponse = { + const expectedResponse = { requestId: 'xyz654_2', cpm: 0, width: 1, @@ -263,20 +263,20 @@ describe('Sublime Adapter', function() { netRevenue: true, ttl: 600, ad: '', - pbav: '0.7.1', + pbav: '0.7.2', }; expect(result[0]).to.deep.equal(expectedResponse); }); it('should return empty bid response because of timeout', function () { - let serverResponse = { + const serverResponse = { 'requestId': 'xyz654_2', 'timeout': true, 'ad': '', }; - let bidRequest = { + const bidRequest = { bidder: 'sublime', adUnitCode: 'sublime_code_2', bidId: 'abc1234_2', @@ -290,9 +290,9 @@ describe('Sublime Adapter', function() { } }; - let result = spec.interpretResponse({ body: serverResponse }, bidRequest); + const result = spec.interpretResponse({ body: serverResponse }, bidRequest); - let expectedResponse = []; + const expectedResponse = []; expect(result).to.deep.equal(expectedResponse); @@ -300,11 +300,28 @@ describe('Sublime Adapter', function() { spec.onTimeout(result); }); }); + + it('should add advertiserDomains', function() { + const responseWithAdvertiserDomains = utils.deepClone(serverResponse); + responseWithAdvertiserDomains.advertiserDomains = ['a_sublime_adomain']; + + const bidRequest = { + bidder: 'sublime', + params: { + zoneId: 456, + } + }; + + const result = spec.interpretResponse({ body: responseWithAdvertiserDomains }, bidRequest); + + expect(Object.keys(result[0].meta)).to.include.members(['advertiserDomains']); + expect(Object.keys(result[0].meta.advertiserDomains)).to.deep.equal([]); + }); }); describe('onBidWon', function() { let sandbox; - let bid = { foo: 'bar' }; + const bid = { foo: 'bar' }; beforeEach(function () { sandbox = sinon.sandbox.create(); From db50c5b49bb7bfdf68f2a36ae7295fac60ff9f37 Mon Sep 17 00:00:00 2001 From: Margaret Liu Date: Wed, 2 Jun 2021 09:27:32 -0500 Subject: [PATCH 60/63] LockerDome Bid Adapter: support for meta.advertiserDomains (#6921) * Add lockerdome adapter advertiserDomains support stub for Prebid 5.0 * Add lockerdome adapter advertiserDomains support stub for Prebid 5.0 * fix linting error Co-authored-by: Chris Huie --- modules/lockerdomeBidAdapter.js | 5 ++++- test/spec/modules/lockerdomeBidAdapter_spec.js | 12 ++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/lockerdomeBidAdapter.js b/modules/lockerdomeBidAdapter.js index 80c40b39f9a..4e30519c6d3 100644 --- a/modules/lockerdomeBidAdapter.js +++ b/modules/lockerdomeBidAdapter.js @@ -66,7 +66,10 @@ export const spec = { currency: bid.currency, netRevenue: bid.netRevenue, ad: bid.ad, - ttl: bid.ttl + ttl: bid.ttl, + meta: { + advertiserDomains: bid.adomain && Array.isArray(bid.adomain) ? bid.adomain : [] + } }; }); }, diff --git a/test/spec/modules/lockerdomeBidAdapter_spec.js b/test/spec/modules/lockerdomeBidAdapter_spec.js index 42e3f52e533..9e3d7981300 100644 --- a/test/spec/modules/lockerdomeBidAdapter_spec.js +++ b/test/spec/modules/lockerdomeBidAdapter_spec.js @@ -180,7 +180,8 @@ describe('LockerDomeAdapter', function () { currency: 'USD', netRevenue: true, ad: '', - ttl: 300 + ttl: 300, + adomain: ['example.com'] }, { requestId: '4510f2834773ce', @@ -191,7 +192,8 @@ describe('LockerDomeAdapter', function () { currency: 'USD', netRevenue: true, ad: '', - ttl: 300 + ttl: 300, + adomain: ['example.com'] }] } }; @@ -217,6 +219,9 @@ describe('LockerDomeAdapter', function () { expect(interpretedResponse[0].netRevenue).to.equal(serverResponse.body.bids[0].netRevenue); expect(interpretedResponse[0].ad).to.equal(serverResponse.body.bids[0].ad); expect(interpretedResponse[0].ttl).to.equal(serverResponse.body.bids[0].ttl); + expect(interpretedResponse[0]).to.have.property('meta'); + expect(interpretedResponse[0].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[0].meta.advertiserDomains).to.deep.equal(serverResponse.body.bids[0].adomain); expect(interpretedResponse[1].requestId).to.equal(serverResponse.body.bids[1].requestId); expect(interpretedResponse[1].cpm).to.equal(serverResponse.body.bids[1].cpm); @@ -227,6 +232,9 @@ describe('LockerDomeAdapter', function () { expect(interpretedResponse[1].netRevenue).to.equal(serverResponse.body.bids[1].netRevenue); expect(interpretedResponse[1].ad).to.equal(serverResponse.body.bids[1].ad); expect(interpretedResponse[1].ttl).to.equal(serverResponse.body.bids[1].ttl); + expect(interpretedResponse[1]).to.have.property('meta'); + expect(interpretedResponse[1].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[1].meta.advertiserDomains).to.deep.equal(serverResponse.body.bids[1].adomain); }); }); }); From b271db1f72258847a27f9e043b7c47e9e8c98991 Mon Sep 17 00:00:00 2001 From: Gena Date: Wed, 2 Jun 2021 17:51:08 +0300 Subject: [PATCH 61/63] Update Adtelligent, Adtarget, ViewDeos adapters to support adomain (#6917) * Update Adtelligent, Adtarget, ViewDeos adapters to support adomain * fix * fix tests --- modules/adtargetBidAdapter.js | 5 ++++- modules/adtelligentBidAdapter.js | 5 ++++- modules/viewdeosDXBidAdapter.js | 5 ++++- test/spec/modules/adtargetBidAdapter_spec.js | 13 ++++++++++--- test/spec/modules/adtelligentBidAdapter_spec.js | 17 ++++++++++++----- test/spec/modules/viewdeosDXBidAdapter_spec.js | 16 +++++++++++----- 6 files changed, 45 insertions(+), 16 deletions(-) diff --git a/modules/adtargetBidAdapter.js b/modules/adtargetBidAdapter.js index b22addec54f..1779ba94371 100644 --- a/modules/adtargetBidAdapter.js +++ b/modules/adtargetBidAdapter.js @@ -173,7 +173,10 @@ function createBid(bidResponse, bidRequest) { cpm: bidResponse.cpm, netRevenue: true, mediaType, - ttl: 300 + ttl: 300, + meta: { + advertiserDomains: bidResponse.adomain || [] + } }; if (mediaType === BANNER) { diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index 9ca4b95dfd2..d21931a6dcb 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -248,7 +248,10 @@ function createBid(bidResponse, bidRequest) { cpm: bidResponse.cpm, netRevenue: true, mediaType, - ttl: 300 + ttl: 300, + meta: { + advertiserDomains: bidResponse.adomain || [] + } }; if (mediaType === BANNER) { diff --git a/modules/viewdeosDXBidAdapter.js b/modules/viewdeosDXBidAdapter.js index 22c16ab5b34..212759642f5 100644 --- a/modules/viewdeosDXBidAdapter.js +++ b/modules/viewdeosDXBidAdapter.js @@ -190,7 +190,10 @@ function createBid(bidResponse, mediaType, bidderParams) { cpm: bidResponse.cpm, netRevenue: true, mediaType, - ttl: 3600 + ttl: 3600, + meta: { + advertiserDomains: bidResponse.adomain || [] + } }; if (mediaType === DISPLAY) { diff --git a/test/spec/modules/adtargetBidAdapter_spec.js b/test/spec/modules/adtargetBidAdapter_spec.js index 5a867e7dd52..d1221d24022 100644 --- a/test/spec/modules/adtargetBidAdapter_spec.js +++ b/test/spec/modules/adtargetBidAdapter_spec.js @@ -45,7 +45,8 @@ const SERVER_VIDEO_RESPONSE = { 'height': 480, 'cur': 'USD', 'width': 640, - 'cpm': 0.9 + 'cpm': 0.9, + 'adomain': ['a.com'] }] }; const SERVER_DISPLAY_RESPONSE = { @@ -107,7 +108,10 @@ const videoEqResponse = [{ height: 480, width: 640, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: ['a.com'] + } }]; const displayEqResponse = [{ @@ -120,7 +124,10 @@ const displayEqResponse = [{ height: 250, width: 300, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: [] + } }]; describe('adtargetBidAdapter', () => { diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index e6916997133..4cfb367efb3 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -80,9 +80,9 @@ const SERVER_VIDEO_RESPONSE = { 'height': 480, 'cur': 'USD', 'width': 640, - 'cpm': 0.9 - } - ] + 'cpm': 0.9, + 'adomain': ['a.com'] + }] }; const SERVER_OUSTREAM_VIDEO_RESPONSE = SERVER_VIDEO_RESPONSE; const SERVER_DISPLAY_RESPONSE = { @@ -163,7 +163,10 @@ const videoEqResponse = [{ height: 480, width: 640, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: ['a.com'] + } }]; const displayEqResponse = [{ @@ -177,7 +180,11 @@ const displayEqResponse = [{ height: 250, width: 300, ttl: 300, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: [] + } + }]; describe('adtelligentBidAdapter', () => { diff --git a/test/spec/modules/viewdeosDXBidAdapter_spec.js b/test/spec/modules/viewdeosDXBidAdapter_spec.js index f9bee1b0efe..31df9244ada 100644 --- a/test/spec/modules/viewdeosDXBidAdapter_spec.js +++ b/test/spec/modules/viewdeosDXBidAdapter_spec.js @@ -39,9 +39,9 @@ const SERVER_VIDEO_RESPONSE = { 'height': 480, 'cur': 'USD', 'width': 640, - 'cpm': 0.9 - } - ] + 'cpm': 0.9, + 'adomain': ['a.com'] + }] }; const SERVER_OUSTREAM_VIDEO_RESPONSE = SERVER_VIDEO_RESPONSE; @@ -123,7 +123,10 @@ const videoEqResponse = [{ height: 480, width: 640, ttl: 3600, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: ['a.com'] + } }]; const displayEqResponse = [{ @@ -136,7 +139,10 @@ const displayEqResponse = [{ height: 250, width: 300, ttl: 3600, - cpm: 0.9 + cpm: 0.9, + meta: { + advertiserDomains: [] + } }]; describe('viewdeosDXBidAdapter', function () { From 42093f41109a9e7e5505243c9cd77d4a2dc932f3 Mon Sep 17 00:00:00 2001 From: Giudici-a <34242194+Giudici-a@users.noreply.github.com> Date: Wed, 2 Jun 2021 17:07:44 +0200 Subject: [PATCH 62/63] Adot Bid Adapter: add the advertising domains support (#6876) --- modules/adotBidAdapter.js | 54 +++++- modules/adotBidAdapter.md | 44 +++-- test/spec/modules/adotBidAdapter_spec.js | 225 +++++++++++++---------- 3 files changed, 191 insertions(+), 132 deletions(-) diff --git a/modules/adotBidAdapter.js b/modules/adotBidAdapter.js index 502ef05b0d5..ddd9531eb43 100644 --- a/modules/adotBidAdapter.js +++ b/modules/adotBidAdapter.js @@ -74,7 +74,7 @@ function isBanner(mediaTypes) { } function isVideo(mediaTypes) { - return isPlainObject(mediaTypes) && isPlainObject(mediaTypes.video); + return isPlainObject(mediaTypes) && 'video' in mediaTypes; } function validateBanner(banner) { @@ -104,13 +104,16 @@ function validateMediaSizes(mediaSize) { function validateParameters(parameters, adUnit) { if (isVideo(adUnit.mediaTypes)) { if (!isPlainObject(parameters)) return false; + if (!isPlainObject(adUnit.mediaTypes.video)) return false; if (!validateVideoParameters(parameters.video, adUnit)) return false; } return true; } -function validateVideoParameters(video, adUnit) { +function validateVideoParameters(videoParams, adUnit) { + const video = adUnit.mediaTypes.video; + if (!video) return false; if (!isArray(video.mimes)) return false; @@ -124,9 +127,9 @@ function validateVideoParameters(video, adUnit) { if (video.protocols.length === 0) return false; if (!video.protocols.every(isNumber)) return false; - if (isInstream(adUnit.mediaTypes.video)) { - if (!video.instreamContext) return false; - if (SUPPORTED_INSTREAM_CONTEXTS.indexOf(video.instreamContext) === -1) return false; + if (isInstream(video)) { + if (!videoParams.instreamContext) return false; + if (SUPPORTED_INSTREAM_CONTEXTS.indexOf(videoParams.instreamContext) === -1) return false; } return true; @@ -203,7 +206,7 @@ function generateImpressionsFromAdUnit(acc, adUnit) { if (mediaType === 'banner') return acc.concat(generateBannerFromAdUnit(impId, data, params)); if (mediaType === 'video') return acc.concat({id: impId, video: generateVideoFromAdUnit(data, params), pmp, ext}); - if (mediaType === 'native') return acc.concat({id: impId, native: generateNativeFromAdUnit(data, params), pmp, ext}); + if (mediaType === 'native') return acc.concat({id: impId, native: generateNativeFromAdUnit(data), pmp, ext}); }, []); return acc.concat(imps); @@ -226,25 +229,52 @@ function generateBannerFromAdUnit(impId, data, params) { function generateVideoFromAdUnit(data, params) { const {playerSize} = data; + const video = data + const hasPlayerSize = isArray(playerSize) && playerSize.length > 0; - const {position, video = {}} = params; const {minDuration, maxDuration, protocols} = video; const size = {width: hasPlayerSize ? playerSize[0][0] : null, height: hasPlayerSize ? playerSize[0][1] : null}; const duration = {min: isNumber(minDuration) ? minDuration : null, max: isNumber(maxDuration) ? maxDuration : null}; + const startdelay = computeStartDelay(data, params); return { mimes: SUPPORTED_VIDEO_MIMES, + skip: video.skippable || 0, w: size.width, h: size.height, - startdelay: computeStartDelay(data, params), + startdelay: startdelay, + linearity: video.linearity || null, minduration: duration.min, maxduration: duration.max, protocols, - pos: position || 0 + api: getApi(protocols), + format: hasPlayerSize ? playerSize.map(s => { + return {w: s[0], h: s[1]}; + }) : null, + pos: video.position || 0 }; } +function getApi(protocols) { + let defaultValue = [2]; + let listProtocols = [ + {key: 'VPAID_1_0', value: 1}, + {key: 'VPAID_2_0', value: 2}, + {key: 'MRAID_1', value: 3}, + {key: 'ORMMA', value: 4}, + {key: 'MRAID_2', value: 5}, + {key: 'MRAID_3', value: 6}, + ]; + if (protocols) { + return listProtocols.filter(p => { + return protocols.indexOf(p.key) !== -1; + }).map(p => p.value) + } else { + return defaultValue; + } +} + function isInstream(video) { return isPlainObject(video) && (video.context === 'instream'); } @@ -263,7 +293,7 @@ function computeStartDelay(data, params) { return null; } -function generateNativeFromAdUnit(data, params) { +function generateNativeFromAdUnit(data) { const {type} = data; const presetFormatter = type && NATIVE_PRESET_FORMATTERS[data.type]; const nativeFields = presetFormatter ? presetFormatter(data) : data; @@ -500,6 +530,10 @@ function generateAdFromBid(bid, bidResponse, serverRequest) { mediaType: bid.ext.adot.media_type, }; + if (bid.adomain) { + base.meta = { advertiserDomains: bid.adomain }; + } + if (isBidANative(bid)) return {...base, native: formatNativeData(bid)}; const size = getSizeFromBid(bid, impressionData); diff --git a/modules/adotBidAdapter.md b/modules/adotBidAdapter.md index 98a4a1a5970..894a592ec18 100644 --- a/modules/adotBidAdapter.md +++ b/modules/adotBidAdapter.md @@ -47,23 +47,22 @@ const adUnit = { context: 'outstream', // Video dimensions supported by the video ad unit. // Each ad unit size is formatted as follows: [width, height]. - playerSize: [[300, 250]] + playerSize: [[300, 250]], + // Content MIME types supported by the ad unit. + mimes: ['video/mp4'], + // Minimum accepted video ad duration (in seconds). + minDuration: 5, + // Maximum accepted video ad duration (in seconds). + maxDuration: 35, + // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, + // section 5.8). + protocols: [2, 3] } }, bids: [{ bidder: 'adot', params: { - video: { - // Content MIME types supported by the ad unit. - mimes: ['video/mp4'], - // Minimum accepted video ad duration (in seconds). - minDuration: 5, - // Maximum accepted video ad duration (in seconds). - maxDuration: 35, - // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, - // section 5.8). - protocols: [2, 3] - } + video: {} } }] } @@ -82,23 +81,22 @@ const adUnit = { context: 'instream', // Video dimensions supported by the video ad unit. // Each ad unit size is formatted as follows: [width, height]. - playerSize: [[300, 250]] + playerSize: [[300, 250]], + // Content MIME types supported by the ad unit. + mimes: ['video/mp4'], + // Minimum accepted video ad duration (in seconds). + minDuration: 5, + // Maximum accepted video ad duration (in seconds). + maxDuration: 35, + // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, + // section 5.8). + protocols: [2, 3] } }, bids: [{ bidder: 'adot', params: { video: { - // Content MIME types supported by the ad unit. - mimes: ['video/mp4'], - // Minimum accepted video ad duration (in seconds). - minDuration: 5, - // Maximum accepted video ad duration (in seconds). - maxDuration: 35, - // Video protocols supported by the ad unit (see the OpenRTB 2.5 specifications, - // section 5.8). - protocols: [2, 3], - // Instream video context. Must be either 'pre-roll', 'mid-roll' or 'post-roll'. instreamContext: 'pre-roll' } } diff --git a/test/spec/modules/adotBidAdapter_spec.js b/test/spec/modules/adotBidAdapter_spec.js index d580cd763a4..40605b17b20 100644 --- a/test/spec/modules/adotBidAdapter_spec.js +++ b/test/spec/modules/adotBidAdapter_spec.js @@ -25,19 +25,16 @@ describe('Adot Adapter', function () { bidder: 'adot', bidderRequestId: 'bid_request_id', bidId: 'bid_id', - params: { + params: {}, + mediaTypes: { video: { + context: 'outstream', + playerSize: [[300, 250]], mimes: ['video/mp4'], minDuration: 5, maxDuration: 30, protocols: [2, 3] } - }, - mediaTypes: { - video: { - context: 'outstream', - playerSize: [[300, 250]] - } } }, @@ -48,17 +45,17 @@ describe('Adot Adapter', function () { bidId: 'bid_id', params: { video: { - instreamContext: 'pre-roll', - mimes: ['video/mp4'], - minDuration: 5, - maxDuration: 30, - protocols: [2, 3] + instreamContext: 'pre-roll' } }, mediaTypes: { video: { context: 'instream', - playerSize: [[300, 250]] + playerSize: [[300, 250]], + mimes: ['video/mp4'], + minDuration: 5, + maxDuration: 30, + protocols: [2, 3] } } }, @@ -557,6 +554,7 @@ describe('Adot Adapter', function () { price: 1.5, h: 350, w: 300, + adomain: ['adot'], ext: { adot: { media_type: 'banner' @@ -568,6 +566,7 @@ describe('Adot Adapter', function () { crid: 'creative_id_2', adm: 'creative_data_2_${AUCTION_PRICE}', nurl: 'win_notice_url_2_${AUCTION_PRICE}', + adomain: ['adot'], price: 2.5, h: 400, w: 350, @@ -913,14 +912,14 @@ describe('Adot Adapter', function () { it('should return true when given an ad unit without minimum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.minDuration = undefined; + adUnit.mediaTypes.video.minDuration = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(true); }); it('should return true when given an ad unit without maximum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.maxDuration = undefined; + adUnit.mediaTypes.video.maxDuration = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(true); }); @@ -941,84 +940,84 @@ describe('Adot Adapter', function () { it('should return false when given an ad unit without video parameters', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video = undefined; + adUnit.mediaTypes.video = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with invalid video parameters', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video = 'bad_bidder_parameters'; + adUnit.mediaTypes.video = 'bad_bidder_parameters'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit without mime types parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = undefined; + adUnit.mediaTypes.video.mimes = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid mime types parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = 'bad_mime_types'; + adUnit.mediaTypes.video.mimes = 'bad_mime_types'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an empty mime types parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = []; + adUnit.mediaTypes.video.mimes = []; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid mime types parameter value', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.mimes = [200]; + adUnit.mediaTypes.video.mimes = [200]; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid minimum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.minDuration = 'bad_min_duration'; + adUnit.mediaTypes.video.minDuration = 'bad_min_duration'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid maximum duration parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.maxDuration = 'bad_max_duration'; + adUnit.mediaTypes.video.maxDuration = 'bad_max_duration'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit without protocols parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = undefined; + adUnit.mediaTypes.video.protocols = undefined; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid protocols parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = 'bad_protocols'; + adUnit.mediaTypes.video.protocols = 'bad_protocols'; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an empty protocols parameter', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = []; + adUnit.mediaTypes.video.protocols = []; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); it('should return false when given an ad unit with an invalid protocols parameter value', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.protocols = ['bad_protocols_value']; + adUnit.mediaTypes.video.protocols = ['bad_protocols_value']; expect(spec.isBidRequestValid(adUnit)).to.equal(false); }); @@ -1452,13 +1451,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid pre-roll instream ad unit', function () { @@ -1476,13 +1475,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.exist.and.to.be.a('number').and.to.equal(0); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid mid-roll instream ad unit', function () { @@ -1500,13 +1499,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.exist.and.to.be.a('number').and.to.equal(-1); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid post-roll instream ad unit', function () { @@ -1524,13 +1523,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.exist.and.to.be.a('number').and.to.equal(-2); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit without player size', function () { @@ -1548,13 +1547,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.equal(null); expect(serverRequests[0].data.imp[0].video.h).to.equal(null); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit with an empty player size', function () { @@ -1572,13 +1571,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.equal(null); expect(serverRequests[0].data.imp[0].video.h).to.equal(null); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit with multiple player sizes', function () { @@ -1596,18 +1595,18 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit without minimum duration', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.minDuration = undefined; + adUnit.mediaTypes.video.minDuration = undefined; const adUnits = [adUnit]; @@ -1620,18 +1619,18 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); expect(serverRequests[0].data.imp[0].video.minduration).to.equal(null); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with one impression when given a valid ad unit without maximum duration', function () { const adUnit = utils.deepClone(examples.adUnit_video_outstream); - adUnit.params.video.maxDuration = undefined; + adUnit.mediaTypes.video.maxDuration = undefined; const adUnits = [adUnit]; @@ -1644,13 +1643,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); expect(serverRequests[0].data.imp[0].video.maxduration).to.equal(null); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); }); it('should return a server request with two impressions when given two valid ad units with different impression identifiers', function () { @@ -1671,31 +1670,31 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); expect(serverRequests[0].data.imp[1]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[1].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[1].bidId}_0`); expect(serverRequests[0].data.imp[1].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[1].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[1].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[1].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[1].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[1].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[1].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.minDuration); - expect(serverRequests[0].data.imp[1].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.maxDuration); - expect(serverRequests[0].data.imp[1].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].params.video.protocols); + expect(serverRequests[0].data.imp[1].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[1].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[1].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].mediaTypes.video.protocols); }); it('should return a server request with one overridden impression when given two valid ad units with identical identifiers', function () { const adUnit_1 = utils.deepClone(examples.adUnit_video_outstream); - adUnit_1.params.video.minDuration = 10; + adUnit_1.mediaTypes.video.minDuration = 10; const adUnit_2 = utils.deepClone(examples.adUnit_video_outstream); - adUnit_2.params.video.minDuration = 15; + adUnit_2.mediaTypes.video.minDuration = 15; const adUnits = [adUnit_1, adUnit_2]; @@ -1708,13 +1707,13 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[1].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].mediaTypes.video.protocols); }); it('should return two server requests with one impression when given two valid ad units with different bid request identifiers', function () { @@ -1735,25 +1734,25 @@ describe('Adot Adapter', function () { expect(serverRequests[0].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[0].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[0].bidId}_0`); expect(serverRequests[0].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[0].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[0].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[0].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][0]); expect(serverRequests[0].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.minDuration); - expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].params.video.maxDuration); - expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.protocols); + expect(serverRequests[0].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.minDuration); + expect(serverRequests[0].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[0].mediaTypes.video.maxDuration); + expect(serverRequests[0].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.protocols); expect(serverRequests[1].data).to.exist.and.to.be.an('object'); expect(serverRequests[1].data.imp).to.exist.and.to.be.an('array').and.to.have.lengthOf(1); expect(serverRequests[1].data.imp[0]).to.exist.and.to.be.an('object'); expect(serverRequests[1].data.imp[0].id).to.exist.and.to.be.a('string').and.to.equal(`${adUnits[1].bidId}_0`); expect(serverRequests[1].data.imp[0].video).to.exist.and.to.be.an('object'); - expect(serverRequests[1].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].params.video.mimes); + expect(serverRequests[1].data.imp[0].video.mimes).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[0].mediaTypes.video.mimes); expect(serverRequests[1].data.imp[0].video.startdelay).to.equal(null); expect(serverRequests[1].data.imp[0].video.w).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][0]); expect(serverRequests[1].data.imp[0].video.h).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.playerSize[0][1]); - expect(serverRequests[1].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.minDuration); - expect(serverRequests[1].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].params.video.maxDuration); - expect(serverRequests[1].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].params.video.protocols); + expect(serverRequests[1].data.imp[0].video.minduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.minDuration); + expect(serverRequests[1].data.imp[0].video.maxduration).to.exist.and.to.be.a('number').and.to.equal(adUnits[1].mediaTypes.video.maxDuration); + expect(serverRequests[1].data.imp[0].video.protocols).to.exist.and.to.be.an('array').and.to.deep.equal(adUnits[1].mediaTypes.video.protocols); }); }); @@ -2121,6 +2120,48 @@ describe('Adot Adapter', function () { expect(ads[1].renderer).to.equal(null); }); + it('should return two ads when given a valid server response with two bids that contains adomain', function () { + const serverRequest = examples.serverRequest_banner_twoImps; + + const serverResponse = examples.serverResponse_banner_twoBids; + + const ads = spec.interpretResponse(serverResponse, serverRequest); + + const admWithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[0].adm, serverResponse.body.seatbid[0].bid[0].price); + const adm2WithAuctionPriceReplaced = utils.replaceAuctionPrice(serverResponse.body.seatbid[0].bid[1].adm, serverResponse.body.seatbid[0].bid[1].price); + + expect(ads).to.be.an('array').and.to.have.length(2); + + expect(ads[0].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[0].bidId); + expect(ads[0].ad).to.exist.and.to.be.a('string').and.to.have.string(admWithAuctionPriceReplaced); + expect(ads[0].adUrl).to.equal(null); + expect(ads[0].vastXml).to.equal(null); + expect(ads[0].vastUrl).to.equal(null); + expect(ads[0].meta.advertiserDomains[0]).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].adomain[0]) + expect(ads[0].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[0].crid); + expect(ads[0].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].price); + expect(ads[0].currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.cur); + expect(ads[0].netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(true); + expect(ads[0].ttl).to.exist.and.to.be.a('number').and.to.equal(10); + expect(ads[0].height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].h); + expect(ads[0].width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[0].w); + expect(ads[0].mediaType).to.exist.and.to.be.a('string').and.to.equal('banner'); + expect(ads[0].renderer).to.equal(null); + expect(ads[1].requestId).to.exist.and.to.be.a('string').and.to.equal(serverRequest._adot_internal.impressions[1].bidId); + expect(ads[1].meta.advertiserDomains[0]).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[1].adomain[0]) + expect(ads[1].ad).to.exist.and.to.be.a('string').and.to.have.string(adm2WithAuctionPriceReplaced); + expect(ads[1].adUrl).to.equal(null); + expect(ads[1].creativeId).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.seatbid[0].bid[1].crid); + expect(ads[1].cpm).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].price); + expect(ads[1].currency).to.exist.and.to.be.a('string').and.to.equal(serverResponse.body.cur); + expect(ads[1].netRevenue).to.exist.and.to.be.a('boolean').and.to.equal(true); + expect(ads[1].ttl).to.exist.and.to.be.a('number').and.to.equal(10); + expect(ads[1].height).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].h); + expect(ads[1].width).to.exist.and.to.be.a('number').and.to.equal(serverResponse.body.seatbid[0].bid[1].w); + expect(ads[1].mediaType).to.exist.and.to.be.a('string').and.to.equal('banner'); + expect(ads[1].renderer).to.equal(null); + }); + it('should return no ad when not given a server response', function () { const ads = spec.interpretResponse(null); @@ -3084,20 +3125,6 @@ describe('Adot Adapter', function () { expect(ads).to.be.an('array').and.to.have.length(1); expect(ads[0].renderer).to.be.an('object'); }); - - it('should append a command to the ad rendering queue when executing the renderer', function (done) { - const serverRequest = examples.serverRequest_video_outstream; - const serverResponse = examples.serverResponse_video_outstream; - - const [ad] = spec.interpretResponse(serverResponse, serverRequest); - - this.spyAdRenderingQueue(ad); - - executeAdRenderer(ad, () => { - expect(ad.renderer.push.calledOnce).to.equal(true); - expect(ad.renderer.push.firstCall.args[0]).to.exist.and.to.be.a('function'); - }, done); - }); }); }); From b04d2129d4de0752577fcdf061c1363870cb6db0 Mon Sep 17 00:00:00 2001 From: Jonas Gresens <5290643+jgresens@users.noreply.github.com> Date: Wed, 2 Jun 2021 17:26:56 +0200 Subject: [PATCH 63/63] Smaato bid adapter: Rework multi imp support (#6814) * Smaato bid adapter: Refactor, clean and add tests * Smaato bid adapter: Create individual request to Smaatos server per valid bid request --- modules/smaatoBidAdapter.js | 181 ++-- test/spec/modules/smaatoBidAdapter_spec.js | 968 +++++++++++---------- 2 files changed, 604 insertions(+), 545 deletions(-) diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index fbb4c14e5f8..719d78892ce 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -5,75 +5,22 @@ import { BANNER, VIDEO } from '../src/mediaTypes.js'; const BIDDER_CODE = 'smaato'; const SMAATO_ENDPOINT = 'https://prebid.ad.smaato.net/oapi/prebid'; -const CLIENT = 'prebid_js_$prebid.version$_1.1' - -/** -* Transform BidRequest to OpenRTB-formatted BidRequest Object -* @param {Array} validBidRequests -* @param {any} bidderRequest -* @returns {string} -*/ -const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { - /** - * Turn incoming prebid sizes into openRtb format mapping. - * @param {*} sizes in format [[10, 10], [20, 20]] - * @returns array of openRtb format mappings [{w: 10, h: 10}, {w: 20, h: 20}] - */ - const parseSizes = (sizes) => { - return sizes.map((size) => { - return {w: size[0], h: size[1]}; - }) - } - - const imp = validBidRequests.map(br => { - const bannerMediaType = utils.deepAccess(br, 'mediaTypes.banner'); - const videoMediaType = utils.deepAccess(br, 'mediaTypes.video'); - let result = { - id: br.bidId, - tagid: utils.deepAccess(br, 'params.adspaceId') - } - - if (bannerMediaType) { - const sizes = parseSizes(utils.getAdUnitSizes(br)); - result.banner = { - w: sizes[0].w, - h: sizes[0].h, - format: sizes - } - } - - if (videoMediaType) { - result.video = { - mimes: videoMediaType.mimes, - minduration: videoMediaType.minduration, - startdelay: videoMediaType.startdelay, - linearity: videoMediaType.linearity, - w: videoMediaType.playerSize[0][0], - h: videoMediaType.playerSize[0][1], - maxduration: videoMediaType.maxduration, - skip: videoMediaType.skip, - protocols: videoMediaType.protocols, - ext: { - rewarded: videoMediaType.ext && videoMediaType.ext.rewarded ? videoMediaType.ext.rewarded : 0 - }, - skipmin: videoMediaType.skipmin, - api: videoMediaType.api - } - } - - return result; - }); +const SMAATO_CLIENT = 'prebid_js_$prebid.version$_1.2' +const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { const request = { id: bidderRequest.auctionId, at: 1, - imp, + imp: [{ + id: bidRequest.bidId, + tagid: utils.deepAccess(bidRequest, 'params.adspaceId') + }], cur: ['USD'], tmax: bidderRequest.timeout, site: { id: window.location.hostname, publisher: { - id: utils.deepAccess(validBidRequests[0], 'params.publisherId') + id: utils.deepAccess(bidRequest, 'params.publisherId') }, domain: window.location.hostname, page: window.location.href, @@ -94,12 +41,40 @@ const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { ext: {} }, ext: { - client: CLIENT + client: SMAATO_CLIENT } }; - let ortb2 = config.getConfig('ortb2') || {}; + if (utils.deepAccess(bidRequest, 'mediaTypes.banner')) { + const sizes = utils.getAdUnitSizes(bidRequest).map((size) => ({w: size[0], h: size[1]})); + request.imp[0].banner = { + w: sizes[0].w, + h: sizes[0].h, + format: sizes + } + } + const videoMediaType = utils.deepAccess(bidRequest, 'mediaTypes.video'); + if (videoMediaType) { + request.imp[0].video = { + mimes: videoMediaType.mimes, + minduration: videoMediaType.minduration, + startdelay: videoMediaType.startdelay, + linearity: videoMediaType.linearity, + w: videoMediaType.playerSize[0][0], + h: videoMediaType.playerSize[0][1], + maxduration: videoMediaType.maxduration, + skip: videoMediaType.skip, + protocols: videoMediaType.protocols, + ext: { + rewarded: videoMediaType.ext && videoMediaType.ext.rewarded ? videoMediaType.ext.rewarded : 0 + }, + skipmin: videoMediaType.skipmin, + api: videoMediaType.api + } + } + + let ortb2 = config.getConfig('ortb2') || {}; Object.assign(request.user, ortb2.user); Object.assign(request.site, ortb2.site); @@ -112,20 +87,19 @@ const buildOpenRtbBidRequestPayload = (validBidRequests, bidderRequest) => { utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (utils.deepAccess(validBidRequests[0], 'params.app')) { - const geo = utils.deepAccess(validBidRequests[0], 'params.app.geo'); + if (utils.deepAccess(bidRequest, 'params.app')) { + const geo = utils.deepAccess(bidRequest, 'params.app.geo'); utils.deepSetValue(request, 'device.geo', geo); - const ifa = utils.deepAccess(validBidRequests[0], 'params.app.ifa') + const ifa = utils.deepAccess(bidRequest, 'params.app.ifa') utils.deepSetValue(request, 'device.ifa', ifa); } - const eids = utils.deepAccess(validBidRequests[0], 'userIdAsEids'); + const eids = utils.deepAccess(bidRequest, 'userIdAsEids'); if (eids && eids.length) { utils.deepSetValue(request, 'user.ext.eids', eids); } - utils.logInfo('[SMAATO] OpenRTB Request:', request); - return JSON.stringify(request); + return request } export const spec = { @@ -133,41 +107,41 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO], /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ + * Determines whether or not the given bid request is valid. + * + * @param {BidRequest} bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ isBidRequestValid: (bid) => { return typeof bid.params === 'object' && - typeof bid.params.publisherId === 'string' && - typeof bid.params.adspaceId === 'string'; + typeof bid.params.publisherId === 'string' && + typeof bid.params.adspaceId === 'string'; }, - /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @return ServerRequest Info describing the request to the server. - */ buildRequests: (validBidRequests, bidderRequest) => { - utils.logInfo('[SMAATO] Client version:', CLIENT); - return { - method: 'POST', - url: validBidRequests[0].params.endpoint || SMAATO_ENDPOINT, - data: buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest), - options: { - withCredentials: true, - crossOrigin: true, - } - }; + utils.logInfo('[SMAATO] Client version:', SMAATO_CLIENT); + + return validBidRequests.map((validBidRequest) => { + const openRtbBidRequest = buildOpenRtbBidRequest(validBidRequest, bidderRequest); + utils.logInfo('[SMAATO] OpenRTB Request:', openRtbBidRequest); + + return { + method: 'POST', + url: validBidRequest.params.endpoint || SMAATO_ENDPOINT, + data: JSON.stringify(openRtbBidRequest), + options: { + withCredentials: true, + crossOrigin: true, + } + }; + }); }, /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ + * Unpack the response from the server into a list of bids. + * + * @param {ServerResponse} serverResponse A successful response from the server. + * @return {Bid[]} An array of bids which were nested inside the server. + */ interpretResponse: (serverResponse, bidRequest) => { // response is empty (HTTP 204) if (utils.isEmpty(serverResponse.body)) { @@ -235,15 +209,14 @@ export const spec = { }, /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @return {UserSync[]} The user syncs which should be dropped. - */ + * Register the user sync pixels which should be dropped after the auction. + * + * @param {SyncOptions} syncOptions Which user syncs are allowed? + * @param {ServerResponse[]} serverResponses List of server's responses. + * @return {UserSync[]} The user syncs which should be dropped. + */ getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { - const syncs = [] - return syncs; + return []; } } registerBidder(spec); diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 1bc77fc9572..0abc8463d28 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -1,200 +1,36 @@ import { spec } from 'modules/smaatoBidAdapter.js'; import * as utils from 'src/utils.js'; -import {config} from 'src/config.js'; -import {createEidsArray} from 'modules/userId/eids.js'; - -const imageAd = { - image: { - img: { - url: 'https://prebid/static/ad.jpg', - w: 320, - h: 50, - ctaurl: 'https://prebid/track/ctaurl' - }, - impressiontrackers: [ - 'https://prebid/track/imp/1', - 'https://prebid/track/imp/2' - ], - clicktrackers: [ - 'https://prebid/track/click/1' - ] - } -}; - -const richmediaAd = { - richmedia: { - mediadata: { - content: '

RICHMEDIA CONTENT

', - w: 800, - h: 600 - }, - impressiontrackers: [ - 'https://prebid/track/imp/1', - 'https://prebid/track/imp/2' - ], - clicktrackers: [ - 'https://prebid/track/click/1' - ] - } -}; +import { config } from 'src/config.js'; +import { createEidsArray } from 'modules/userId/eids.js'; const ADTYPE_IMG = 'Img'; const ADTYPE_RICHMEDIA = 'Richmedia'; const ADTYPE_VIDEO = 'Video'; -const site = { - keywords: 'power tools,drills' -}; - -const user = { - keywords: 'a,b', - gender: 'M', - yob: 1984 -}; - -const openRtbBidResponse = (adType) => { - let adm = ''; - - switch (adType) { - case ADTYPE_IMG: - adm = JSON.stringify(imageAd); - break; - case ADTYPE_RICHMEDIA: - adm = JSON.stringify(richmediaAd); - break; - case ADTYPE_VIDEO: - adm = ''; - break; - default: - throw Error('Invalid AdType'); - } - - let resp = { - body: { - bidid: '04db8629-179d-4bcd-acce-e54722969006', - cur: 'USD', - ext: {}, - id: '5ebea288-f13a-4754-be6d-4ade66c68877', - seatbid: [ - { - bid: [ - { - 'adm': adm, - 'adomain': [ - 'smaato.com' - ], - 'bidderName': 'smaato', - 'cid': 'CM6523', - 'crid': 'CR69381', - 'dealid': '12345', - 'id': '6906aae8-7f74-4edd-9a4f-f49379a3cadd', - 'impid': '226416e6e6bf41', - 'iurl': 'https://prebid/iurl', - 'nurl': 'https://prebid/nurl', - 'price': 0.01, - 'w': 350, - 'h': 50 - } - ], - seat: 'CM6523' - } - ], - }, - headers: { - get: function (header) { - if (header === 'X-SMT-ADTYPE') { - return adType; - } - } - } - }; - return resp; -}; - const request = { method: 'POST', url: 'https://prebid.ad.smaato.net/oapi/prebid', data: '' }; -const interpretedBidsImg = [ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - ad: '
\"\"\"\"
', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'banner' - } - } -]; - -const interpretedBidsRichmedia = [ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - ad: '

RICHMEDIA CONTENT

\"\"\"\"
', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'banner' - } - } -]; - -const interpretedBidsVideo = [ - { - requestId: '226416e6e6bf41', - cpm: 0.01, - width: 350, - height: 50, - vastXml: '', - ttl: 300, - creativeId: 'CR69381', - dealId: '12345', - netRevenue: true, - currency: 'USD', - meta: { - advertiserDomains: ['smaato.com'], - agencyId: 'CM6523', - networkName: 'smaato', - mediaType: 'video' - } - } -]; +const REFERRER = 'http://example.com/page.html' +const CONSENT_STRING = 'HFIDUYFIUYIUYWIPOI87392DSU' const defaultBidderRequest = { gdprConsent: { - consentString: 'HFIDUYFIUYIUYWIPOI87392DSU', + consentString: CONSENT_STRING, gdprApplies: true }, uspConsent: 'uspConsentString', refererInfo: { - referer: 'http://example.com/page.html', + referer: REFERRER, }, timeout: 1200 }; const minimalBidderRequest = { refererInfo: { - referer: 'http://example.com/page.html', + referer: REFERRER, } }; @@ -221,77 +57,6 @@ const singleBannerBidRequest = { bidderWinsCount: 0 }; -const singleVideoBidRequest = { - bidder: 'smaato', - params: { - publisherId: 'publisherId', - adspaceId: 'adspaceId' - }, - mediaTypes: { - video: { - context: 'outstream', - playerSize: [[768, 1024]], - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - maxduration: 30, - startdelay: 0, - linearity: 1, - protocols: [7], - skip: 1, - skipmin: 5, - api: [7], - ext: {rewarded: 0} - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'transactionId', - sizes: [[300, 50]], - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 -}; - -const combinedBannerAndVideoBidRequest = { - bidder: 'smaato', - params: { - publisherId: 'publisherId', - adspaceId: 'adspaceId' - }, - mediaTypes: { - banner: { - sizes: [[300, 50]] - }, - video: { - context: 'outstream', - playerSize: [[768, 1024]], - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - maxduration: 30, - startdelay: 0, - linearity: 1, - protocols: [7], - skip: 1, - skipmin: 5, - api: [7], - ext: {rewarded: 0} - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'transactionId', - sizes: [[300, 50]], - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0 -}; - const inAppBidRequest = { bidder: 'smaato', params: { @@ -322,36 +87,10 @@ const inAppBidRequest = { bidderWinsCount: 0 }; -const userIdBidRequest = { - bidder: 'smaato', - params: { - publisherId: 'publisherId', - adspaceId: 'adspaceId' - }, - mediaTypes: { - banner: { - sizes: [[300, 50]] - } - }, - adUnitCode: '/19968336/header-bid-tag-0', - transactionId: 'transactionId', - sizes: [[300, 50]], - bidId: 'bidId', - bidderRequestId: 'bidderRequestId', - auctionId: 'auctionId', - src: 'client', - bidRequestsCount: 1, - bidderRequestsCount: 1, - bidderWinsCount: 0, - userId: { - criteoId: '123456', - tdid: '89145' - }, - userIdAsEids: createEidsArray({ - criteoId: '123456', - tdid: '89145' - }) -}; +const extractPayloadOfFirstAndOnlyRequest = (reqs) => { + expect(reqs).to.have.length(1); + return JSON.parse(reqs[0].data); +} describe('smaatoBidAdapterTest', () => { describe('isBidRequestValid', () => { @@ -368,222 +107,557 @@ describe('smaatoBidAdapterTest', () => { }); describe('buildRequests', () => { - beforeEach(() => { - this.req = JSON.parse(spec.buildRequests([singleBannerBidRequest], defaultBidderRequest).data); - this.sandbox = sinon.sandbox.create(); - }); + describe('common', () => { + it('auction type is 1 (first price auction)', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); - afterEach(() => { - this.sandbox.restore(); - }); + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.at).to.be.equal(1); + }) - it('can override endpoint', () => { - const overridenEndpoint = 'https://prebid/bidder'; - let bidRequest = utils.deepClone(singleBannerBidRequest); - utils.deepSetValue(bidRequest, 'params.endpoint', overridenEndpoint); - const actualEndpoint = spec.buildRequests([bidRequest], defaultBidderRequest).url; - expect(actualEndpoint).to.equal(overridenEndpoint); - }); + it('currency is US dollar', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); - it('sends correct imps', () => { - expect(this.req.imp).to.deep.equal([ - { - id: 'bidId', - banner: { - w: 300, - h: 50, - format: [ - { - h: 50, - w: 300 + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.cur).to.be.deep.equal(['USD']); + }) + + it('can override endpoint', () => { + const overridenEndpoint = 'https://prebid/bidder'; + const updatedBidRequest = utils.deepClone(singleBannerBidRequest); + utils.deepSetValue(updatedBidRequest, 'params.endpoint', overridenEndpoint); + + const reqs = spec.buildRequests([updatedBidRequest], defaultBidderRequest); + + expect(reqs).to.have.length(1); + expect(reqs[0].url).to.equal(overridenEndpoint); + }); + + it('sends correct imp', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + banner: { + w: 300, + h: 50, + format: [ + { + h: 50, + w: 300 + } + ] + }, + tagid: 'adspaceId' + } + ]); + }); + + it('sends correct site', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.site.id).to.exist.and.to.be.a('string'); + expect(req.site.domain).to.exist.and.to.be.a('string'); + expect(req.site.page).to.exist.and.to.be.a('string'); + expect(req.site.ref).to.equal(REFERRER); + expect(req.site.publisher.id).to.equal('publisherId'); + }) + + it('sends gdpr applies if exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.gdpr).to.equal(1); + expect(req.user.ext.consent).to.equal(CONSENT_STRING); + }); + + it('sends no gdpr applies if no gdpr exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], minimalBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.gdpr).to.not.exist; + expect(req.user.ext.consent).to.not.exist; + }); + + it('sends us_privacy if exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.us_privacy).to.equal('uspConsentString'); + }); + + it('sends tmax', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.tmax).to.equal(1200); + }); + + it('sends no us_privacy if no us_privacy exists', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], minimalBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.regs.ext.us_privacy).to.not.exist; + }); + + it('sends first party data', () => { + this.sandbox = sinon.sandbox.create() + this.sandbox.stub(config, 'getConfig').callsFake(key => { + const config = { + ortb2: { + site: { + keywords: 'power tools,drills' + }, + user: { + keywords: 'a,b', + gender: 'M', + yob: 1984 } - ] - }, - tagid: 'adspaceId' - } - ]) - }); + } + }; + return utils.deepAccess(config, key); + }); + + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.user.gender).to.equal('M'); + expect(req.user.yob).to.equal(1984); + expect(req.user.keywords).to.eql('a,b'); + expect(req.user.ext.consent).to.equal(CONSENT_STRING); + expect(req.site.keywords).to.eql('power tools,drills'); + expect(req.site.publisher.id).to.equal('publisherId'); + this.sandbox.restore(); + }); - it('sends correct site', () => { - expect(this.req.site.id).to.exist.and.to.be.a('string'); - expect(this.req.site.domain).to.exist.and.to.be.a('string'); - expect(this.req.site.page).to.exist.and.to.be.a('string'); - expect(this.req.site.ref).to.equal('http://example.com/page.html'); - expect(this.req.site.publisher.id).to.equal('publisherId'); - }) + it('has no user ids', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); - it('sends gdpr applies if exists', () => { - expect(this.req.regs.ext.gdpr).to.equal(1); - expect(this.req.user.ext.consent).to.equal('HFIDUYFIUYIUYWIPOI87392DSU'); + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.user.ext.eids).to.not.exist; + }); }); - it('sends no gdpr applies if no gdpr exists', () => { - let req_without_gdpr = JSON.parse(spec.buildRequests([singleBannerBidRequest], minimalBidderRequest).data); - expect(req_without_gdpr.regs.ext.gdpr).to.not.exist; - expect(req_without_gdpr.user.ext.consent).to.not.exist; - }); + describe('multiple requests', () => { + it('build individual server request for each bid request', () => { + const bidRequest1 = utils.deepClone(singleBannerBidRequest); + const bidRequest1BidId = '1111'; + utils.deepSetValue(bidRequest1, 'bidId', bidRequest1BidId); + const bidRequest2 = utils.deepClone(singleBannerBidRequest); + const bidRequest2BidId = '2222'; + utils.deepSetValue(bidRequest2, 'bidId', bidRequest2BidId); - it('sends usp if exists', () => { - expect(this.req.regs.ext.us_privacy).to.equal('uspConsentString'); - }); + const reqs = spec.buildRequests([bidRequest1, bidRequest2], defaultBidderRequest); - it('sends tmax', () => { - expect(this.req.tmax).to.equal(1200); + expect(reqs).to.have.length(2); + expect(JSON.parse(reqs[0].data).imp[0].id).to.be.equal(bidRequest1BidId); + expect(JSON.parse(reqs[1].data).imp[0].id).to.be.equal(bidRequest2BidId); + }); }); - it('sends no usp if no usp exists', () => { - let req_without_usp = JSON.parse(spec.buildRequests([singleBannerBidRequest], minimalBidderRequest).data); - expect(req_without_usp.regs.ext.us_privacy).to.not.exist; - }); + describe('buildRequests for video imps', () => { + it('sends correct video imps', () => { + const singleVideoBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + video: { + context: 'outstream', + playerSize: [[768, 1024]], + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + maxduration: 30, + startdelay: 0, + linearity: 1, + protocols: [7], + skip: 1, + skipmin: 5, + api: [7], + ext: {rewarded: 0} + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 + }; - it('sends fp data', () => { - this.sandbox.stub(config, 'getConfig').callsFake(key => { - const config = { - ortb2: { - site, - user + const reqs = spec.buildRequests([singleVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + video: { + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + startdelay: 0, + linearity: 1, + h: 1024, + maxduration: 30, + skip: 1, + protocols: [7], + ext: { + rewarded: 0 + }, + skipmin: 5, + api: [7], + w: 768 + }, + tagid: 'adspaceId' } + ]); + }); + + it('allows combined banner and video imp in single bid request', () => { + const combinedBannerAndVideoBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + banner: { + sizes: [[300, 50]] + }, + video: { + context: 'outstream', + playerSize: [[768, 1024]], + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + maxduration: 30, + startdelay: 0, + linearity: 1, + protocols: [7], + skip: 1, + skipmin: 5, + api: [7], + ext: {rewarded: 0} + } + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 }; - return utils.deepAccess(config, key); + + const reqs = spec.buildRequests([combinedBannerAndVideoBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp).to.deep.equal([ + { + id: 'bidId', + banner: { + w: 300, + h: 50, + format: [ + { + h: 50, + w: 300 + } + ] + }, + video: { + mimes: ['video/mp4', 'video/quicktime', 'video/3gpp', 'video/x-m4v'], + minduration: 5, + startdelay: 0, + linearity: 1, + h: 1024, + maxduration: 30, + skip: 1, + protocols: [7], + ext: { + rewarded: 0 + }, + skipmin: 5, + api: [7], + w: 768 + }, + tagid: 'adspaceId' + } + ]); }); - let bidRequest = utils.deepClone(singleBannerBidRequest); - let req_fpd = JSON.parse(spec.buildRequests([bidRequest], defaultBidderRequest).data); - expect(req_fpd.user.gender).to.equal('M'); - expect(req_fpd.user.yob).to.equal(1984); - expect(req_fpd.user.keywords).to.eql('a,b'); - expect(req_fpd.user.ext.consent).to.equal('HFIDUYFIUYIUYWIPOI87392DSU'); - expect(req_fpd.site.keywords).to.eql('power tools,drills'); - expect(req_fpd.site.publisher.id).to.equal('publisherId'); }); - it('has no user ids', () => { - expect(this.req.user.ext.eids).to.not.exist; + describe('in-app requests', () => { + it('add geo and ifa info to device object', () => { + const reqs = spec.buildRequests([inAppBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.device.geo).to.deep.equal({'lat': 33.3, 'lon': -88.8}); + expect(req.device.ifa).to.equal('aDeviceId'); + }); + + it('when geo is missing, then add only ifa to device object', () => { + const inAppBidRequestWithoutGeo = utils.deepClone(inAppBidRequest); + delete inAppBidRequestWithoutGeo.params.app.geo + + const reqs = spec.buildRequests([inAppBidRequestWithoutGeo], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.device.geo).to.not.exist; + expect(req.device.ifa).to.equal('aDeviceId'); + }); + + it('add no specific device info if param does not exist', () => { + const reqs = spec.buildRequests([singleBannerBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.device.geo).to.not.exist; + expect(req.device.ifa).to.not.exist; + }); }); - }); - describe('buildRequests for video imps', () => { - it('sends correct video imps', () => { - let req = JSON.parse(spec.buildRequests([singleVideoBidRequest], defaultBidderRequest).data); - expect(req.imp).to.deep.equal([ - { - id: 'bidId', - video: { - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - startdelay: 0, - linearity: 1, - h: 1024, - maxduration: 30, - skip: 1, - protocols: [7], - ext: { - rewarded: 0 - }, - skipmin: 5, - api: [7], - w: 768 + describe('user ids in requests', () => { + it('user ids are added to user.ext.eids', () => { + const userIdBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' }, - tagid: 'adspaceId' - } - ]) - }); - it('allows combines banner and video imp in single bid request', () => { - let req = JSON.parse(spec.buildRequests([combinedBannerAndVideoBidRequest], defaultBidderRequest).data); - expect(req.imp).to.deep.equal([ - { - id: 'bidId', - banner: { - w: 300, - h: 50, - format: [ - { - h: 50, - w: 300 - } - ] + mediaTypes: { + banner: { + sizes: [[300, 50]] + } }, - video: { - mimes: ['video\/mp4', 'video\/quicktime', 'video\/3gpp', 'video\/x-m4v'], - minduration: 5, - startdelay: 0, - linearity: 1, - h: 1024, - maxduration: 30, - skip: 1, - protocols: [7], - ext: { - rewarded: 0 - }, - skipmin: 5, - api: [7], - w: 768 + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + sizes: [[300, 50]], + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + auctionId: 'auctionId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0, + userId: { + criteoId: '123456', + tdid: '89145' }, - tagid: 'adspaceId' - } - ]) - }); - it('allows two imps in the same bid request', () => { - let req = JSON.parse(spec.buildRequests([singleBannerBidRequest, singleBannerBidRequest], defaultBidderRequest).data); - expect(req.imp).to.have.length(2); - }); - }); + userIdAsEids: createEidsArray({ + criteoId: '123456', + tdid: '89145' + }) + }; - describe('in-app requests', () => { - it('add geo and ifa info to device object', () => { - let req = JSON.parse(spec.buildRequests([inAppBidRequest], defaultBidderRequest).data); - expect(req.device.geo).to.deep.equal({'lat': 33.3, 'lon': -88.8}); - expect(req.device.ifa).to.equal('aDeviceId'); - }); - it('add only ifa to device object', () => { - let inAppBidRequestWithoutGeo = utils.deepClone(inAppBidRequest); - delete inAppBidRequestWithoutGeo.params.app.geo - let req = JSON.parse(spec.buildRequests([inAppBidRequestWithoutGeo], defaultBidderRequest).data); + const reqs = spec.buildRequests([userIdBidRequest], defaultBidderRequest); - expect(req.device.geo).to.not.exist; - expect(req.device.ifa).to.equal('aDeviceId'); - }); - it('add no specific device info if param does not exist', () => { - let req = JSON.parse(spec.buildRequests([singleBannerBidRequest], defaultBidderRequest).data); - expect(req.device.geo).to.not.exist; - expect(req.device.ifa).to.not.exist; + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.user.ext.eids).to.exist; + expect(req.user.ext.eids).to.have.length(2); + }); }); }); - describe('user ids in requests', () => { - it('user ids are added to user.ext.eids', () => { - let req = JSON.parse(spec.buildRequests([userIdBidRequest], defaultBidderRequest).data); - expect(req.user.ext.eids).to.exist; - expect(req.user.ext.eids).to.have.length(2); + describe('interpretResponse', () => { + const buildOpenRtbBidResponse = (adType) => { + let adm = ''; + + switch (adType) { + case ADTYPE_IMG: + adm = JSON.stringify({ + image: { + img: { + url: 'https://prebid/static/ad.jpg', + w: 320, + h: 50, + ctaurl: 'https://prebid/track/ctaurl' + }, + impressiontrackers: [ + 'https://prebid/track/imp/1', + 'https://prebid/track/imp/2' + ], + clicktrackers: [ + 'https://prebid/track/click/1' + ] + } + }); + break; + case ADTYPE_RICHMEDIA: + adm = JSON.stringify({ + richmedia: { + mediadata: { + content: '

RICHMEDIA CONTENT

', + w: 800, + h: 600 + }, + impressiontrackers: [ + 'https://prebid/track/imp/1', + 'https://prebid/track/imp/2' + ], + clicktrackers: [ + 'https://prebid/track/click/1' + ] + } + }); + break; + case ADTYPE_VIDEO: + adm = ''; + break; + default: + throw Error('Invalid AdType'); + } + + return { + body: { + bidid: '04db8629-179d-4bcd-acce-e54722969006', + cur: 'USD', + ext: {}, + id: '5ebea288-f13a-4754-be6d-4ade66c68877', + seatbid: [ + { + bid: [ + { + 'adm': adm, + 'adomain': [ + 'smaato.com' + ], + 'bidderName': 'smaato', + 'cid': 'CM6523', + 'crid': 'CR69381', + 'dealid': '12345', + 'id': '6906aae8-7f74-4edd-9a4f-f49379a3cadd', + 'impid': '226416e6e6bf41', + 'iurl': 'https://prebid/iurl', + 'nurl': 'https://prebid/nurl', + 'price': 0.01, + 'w': 350, + 'h': 50 + } + ], + seat: 'CM6523' + } + ], + }, + headers: { + get: function (header) { + if (header === 'X-SMT-ADTYPE') { + return adType; + } + } + } + }; + }; + + it('returns empty array on no bid responses', () => { + const response_with_empty_body = {body: {}} + + const bids = spec.interpretResponse(response_with_empty_body, request); + + expect(bids).to.be.empty }); - }); - describe('interpretResponse', () => { it('single image reponse', () => { - const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_IMG), request); - assert.deepStrictEqual(bids, interpretedBidsImg); + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_IMG), request); + + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + ad: '
', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'banner' + } + } + ]); }); + it('single richmedia reponse', () => { - const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_RICHMEDIA), request); - assert.deepStrictEqual(bids, interpretedBidsRichmedia); + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_RICHMEDIA), request); + + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + ad: '

RICHMEDIA CONTENT

', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'banner' + } + } + ]); }); + it('single video reponse', () => { - const bids = spec.interpretResponse(openRtbBidResponse(ADTYPE_VIDEO), request); - assert.deepStrictEqual(bids, interpretedBidsVideo); + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_VIDEO), request); + + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + vastXml: '', + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'video' + } + } + ]); }); + it('ignores bid response with invalid ad type', () => { - let resp = openRtbBidResponse(ADTYPE_IMG); + const resp = buildOpenRtbBidResponse(ADTYPE_IMG); resp.headers.get = (header) => { if (header === 'X-SMT-ADTYPE') { return undefined; } } + const bids = spec.interpretResponse(resp, request); + expect(bids).to.be.empty }); + it('uses correct TTL when expire header exists', () => { const clock = sinon.useFakeTimers(); clock.tick(2000); - let resp = openRtbBidResponse(ADTYPE_IMG); + const resp = buildOpenRtbBidResponse(ADTYPE_IMG); resp.headers.get = (header) => { if (header === 'X-SMT-ADTYPE') { return ADTYPE_IMG; @@ -592,15 +666,27 @@ describe('smaatoBidAdapterTest', () => { return 2000 + (400 * 1000); } } + const bids = spec.interpretResponse(resp, request); + expect(bids[0].ttl).to.equal(400); + clock.restore(); }); + it('uses net revenue flag send from server', () => { - let resp = openRtbBidResponse(ADTYPE_IMG); + const resp = buildOpenRtbBidResponse(ADTYPE_IMG); resp.body.seatbid[0].bid[0].ext = {net: false}; + const bids = spec.interpretResponse(resp, request); + expect(bids[0].netRevenue).to.equal(false); }) }); + + describe('getUserSyncs', () => { + it('returns no pixels', () => { + expect(spec.getUserSyncs()).to.be.empty + }) + }) });