diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 4246a39bc69..8ada9d59ae2 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -5,108 +5,151 @@ import {config} from '../src/config.js'; import {getPriceBucketString} from '../src/cpmBucketManager.js'; import { Renderer } from '../src/Renderer.js'; const BIDDER_CODE = 'ozone'; -const ALLOWED_LOTAME_PARAMS = ['oz_lotameid', 'oz_lotamepid', 'oz_lotametpid']; // *** PROD *** -const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; -const OZONECOOKIESYNC = 'https://elb.the-ozone-project.com/static/load-cookie.html'; +const ORIGIN = 'https://elb.the-ozone-project.com' // applies only to auction & cookie +const AUCTIONURI = '/openrtb2/auction'; +const OZONECOOKIESYNC = '/static/load-cookie.html'; const OZONE_RENDERER_URL = 'https://prebid.the-ozone-project.com/ozone-renderer.js'; -const OZONEVERSION = '2.4.0'; +const OZONEVERSION = '2.5.0'; export const spec = { + gvlid: 524, + aliases: [{ code: 'lmc' }], + version: OZONEVERSION, code: BIDDER_CODE, supportedMediaTypes: [VIDEO, BANNER], cookieSyncBag: {'publisherId': null, 'siteId': null, 'userIdObject': {}}, // variables we want to make available to cookie sync - propertyBag: {'lotameWasOverridden': 0, 'pageId': null, 'buildRequestsStart': 0, 'buildRequestsEnd': 0}, /* allow us to store vars in instance scope - needs to be an object to be mutable */ - + propertyBag: {'pageId': null, 'buildRequestsStart': 0, 'buildRequestsEnd': 0}, /* allow us to store vars in instance scope - needs to be an object to be mutable */ + whitelabel_defaults: { + 'logId': 'OZONE', + 'bidder': 'ozone', + 'keyPrefix': 'oz', + 'auctionUrl': ORIGIN + AUCTIONURI, + 'cookieSyncUrl': ORIGIN + OZONECOOKIESYNC, + 'rendererUrl': OZONE_RENDERER_URL + }, + /** + * make sure that the whitelabel/default values are available in the propertyBag + * @param bid Object : the bid + */ + loadWhitelabelData(bid) { + if (this.propertyBag.whitelabel) { return; } + this.propertyBag.whitelabel = JSON.parse(JSON.stringify(this.whitelabel_defaults)); + let bidder = bid.bidder || 'ozone'; // eg. ozone + this.propertyBag.whitelabel.logId = bidder.toUpperCase(); + this.propertyBag.whitelabel.bidder = bidder; + let bidderConfig = config.getConfig(bidder) || {}; + if (bidderConfig.kvpPrefix) { + this.propertyBag.whitelabel.keyPrefix = bidderConfig.kvpPrefix; + } + if (bidderConfig.endpointOverride) { + if (bidderConfig.endpointOverride.origin) { + this.propertyBag.whitelabel.auctionUrl = bidderConfig.endpointOverride.origin + AUCTIONURI; + this.propertyBag.whitelabel.cookieSyncUrl = bidderConfig.endpointOverride.origin + OZONECOOKIESYNC; + } + if (bidderConfig.endpointOverride.rendererUrl) { + this.propertyBag.whitelabel.rendererUrl = bidderConfig.endpointOverride.rendererUrl; + } + } + this.logInfo('set propertyBag.whitelabel to', this.propertyBag.whitelabel); + }, + getAuctionUrl() { + return this.propertyBag.whitelabel.auctionUrl; + }, + getCookieSyncUrl() { + return this.propertyBag.whitelabel.cookieSyncUrl; + }, + getRendererUrl() { + return this.propertyBag.whitelabel.rendererUrl; + }, + /** + * wrappers for this.logInfo logWarn & logError, to add the proper prefix + */ + logInfo() { + if (!this.propertyBag.whitelabel) { return; } + let args = arguments; + args[0] = `${this.propertyBag.whitelabel.logId}: ${arguments[0]}`; + utils.logInfo.apply(this, args); + }, + logError() { + if (!this.propertyBag.whitelabel) { return; } + let args = arguments; + args[0] = `${this.propertyBag.whitelabel.logId}: ${arguments[0]}`; + utils.logError.apply(this, args); + }, + logWarn() { + if (!this.propertyBag.whitelabel) { return; } + let args = arguments; + args[0] = `${this.propertyBag.whitelabel.logId}: ${arguments[0]}`; + utils.logWarn.apply(this, args); + }, /** * Basic check to see whether required parameters are in the request. * @param bid * @returns {boolean} */ isBidRequestValid(bid) { - utils.logInfo('OZONE: isBidRequestValid : ', config.getConfig(), bid); + this.loadWhitelabelData(bid); + this.logInfo('isBidRequestValid : ', config.getConfig(), bid); let adUnitCode = bid.adUnitCode; // adunit[n].code if (!(bid.params.hasOwnProperty('placementId'))) { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : missing placementId : siteId, placementId and publisherId are REQUIRED', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : missing placementId : siteId, placementId and publisherId are REQUIRED', adUnitCode); return false; } if (!this.isValidPlacementId(bid.params.placementId)) { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : placementId must be exactly 10 numeric characters', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : placementId must be exactly 10 numeric characters', adUnitCode); return false; } if (!(bid.params.hasOwnProperty('publisherId'))) { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : missing publisherId : siteId, placementId and publisherId are REQUIRED', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : missing publisherId : siteId, placementId and publisherId are REQUIRED', adUnitCode); return false; } if (!(bid.params.publisherId).toString().match(/^[a-zA-Z0-9\-]{12}$/)) { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : publisherId must be exactly 12 alphanumieric characters including hyphens', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : publisherId must be exactly 12 alphanumieric characters including hyphens', adUnitCode); return false; } if (!(bid.params.hasOwnProperty('siteId'))) { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : missing siteId : siteId, placementId and publisherId are REQUIRED', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : missing siteId : siteId, placementId and publisherId are REQUIRED', adUnitCode); return false; } if (!(bid.params.siteId).toString().match(/^[0-9]{10}$/)) { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : siteId must be exactly 10 numeric characters', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : siteId must be exactly 10 numeric characters', adUnitCode); return false; } if (bid.params.hasOwnProperty('customParams')) { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : customParams should be renamed to customData', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : customParams should be renamed to customData', adUnitCode); return false; } if (bid.params.hasOwnProperty('customData')) { if (!Array.isArray(bid.params.customData)) { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : customData is not an Array', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : customData is not an Array', adUnitCode); return false; } if (bid.params.customData.length < 1) { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : customData is an array but does not contain any elements', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : customData is an array but does not contain any elements', adUnitCode); return false; } if (!(bid.params.customData[0]).hasOwnProperty('targeting')) { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : customData[0] does not contain "targeting"', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : customData[0] does not contain "targeting"', adUnitCode); return false; } if (typeof bid.params.customData[0]['targeting'] != 'object') { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : customData[0] targeting is not an object', adUnitCode); - return false; - } - } - if (bid.params.hasOwnProperty('lotameData')) { - if (typeof bid.params.lotameData !== 'object') { - utils.logError('OZONE: OZONE BID ADAPTER VALIDATION FAILED : lotameData is not an object', adUnitCode); + this.logError('BID ADAPTER VALIDATION FAILED : customData[0] targeting is not an object', adUnitCode); return false; } } if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { - utils.logError('OZONE: No video context key/value in bid. Rejecting bid: ', bid); + this.logError('No video context key/value in bid. Rejecting bid: ', bid); return false; } if (bid.mediaTypes[VIDEO].context !== 'instream' && bid.mediaTypes[VIDEO].context !== 'outstream') { - utils.logError('OZONE: video.context is invalid. Only instream/outstream video is supported. Rejecting bid: ', bid); + this.logError('video.context is invalid. Only instream/outstream video is supported. Rejecting bid: ', bid); return false; } } - // guard against hacks in GET parameters that we might allow - const arrLotameOverride = this.getLotameOverrideParams(); - // lotame override, test params. All 3 must be present, or none. - let lotameKeys = Object.keys(arrLotameOverride); - if (lotameKeys.length === ALLOWED_LOTAME_PARAMS.length) { - utils.logInfo('OZONE: VALIDATION : arrLotameOverride', arrLotameOverride); - for (let i in lotameKeys) { - if (!arrLotameOverride[ALLOWED_LOTAME_PARAMS[i]].toString().match(/^[0-9a-zA-Z]+$/)) { - utils.logError('OZONE: Only letters & numbers allowed in lotame override: ' + i.toString() + ': ' + arrLotameOverride[ALLOWED_LOTAME_PARAMS[i]].toString() + '. Rejecting bid: ', bid); - return false; - } - } - } else if (lotameKeys.length > 0) { - utils.logInfo('OZONE: VALIDATION : arrLotameOverride', arrLotameOverride); - utils.logError('OZONE: lotame override params are incomplete. You must set all ' + ALLOWED_LOTAME_PARAMS.length + ': ' + JSON.stringify(ALLOWED_LOTAME_PARAMS) + ', . Rejecting bid: ', bid); - return false; - } return true; }, @@ -119,8 +162,11 @@ export const spec = { }, buildRequests(validBidRequests, bidderRequest) { + this.loadWhitelabelData(validBidRequests[0]); this.propertyBag.buildRequestsStart = new Date().getTime(); - utils.logInfo(`OZONE: buildRequests time: ${this.propertyBag.buildRequestsStart} ozone v ${OZONEVERSION} validBidRequests`, validBidRequests, 'bidderRequest', bidderRequest); + let whitelabelBidder = this.propertyBag.whitelabel.bidder; // by default = ozone + let whitelabelPrefix = this.propertyBag.whitelabel.keyPrefix; + this.logInfo(`buildRequests time: ${this.propertyBag.buildRequestsStart} v ${OZONEVERSION} validBidRequests`, JSON.parse(JSON.stringify(validBidRequests)), 'bidderRequest', JSON.parse(JSON.stringify(bidderRequest))); // First check - is there any config to block this request? if (this.blockTheRequest()) { return []; @@ -132,31 +178,31 @@ export const spec = { this.cookieSyncBag.publisherId = utils.deepAccess(validBidRequests[0], 'params.publisherId'); htmlParams = validBidRequests[0].params; } - utils.logInfo('OZONE: cookie sync bag', this.cookieSyncBag); - let singleRequest = config.getConfig('ozone.singleRequest'); + this.logInfo('cookie sync bag', this.cookieSyncBag); + let singleRequest = this.getWhitelabelConfigItem('ozone.singleRequest'); singleRequest = singleRequest !== false; // undefined & true will be true - utils.logInfo('OZONE: config ozone.singleRequest : ', singleRequest); + this.logInfo(`config ${whitelabelBidder}.singleRequest : `, singleRequest); let ozoneRequest = {}; // we only want to set specific properties on this, not validBidRequests[0].params delete ozoneRequest.test; // don't allow test to be set in the config - ONLY use $_GET['pbjs_debug'] if (bidderRequest && bidderRequest.gdprConsent) { - utils.logInfo('OZONE: ADDING GDPR info'); - let apiVersion = utils.deepAccess(bidderRequest.gdprConsent, 'apiVersion', '1'); + this.logInfo('ADDING GDPR info'); + let apiVersion = bidderRequest.gdprConsent.apiVersion || '1'; ozoneRequest.regs = {ext: {gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0, apiVersion: apiVersion}}; if (ozoneRequest.regs.ext.gdpr) { ozoneRequest.user = ozoneRequest.user || {}; ozoneRequest.user.ext = {'consent': bidderRequest.gdprConsent.consentString}; } else { - utils.logInfo('OZONE: **** Strange CMP info: bidderRequest.gdprConsent exists BUT bidderRequest.gdprConsent.gdprApplies is false. See bidderRequest logged above. ****'); + this.logInfo('**** Strange CMP info: bidderRequest.gdprConsent exists BUT bidderRequest.gdprConsent.gdprApplies is false. See bidderRequest logged above. ****'); } } else { - utils.logInfo('OZONE: WILL NOT ADD GDPR info; no bidderRequest.gdprConsent object was present.'); + this.logInfo('WILL NOT ADD GDPR info; no bidderRequest.gdprConsent object was present.'); } const getParams = this.getGetParametersAsObject(); - const ozTestMode = getParams.hasOwnProperty('oztestmode') ? getParams.oztestmode : null; // this can be any string, it's used for testing ads + const wlOztestmodeKey = whitelabelPrefix + 'testmode'; + const isTestMode = getParams[wlOztestmodeKey] || null; // this can be any string, it's used for testing ads ozoneRequest.device = {'w': window.innerWidth, 'h': window.innerHeight}; let placementIdOverrideFromGetParam = this.getPlacementIdOverrideFromGetParam(); // null or string - let lotameDataSingle = {}; // we will capture lotame data once & send it to the server as ext.ozone.lotameData // build the array of params to attach to `imp` let tosendtags = validBidRequests.map(ozoneBidRequest => { var obj = {}; @@ -168,18 +214,18 @@ export const spec = { let arrBannerSizes = []; if (!ozoneBidRequest.hasOwnProperty('mediaTypes')) { if (ozoneBidRequest.hasOwnProperty('sizes')) { - utils.logInfo('OZONE: no mediaTypes detected - will use the sizes array in the config root'); + this.logInfo('no mediaTypes detected - will use the sizes array in the config root'); arrBannerSizes = ozoneBidRequest.sizes; } else { - utils.logInfo('OZONE: no mediaTypes detected, no sizes array in the config root either. Cannot set sizes for banner type'); + this.logInfo('no mediaTypes detected, no sizes array in the config root either. Cannot set sizes for banner type'); } } else { if (ozoneBidRequest.mediaTypes.hasOwnProperty(BANNER)) { arrBannerSizes = ozoneBidRequest.mediaTypes[BANNER].sizes; /* Note - if there is a sizes element in the config root it will be pushed into here */ - utils.logInfo('OZONE: setting banner size from the mediaTypes.banner element for bidId ' + obj.id + ': ', arrBannerSizes); + this.logInfo('setting banner size from the mediaTypes.banner element for bidId ' + obj.id + ': ', arrBannerSizes); } if (ozoneBidRequest.mediaTypes.hasOwnProperty(VIDEO)) { - utils.logInfo('OZONE: openrtb 2.5 compliant video'); + this.logInfo('openrtb 2.5 compliant video'); // examine all the video attributes in the config, and either put them into obj.video if allowed by IAB2.5 or else in to obj.video.ext if (typeof ozoneBidRequest.mediaTypes[VIDEO] == 'object') { let childConfig = utils.deepAccess(ozoneBidRequest, 'params.video', {}); @@ -188,25 +234,25 @@ export const spec = { } // we need to duplicate some of the video values let wh = getWidthAndHeightFromVideoObject(obj.video); - utils.logInfo('OZONE: setting video object from the mediaTypes.video element: ' + obj.id + ':', obj.video, 'wh=', wh); + this.logInfo('setting video object from the mediaTypes.video element: ' + obj.id + ':', obj.video, 'wh=', wh); if (wh && typeof wh === 'object') { obj.video.w = wh['w']; obj.video.h = wh['h']; if (playerSizeIsNestedArray(obj.video)) { // this should never happen; it was in the original spec for this change though. - utils.logInfo('OZONE: setting obj.video.format to be an array of objects'); + this.logInfo('setting obj.video.format to be an array of objects'); obj.video.ext.format = [wh]; } else { - utils.logInfo('OZONE: setting obj.video.format to be an object'); + this.logInfo('setting obj.video.format to be an object'); obj.video.ext.format = wh; } } else { - utils.logWarn('OZONE: cannot set w, h & format values for video; the config is not right'); + this.logWarn('cannot set w, h & format values for video; the config is not right'); } } // Native integration is not complete yet if (ozoneBidRequest.mediaTypes.hasOwnProperty(NATIVE)) { obj.native = ozoneBidRequest.mediaTypes[NATIVE]; - utils.logInfo('OZONE: setting native object from the mediaTypes.native element: ' + obj.id + ':', obj.native); + this.logInfo('setting native object from the mediaTypes.native element: ' + obj.id + ':', obj.native); } } if (arrBannerSizes.length > 0) { @@ -223,52 +269,55 @@ export const spec = { // these 3 MUST exist - we check them in the validation method obj.placementId = placementId; // build the imp['ext'] object - obj.ext = {'prebid': {'storedrequest': {'id': placementId}}, 'ozone': {}}; - obj.ext.ozone.adUnitCode = ozoneBidRequest.adUnitCode; // eg. 'mpu' - obj.ext.ozone.transactionId = ozoneBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit + obj.ext = {'prebid': {'storedrequest': {'id': placementId}}}; + obj.ext[whitelabelBidder] = {}; + obj.ext[whitelabelBidder].adUnitCode = ozoneBidRequest.adUnitCode; // eg. 'mpu' + obj.ext[whitelabelBidder].transactionId = ozoneBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit if (ozoneBidRequest.params.hasOwnProperty('customData')) { - obj.ext.ozone.customData = ozoneBidRequest.params.customData; + obj.ext[whitelabelBidder].customData = ozoneBidRequest.params.customData; } - utils.logInfo('OZONE: obj.ext.ozone is ', obj.ext.ozone); - if (ozTestMode != null) { - utils.logInfo('OZONE: setting ozTestMode to ', ozTestMode); - if (obj.ext.ozone.hasOwnProperty('customData')) { - for (let i = 0; i < obj.ext.ozone.customData.length; i++) { - obj.ext.ozone.customData[i]['targeting']['oztestmode'] = ozTestMode; + this.logInfo(`obj.ext.${whitelabelBidder} is `, obj.ext[whitelabelBidder]); + if (isTestMode != null) { + this.logInfo('setting isTestMode to ', isTestMode); + if (obj.ext[whitelabelBidder].hasOwnProperty('customData')) { + for (let i = 0; i < obj.ext[whitelabelBidder].customData.length; i++) { + obj.ext[whitelabelBidder].customData[i]['targeting'][wlOztestmodeKey] = isTestMode; } } else { - obj.ext.ozone.customData = [{'settings': {}, 'targeting': {'oztestmode': ozTestMode}}]; + obj.ext[whitelabelBidder].customData = [{'settings': {}, 'targeting': {}}]; + obj.ext[whitelabelBidder].customData[0].targeting[wlOztestmodeKey] = isTestMode; } - } else { - utils.logInfo('OZONE: no ozTestMode '); - } - // now deal with lotame, including the optional override parameters - if (Object.keys(lotameDataSingle).length === 0) { // we've not yet found lotameData, see if we can get it from this bid request object - lotameDataSingle = this.tryGetLotameData(ozoneBidRequest); } return obj; }); // in v 2.0.0 we moved these outside of the individual ad slots - let extObj = {'ozone': {'oz_pb_v': OZONEVERSION, 'oz_rw': placementIdOverrideFromGetParam ? 1 : 0, 'oz_lot_rw': this.propertyBag.lotameWasOverridden}}; + let extObj = {}; + extObj[whitelabelBidder] = {}; + extObj[whitelabelBidder][whitelabelPrefix + '_pb_v'] = OZONEVERSION; + extObj[whitelabelBidder][whitelabelPrefix + '_rw'] = placementIdOverrideFromGetParam ? 1 : 0; if (validBidRequests.length > 0) { - let userIds = this.findAllUserIds(validBidRequests[0]); + let userIds = this.cookieSyncBag.userIdObject; // 2021-01-06 - slight optimisation - we've already found this info + // let userIds = this.findAllUserIds(validBidRequests[0]); if (userIds.hasOwnProperty('pubcid')) { - extObj.ozone.pubcid = userIds.pubcid; + extObj[whitelabelBidder].pubcid = userIds.pubcid; } } - extObj.ozone.pv = this.getPageId(); // attach the page ID that will be common to all auciton calls for this page if refresh() is called - extObj.ozone.lotameData = lotameDataSingle; // 2.4.0 moved lotameData out of bid objects into the single ext.ozone area to remove duplication - let ozOmpFloorDollars = config.getConfig('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') - utils.logInfo('OZONE: oz_omp_floor dollar value = ', ozOmpFloorDollars); + extObj[whitelabelBidder].pv = this.getPageId(); // attach the page ID that will be common to all auciton calls for this page if refresh() is called + let ozOmpFloorDollars = this.getWhitelabelConfigItem('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') + this.logInfo(`${whitelabelPrefix}_omp_floor dollar value = `, ozOmpFloorDollars); if (typeof ozOmpFloorDollars === 'number') { - extObj.ozone.oz_omp_floor = ozOmpFloorDollars; + extObj[whitelabelBidder][whitelabelPrefix + '_omp_floor'] = ozOmpFloorDollars; } else if (typeof ozOmpFloorDollars !== 'undefined') { - utils.logError('OZONE: oz_omp_floor is invalid - IF SET then this must be a number, representing dollar value eg. oz_omp_floor: 1.55. You have it set as a ' + (typeof ozOmpFloorDollars)); + this.logError(`${whitelabelPrefix}_omp_floor is invalid - IF SET then this must be a number, representing dollar value eg. ${whitelabelPrefix}_omp_floor: 1.55. You have it set as a ` + (typeof ozOmpFloorDollars)); } - let ozWhitelistAdserverKeys = config.getConfig('ozone.oz_whitelist_adserver_keys'); + let ozWhitelistAdserverKeys = this.getWhitelabelConfigItem('ozone.oz_whitelist_adserver_keys'); let useOzWhitelistAdserverKeys = utils.isArray(ozWhitelistAdserverKeys) && ozWhitelistAdserverKeys.length > 0; - extObj.ozone.oz_kvp_rw = useOzWhitelistAdserverKeys ? 1 : 0; + extObj[whitelabelBidder][whitelabelPrefix + '_kvp_rw'] = useOzWhitelistAdserverKeys ? 1 : 0; + if (whitelabelBidder != 'ozone') { + this.logInfo('setting aliases object'); + extObj.prebid = {aliases: {'ozone': whitelabelBidder}}; + } var userExtEids = this.generateEids(validBidRequests); // generate the UserIDs in the correct format for UserId module @@ -277,7 +326,7 @@ export const spec = { 'page': document.location.href, 'id': htmlParams.siteId }; - ozoneRequest.test = (getParams.hasOwnProperty('pbjs_debug') && getParams['pbjs_debug'] == 'true') ? 1 : 0; + ozoneRequest.test = (getParams.hasOwnProperty('pbjs_debug') && getParams['pbjs_debug'] === 'true') ? 1 : 0; // this is for 2.2.1 // coppa compliance @@ -287,7 +336,7 @@ export const spec = { // return the single request object OR the array: if (singleRequest) { - utils.logInfo('OZONE: buildRequests starting to generate response for a single request'); + this.logInfo('buildRequests starting to generate response for a single request'); ozoneRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange. ozoneRequest.auctionId = bidderRequest.auctionId; // not sure if this should be here? ozoneRequest.imp = tosendtags; @@ -296,36 +345,36 @@ export const spec = { utils.deepSetValue(ozoneRequest, 'user.ext.eids', userExtEids); var ret = { method: 'POST', - url: OZONEURI, + url: this.getAuctionUrl(), data: JSON.stringify(ozoneRequest), bidderRequest: bidderRequest }; - utils.logInfo('OZONE: buildRequests ozoneRequest for single = ', ozoneRequest); + this.logInfo('buildRequests request data for single = ', ozoneRequest); this.propertyBag.buildRequestsEnd = new Date().getTime(); - utils.logInfo(`OZONE: buildRequests going to return for single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, ret); + this.logInfo(`buildRequests going to return for single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, ret); return ret; } // not single request - pull apart the tosendtags array & return an array of objects each containing one element in the imp array. let arrRet = tosendtags.map(imp => { - utils.logInfo('OZONE: buildRequests starting to generate non-single response, working on imp : ', imp); + this.logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); let ozoneRequestSingle = Object.assign({}, ozoneRequest); - imp.ext.ozone.pageAuctionId = bidderRequest['auctionId']; // make a note in the ext object of what the original auctionId was, in the bidderRequest object - ozoneRequestSingle.id = imp.ext.ozone.transactionId; // Unique ID of the bid request, provided by the exchange. - ozoneRequestSingle.auctionId = imp.ext.ozone.transactionId; // not sure if this should be here? + imp.ext[whitelabelBidder].pageAuctionId = bidderRequest['auctionId']; // make a note in the ext object of what the original auctionId was, in the bidderRequest object + ozoneRequestSingle.id = imp.ext[whitelabelBidder].transactionId; // Unique ID of the bid request, provided by the exchange. + ozoneRequestSingle.auctionId = imp.ext[whitelabelBidder].transactionId; // not sure if this should be here? ozoneRequestSingle.imp = [imp]; ozoneRequestSingle.ext = extObj; - ozoneRequestSingle.source = {'tid': imp.ext.ozone.transactionId}; + ozoneRequestSingle.source = {'tid': imp.ext[whitelabelBidder].transactionId}; utils.deepSetValue(ozoneRequestSingle, 'user.ext.eids', userExtEids); - utils.logInfo('OZONE: buildRequests ozoneRequestSingle (for non-single) = ', ozoneRequestSingle); + this.logInfo('buildRequests RequestSingle (for non-single) = ', ozoneRequestSingle); return { method: 'POST', - url: OZONEURI, + url: this.getAuctionUrl(), data: JSON.stringify(ozoneRequestSingle), bidderRequest: bidderRequest }; }); this.propertyBag.buildRequestsEnd = new Date().getTime(); - utils.logInfo(`OZONE: buildRequests going to return for non-single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, arrRet); + this.logInfo(`buildRequests going to return for non-single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, arrRet); return arrRet; }, /** @@ -339,9 +388,12 @@ export const spec = { * @returns {*} */ interpretResponse(serverResponse, request) { + if (request && request.bidderRequest && request.bidderRequest.bids) { this.loadWhitelabelData(request.bidderRequest.bids[0]); } let startTime = new Date().getTime(); - utils.logInfo(`OZONE: interpretResponse time: ${startTime} . Time between buildRequests done and interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); - utils.logInfo(`OZONE: serverResponse, request`, serverResponse, request); + let whitelabelBidder = this.propertyBag.whitelabel.bidder; // by default = ozone + let whitelabelPrefix = this.propertyBag.whitelabel.keyPrefix; + this.logInfo(`interpretResponse time: ${startTime} . Time between buildRequests done and interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); + this.logInfo(`serverResponse, request`, JSON.parse(JSON.stringify(serverResponse)), JSON.parse(JSON.stringify(request))); serverResponse = serverResponse.body || {}; // note that serverResponse.id value is the auction_id we might want to use for reporting reasons. if (!serverResponse.hasOwnProperty('seatbid')) { @@ -351,91 +403,91 @@ export const spec = { return []; } let arrAllBids = []; - let enhancedAdserverTargeting = config.getConfig('ozone.enhancedAdserverTargeting'); - utils.logInfo('OZONE: enhancedAdserverTargeting', enhancedAdserverTargeting); + let enhancedAdserverTargeting = this.getWhitelabelConfigItem('ozone.enhancedAdserverTargeting'); + this.logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); if (typeof enhancedAdserverTargeting == 'undefined') { enhancedAdserverTargeting = true; } - utils.logInfo('OZONE: enhancedAdserverTargeting', enhancedAdserverTargeting); + this.logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); serverResponse.seatbid = injectAdIdsIntoAllBidResponses(serverResponse.seatbid); // we now make sure that each bid in the bidresponse has a unique (within page) adId attribute. serverResponse.seatbid = this.removeSingleBidderMultipleBids(serverResponse.seatbid); - let ozOmpFloorDollars = config.getConfig('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') + let ozOmpFloorDollars = this.getWhitelabelConfigItem('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') let addOzOmpFloorDollars = typeof ozOmpFloorDollars === 'number'; - let ozWhitelistAdserverKeys = config.getConfig('ozone.oz_whitelist_adserver_keys'); + let ozWhitelistAdserverKeys = this.getWhitelabelConfigItem('ozone.oz_whitelist_adserver_keys'); let useOzWhitelistAdserverKeys = utils.isArray(ozWhitelistAdserverKeys) && ozWhitelistAdserverKeys.length > 0; for (let i = 0; i < serverResponse.seatbid.length; i++) { let sb = serverResponse.seatbid[i]; for (let j = 0; j < sb.bid.length; j++) { let thisRequestBid = this.getBidRequestForBidId(sb.bid[j].impid, request.bidderRequest.bids); - utils.logInfo(`OZONE seatbid:${i}, bid:${j} Going to set default w h for seatbid/bidRequest`, sb.bid[j], thisRequestBid); + this.logInfo(`seatbid:${i}, bid:${j} Going to set default w h for seatbid/bidRequest`, sb.bid[j], thisRequestBid); const {defaultWidth, defaultHeight} = defaultSize(thisRequestBid); let thisBid = ozoneAddStandardProperties(sb.bid[j], defaultWidth, defaultHeight); let videoContext = null; let isVideo = false; let bidType = utils.deepAccess(thisBid, 'ext.prebid.type'); - utils.logInfo(`OZONE: this bid type is : ${bidType}`, j); + this.logInfo(`this bid type is : ${bidType}`, j); if (bidType === VIDEO) { isVideo = true; videoContext = this.getVideoContextForBidId(thisBid.bidId, request.bidderRequest.bids); // should be instream or outstream (or null if error) if (videoContext === 'outstream') { - utils.logInfo('OZONE: going to attach a renderer to OUTSTREAM video : ', j); + this.logInfo('going to attach a renderer to OUTSTREAM video : ', j); thisBid.renderer = newRenderer(thisBid.bidId); } else { - utils.logInfo('OZONE: bid is not an outstream video, will not attach a renderer: ', j); + this.logInfo('bid is not an outstream video, will not attach a renderer: ', j); } } let adserverTargeting = {}; if (enhancedAdserverTargeting) { let allBidsForThisBidid = ozoneGetAllBidsForBidId(thisBid.bidId, serverResponse.seatbid); // add all the winning & non-winning bids for this bidId: - utils.logInfo('OZONE: Going to iterate allBidsForThisBidId', allBidsForThisBidid); - Object.keys(allBidsForThisBidid).forEach(function (bidderName, index, ar2) { - utils.logInfo(`OZONE: adding adserverTargeting for ${bidderName} for bidId ${thisBid.bidId}`); + this.logInfo('Going to iterate allBidsForThisBidId', allBidsForThisBidid); + Object.keys(allBidsForThisBidid).forEach((bidderName, index, ar2) => { + this.logInfo(`adding adserverTargeting for ${bidderName} for bidId ${thisBid.bidId}`); // let bidderName = bidderNameWH.split('_')[0]; - adserverTargeting['oz_' + bidderName] = bidderName; - adserverTargeting['oz_' + bidderName + '_crid'] = String(allBidsForThisBidid[bidderName].crid); - adserverTargeting['oz_' + bidderName + '_adv'] = String(allBidsForThisBidid[bidderName].adomain); - adserverTargeting['oz_' + bidderName + '_adId'] = String(allBidsForThisBidid[bidderName].adId); - adserverTargeting['oz_' + bidderName + '_pb_r'] = getRoundedBid(allBidsForThisBidid[bidderName].price, allBidsForThisBidid[bidderName].ext.prebid.type); + adserverTargeting[whitelabelPrefix + '_' + bidderName] = bidderName; + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_crid'] = String(allBidsForThisBidid[bidderName].crid); + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_adv'] = String(allBidsForThisBidid[bidderName].adomain); + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_adId'] = String(allBidsForThisBidid[bidderName].adId); + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_pb_r'] = getRoundedBid(allBidsForThisBidid[bidderName].price, allBidsForThisBidid[bidderName].ext.prebid.type); if (allBidsForThisBidid[bidderName].hasOwnProperty('dealid')) { - adserverTargeting['oz_' + bidderName + '_dealid'] = String(allBidsForThisBidid[bidderName].dealid); + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_dealid'] = String(allBidsForThisBidid[bidderName].dealid); } if (addOzOmpFloorDollars) { - adserverTargeting['oz_' + bidderName + '_omp'] = allBidsForThisBidid[bidderName].price >= ozOmpFloorDollars ? '1' : '0'; + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_omp'] = allBidsForThisBidid[bidderName].price >= ozOmpFloorDollars ? '1' : '0'; } if (isVideo) { - adserverTargeting['oz_' + bidderName + '_vid'] = videoContext; // outstream or instream + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_vid'] = videoContext; // outstream or instream } - let flr = utils.deepAccess(allBidsForThisBidid[bidderName], 'ext.bidder.ozone.floor', null); + let flr = utils.deepAccess(allBidsForThisBidid[bidderName], `ext.bidder.${whitelabelBidder}.floor`, null); if (flr != null) { - adserverTargeting['oz_' + bidderName + '_flr'] = flr; + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_flr'] = flr; } - let rid = utils.deepAccess(allBidsForThisBidid[bidderName], 'ext.bidder.ozone.ruleId', null); + let rid = utils.deepAccess(allBidsForThisBidid[bidderName], `ext.bidder.${whitelabelBidder}.ruleId`, null); if (rid != null) { - adserverTargeting['oz_' + bidderName + '_rid'] = rid; + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_rid'] = rid; } if (bidderName.match(/^ozappnexus/)) { - adserverTargeting['oz_' + bidderName + '_sid'] = String(allBidsForThisBidid[bidderName].cid); + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_sid'] = String(allBidsForThisBidid[bidderName].cid); } }); } else { if (useOzWhitelistAdserverKeys) { - utils.logWarn('OZONE: You have set a whitelist of adserver keys but this will be ignored because ozone.enhancedAdserverTargeting is set to false. No per-bid keys will be sent to adserver.'); + this.logWarn(`You have set a whitelist of adserver keys but this will be ignored because ${whitelabelBidder}.enhancedAdserverTargeting is set to false. No per-bid keys will be sent to adserver.`); } else { - utils.logInfo('OZONE: ozone.enhancedAdserverTargeting is set to false, so no per-bid keys will be sent to adserver.'); + this.logInfo(`${whitelabelBidder}.enhancedAdserverTargeting is set to false, so no per-bid keys will be sent to adserver.`); } } // also add in the winning bid, to be sent to dfp let {seat: winningSeat, bid: winningBid} = ozoneGetWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); - adserverTargeting['oz_auc_id'] = String(request.bidderRequest.auctionId); - adserverTargeting['oz_winner'] = String(winningSeat); + adserverTargeting[whitelabelPrefix + '_auc_id'] = String(request.bidderRequest.auctionId); + adserverTargeting[whitelabelPrefix + '_winner'] = String(winningSeat); if (enhancedAdserverTargeting) { - adserverTargeting['oz_imp_id'] = String(winningBid.impid); - adserverTargeting['oz_pb_v'] = OZONEVERSION; + adserverTargeting[whitelabelPrefix + '_imp_id'] = String(winningBid.impid); + adserverTargeting[whitelabelPrefix + '_pb_v'] = OZONEVERSION; } if (useOzWhitelistAdserverKeys) { // delete any un-whitelisted keys - utils.logInfo('OZONE: Going to filter out adserver targeting keys not in the whitelist: ', ozWhitelistAdserverKeys); + this.logInfo('Going to filter out adserver targeting keys not in the whitelist: ', ozWhitelistAdserverKeys); Object.keys(adserverTargeting).forEach(function(key) { if (ozWhitelistAdserverKeys.indexOf(key) === -1) { delete adserverTargeting[key]; } }); } thisBid.adserverTargeting = adserverTargeting; @@ -443,9 +495,22 @@ export const spec = { } } let endTime = new Date().getTime(); - utils.logInfo(`OZONE: interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`, arrAllBids); + this.logInfo(`interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`, arrAllBids); return arrAllBids; }, + /** + * Use this to get all config values + * Now it's getting complicated with whitelabeling, this simplifies the code for getting config values. + * eg. to get ozone.oz_omp_floor you just send '_omp_floor' + * @param ozoneVersion string like 'ozone.oz_omp_floor' + * @return {string|object} + */ + getWhitelabelConfigItem(ozoneVersion) { + if (this.propertyBag.whitelabel.bidder == 'ozone') { return config.getConfig(ozoneVersion); } + let whitelabelledSearch = ozoneVersion.replace('ozone', this.propertyBag.whitelabel.bidder); + whitelabelledSearch = ozoneVersion.replace('oz_', this.propertyBag.whitelabel.keyPrefix + '_'); + return config.getConfig(whitelabelledSearch); + }, /** * If a bidder bids for > 1 size for an adslot, allow only the highest bid * @param seatbid object (serverResponse.seatbid) @@ -475,7 +540,7 @@ export const spec = { }, // see http://prebid.org/dev-docs/bidder-adaptor.html#registering-user-syncs getUserSyncs(optionsType, serverResponse, gdprConsent) { - utils.logInfo('OZONE: getUserSyncs optionsType, serverResponse, gdprConsent, cookieSyncBag', optionsType, serverResponse, gdprConsent, this.cookieSyncBag); + this.logInfo('getUserSyncs optionsType, serverResponse, gdprConsent, cookieSyncBag', optionsType, serverResponse, gdprConsent, this.cookieSyncBag); if (!serverResponse || serverResponse.length === 0) { return []; } @@ -494,15 +559,16 @@ export const spec = { arrQueryString.push('publisherId=' + this.cookieSyncBag.publisherId); arrQueryString.push('siteId=' + this.cookieSyncBag.siteId); arrQueryString.push('cb=' + Date.now()); + arrQueryString.push('bidder=' + this.propertyBag.whitelabel.bidder); var strQueryString = arrQueryString.join('&'); if (strQueryString.length > 0) { strQueryString = '?' + strQueryString; } - utils.logInfo('OZONE: getUserSyncs going to return cookie sync url : ' + OZONECOOKIESYNC + strQueryString); + this.logInfo('getUserSyncs going to return cookie sync url : ' + this.getCookieSyncUrl() + strQueryString); return [{ type: 'iframe', - url: OZONECOOKIESYNC + strQueryString + url: this.getCookieSyncUrl() + strQueryString }]; } }, @@ -535,26 +601,31 @@ export const spec = { }, /** * Look for pubcid & all the other IDs according to http://prebid.org/dev-docs/modules/userId.html + * NOTE that criteortus is deprecated & should be removed asap * @return map */ findAllUserIds(bidRequest) { var ret = {}; - let searchKeysSingle = ['pubcid', 'tdid', 'parrableId', 'idl_env', 'digitrustid', 'criteortus']; + // @todo - what is fabrick called & where to look for it? If it's a simple value then it will automatically be ok + let searchKeysSingle = ['pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'criteoId', 'criteortus', + 'sharedid', 'lotamePanoramaId', 'fabrickId']; if (bidRequest.hasOwnProperty('userId')) { for (let arrayId in searchKeysSingle) { let key = searchKeysSingle[arrayId]; if (bidRequest.userId.hasOwnProperty(key)) { - ret[key] = bidRequest.userId[key]; + if (typeof (bidRequest.userId[key]) == 'string') { + ret[key] = bidRequest.userId[key]; + } else if (typeof (bidRequest.userId[key]) == 'object') { + ret[key] = bidRequest.userId[key][Object.keys(bidRequest.userId[key])[0]]; // cannot use Object.values + } else { + this.logError(`failed to get string key value for userId : ${key}`); + } } } var lipbid = utils.deepAccess(bidRequest.userId, 'lipb.lipbid'); if (lipbid) { ret['lipb'] = {'lipbid': lipbid}; } - var id5id = utils.deepAccess(bidRequest.userId, 'id5id.uid'); - if (id5id) { - ret['id5id'] = id5id; - } } if (!ret.hasOwnProperty('pubcid')) { var pubcid = utils.deepAccess(bidRequest, 'crumbs.pubcid'); @@ -564,69 +635,6 @@ export const spec = { } return ret; }, - /** - * get all the lotame override keys/values from the querystring. - * @return object containing zero or more keys/values - */ - getLotameOverrideParams() { - const arrGet = this.getGetParametersAsObject(); - utils.logInfo('OZONE: getLotameOverrideParams - arrGet=', arrGet); - let arrRet = {}; - for (let i in ALLOWED_LOTAME_PARAMS) { - if (arrGet.hasOwnProperty(ALLOWED_LOTAME_PARAMS[i])) { - arrRet[ALLOWED_LOTAME_PARAMS[i]] = arrGet[ALLOWED_LOTAME_PARAMS[i]]; - } - } - return arrRet; - }, - /** - * Boolean function to check that this lotame data is valid (check Audience.id) - */ - isLotameDataValid(lotameObj) { - if (!lotameObj.hasOwnProperty('Profile')) return false; - let prof = lotameObj.Profile; - if (!prof.hasOwnProperty('tpid')) return false; - if (!prof.hasOwnProperty('pid')) return false; - let audiences = utils.deepAccess(prof, 'Audiences.Audience'); - if (typeof audiences != 'object') { - return false; - } - for (var i = 0; i < audiences.length; i++) { - let aud = audiences[i]; - if (!aud.hasOwnProperty('id')) { - return false; - } - } - return true; // All Audiences objects have an 'id' key - }, - /** - * Use the arrOverride keys/vals to update the arrExisting lotame object. - * Ideally we will only be using the oz_lotameid value to update the audiences id, but in the event of bad/missing - * pid & tpid we will also have to use substitute values for those too. - * - * @param objOverride object will contain all the ALLOWED_LOTAME_PARAMS parameters - * @param lotameData object might be {} or contain the lotame data - */ - makeLotameObjectFromOverride(objOverride, lotameData) { - if ((lotameData.hasOwnProperty('Profile') && Object.keys(lotameData.Profile).length < 3) || - (!lotameData.hasOwnProperty('Profile'))) { // bad or empty lotame object (should contain pid, tpid & Audiences object) - build a total replacement - utils.logInfo('OZONE: makeLotameObjectFromOverride will return a full default lotame object'); - return { - 'Profile': { - 'tpid': objOverride['oz_lotametpid'], - 'pid': objOverride['oz_lotamepid'], - 'Audiences': {'Audience': [{'id': objOverride['oz_lotameid'], 'abbr': objOverride['oz_lotameid']}]} - } - }; - } - if (utils.deepAccess(lotameData, 'Profile.Audiences.Audience')) { - utils.logInfo('OZONE: makeLotameObjectFromOverride will return the existing lotame object with updated Audience by oz_lotameid'); - lotameData.Profile.Audiences.Audience = [{'id': objOverride['oz_lotameid'], 'abbr': objOverride['oz_lotameid']}]; - return lotameData; - } - utils.logInfo('OZONE: makeLotameObjectFromOverride Weird error - failed to find Profile.Audiences.Audience in lotame object. Will return the object as-is'); - return lotameData; - }, /** * Convenient method to get the value we need for the placementId - ONLY from the bidRequest - NOT taking into account any GET override ID * @param bidRequest @@ -642,48 +650,30 @@ export const spec = { * @returns null|string */ getPlacementIdOverrideFromGetParam() { + let whitelabelPrefix = this.propertyBag.whitelabel.keyPrefix; let arr = this.getGetParametersAsObject(); - if (arr.hasOwnProperty('ozstoredrequest')) { - if (this.isValidPlacementId(arr.ozstoredrequest)) { - utils.logInfo('OZONE: using GET ozstoredrequest ' + arr.ozstoredrequest + ' to replace placementId'); - return arr.ozstoredrequest; + if (arr.hasOwnProperty(whitelabelPrefix + 'storedrequest')) { + if (this.isValidPlacementId(arr[whitelabelPrefix + 'storedrequest'])) { + this.logInfo(`using GET ${whitelabelPrefix}storedrequest ` + arr[whitelabelPrefix + 'storedrequest'] + ' to replace placementId'); + return arr[whitelabelPrefix + 'storedrequest']; } else { - utils.logError('OZONE: GET ozstoredrequest FAILED VALIDATION - will not use it'); + this.logError(`GET ${whitelabelPrefix}storedrequest FAILED VALIDATION - will not use it`); } } return null; }, - /** - * Produces external userid object - */ - addExternalUserId(eids, value, source, atype) { - if (utils.isStr(value)) { - eids.push({ - source, - uids: [{ - id: value, - atype - }] - }); - } - }, /** * Generate an object we can append to the auction request, containing user data formatted correctly for different ssps + * http://prebid.org/dev-docs/modules/userId.html * @param validBidRequests * @return {Array} */ generateEids(validBidRequests) { - let eids = []; - this.handleTTDId(eids, validBidRequests); + let eids; const bidRequest = validBidRequests[0]; if (bidRequest && bidRequest.userId) { - this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.pubcid`), 'pubcid', 1); - this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.pubcid`), 'pubcommon', 1); - this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.id5id.uid`), 'id5-sync.com', 1); - this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.criteortus.${BIDDER_CODE}.userid`), 'criteortus', 1); - this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.idl_env`), 'liveramp.com', 1); - this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.lipb.lipbid`), 'liveintent.com', 1); - this.addExternalUserId(eids, utils.deepAccess(bidRequest, `userId.parrableId.eid`), 'parrable.com', 1); + eids = bidRequest.userIdAsEids; + this.handleTTDId(eids, validBidRequests); } return eids; }, @@ -725,9 +715,9 @@ export const spec = { */ blockTheRequest() { // if there is an ozone.oz_request = false then quit now. - let ozRequest = config.getConfig('ozone.oz_request'); + let ozRequest = this.getWhitelabelConfigItem('ozone.oz_request'); if (typeof ozRequest == 'boolean' && !ozRequest) { - utils.logWarn('OZONE: Will not allow auction : ozone.oz_request is set to false'); + this.logWarn(`Will not allow auction : ${this.propertyBag.whitelabel.keyPrefix}one.${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); return true; } return false; @@ -747,34 +737,6 @@ export const spec = { } return this.propertyBag.pageId; }, - /** - * handle the complexity of there possibly being lotameData override (may be valid/invalid) & there may or may not be lotameData present in the bidRequest - * NOTE THAT this will also set this.propertyBag.lotameWasOverridden=1 if we use lotame override - * @param ozoneBidRequest - * @return object representing the absolute lotameData we need to use. - */ - tryGetLotameData: function(ozoneBidRequest) { - const arrLotameOverride = this.getLotameOverrideParams(); - let ret = {}; - if (Object.keys(arrLotameOverride).length === ALLOWED_LOTAME_PARAMS.length) { - // all override params are present, override lotame object: - if (ozoneBidRequest.params.hasOwnProperty('lotameData')) { - ret = this.makeLotameObjectFromOverride(arrLotameOverride, ozoneBidRequest.params.lotameData); - } else { - ret = this.makeLotameObjectFromOverride(arrLotameOverride, {}); - } - this.propertyBag.lotameWasOverridden = 1; - } else if (ozoneBidRequest.params.hasOwnProperty('lotameData')) { - // no lotame override, use it as-is - if (this.isLotameDataValid(ozoneBidRequest.params.lotameData)) { - ret = ozoneBidRequest.params.lotameData; - } else { - utils.logError('OZONE: INVALID LOTAME DATA FOUND - WILL NOT USE THIS AT ALL ELSE IT MIGHT BREAK THE AUCTION CALL!', ozoneBidRequest.params.lotameData); - ret = {}; - } - } - return ret; - }, unpackVideoConfigIntoIABformat(videoConfig, childConfig) { let ret = {'ext': {}}; ret = this._unpackVideoConfigIntoIABformat(ret, videoConfig); @@ -853,7 +815,7 @@ export const spec = { * @returns seatbid object */ export function injectAdIdsIntoAllBidResponses(seatbid) { - utils.logInfo('OZONE: injectAdIdsIntoAllBidResponses', seatbid); + spec.logInfo('injectAdIdsIntoAllBidResponses', seatbid); for (let i = 0; i < seatbid.length; i++) { let sb = seatbid[i]; for (let j = 0; j < sb.bid.length; j++) { @@ -879,7 +841,7 @@ export function checkDeepArray(Arr) { export function defaultSize(thebidObj) { if (!thebidObj) { - utils.logInfo('OZONE: defaultSize received empty bid obj! going to return fixed default size'); + spec.logInfo('defaultSize received empty bid obj! going to return fixed default size'); return { 'defaultHeight': 250, 'defaultWidth': 300 @@ -932,7 +894,7 @@ export function ozoneGetAllBidsForBidId(matchBidId, serverResponseSeatBid) { for (let k = 0; k < theseBids.length; k++) { if (theseBids[k].impid === matchBidId) { if (objBids.hasOwnProperty(thisSeat)) { // > 1 bid for an adunit from a bidder - only use the one with the highest bid - // objBids[`${thisSeat}${theseBids[k].w}x${theseBids[k].h}`] = theseBids[k]; + // objBids[`${thisSeat}${theseBids[k].w}x${theseBids[k].h}`] = theseBids[k]; if (objBids[thisSeat]['price'] < theseBids[k].price) { objBids[thisSeat] = theseBids[k]; } @@ -957,14 +919,14 @@ export function getRoundedBid(price, mediaType) { let theConfigObject = getGranularityObject(mediaType, mediaTypeGranularity, strBuckets, objBuckets); let theConfigKey = getGranularityKeyName(mediaType, mediaTypeGranularity, strBuckets); - utils.logInfo('OZONE: getRoundedBid. price:', price, 'mediaType:', mediaType, 'configkey:', theConfigKey, 'configObject:', theConfigObject, 'mediaTypeGranularity:', mediaTypeGranularity, 'strBuckets:', strBuckets); + spec.logInfo('getRoundedBid. price:', price, 'mediaType:', mediaType, 'configkey:', theConfigKey, 'configObject:', theConfigObject, 'mediaTypeGranularity:', mediaTypeGranularity, 'strBuckets:', strBuckets); let priceStringsObj = getPriceBucketString( price, theConfigObject, config.getConfig('currency.granularityMultiplier') ); - utils.logInfo('OZONE: priceStringsObj', priceStringsObj); + spec.logInfo('priceStringsObj', priceStringsObj); // by default, without any custom granularity set, you get granularity name : 'medium' let granularityNamePriceStringsKeyMapping = { 'medium': 'med', @@ -975,7 +937,7 @@ export function getRoundedBid(price, mediaType) { }; if (granularityNamePriceStringsKeyMapping.hasOwnProperty(theConfigKey)) { let priceStringsKey = granularityNamePriceStringsKeyMapping[theConfigKey]; - utils.logInfo('OZONE: getRoundedBid: looking for priceStringsKey:', priceStringsKey); + spec.logInfo('getRoundedBid: looking for priceStringsKey:', priceStringsKey); return priceStringsObj[priceStringsKey]; } return priceStringsObj['auto']; @@ -1044,15 +1006,15 @@ export function getWidthAndHeightFromVideoObject(objVideo) { return null; } if (playerSize[0] && typeof playerSize[0] === 'object') { - utils.logInfo('OZONE: getWidthAndHeightFromVideoObject found nested array inside playerSize.', playerSize[0]); + spec.logInfo('getWidthAndHeightFromVideoObject found nested array inside playerSize.', playerSize[0]); playerSize = playerSize[0]; if (typeof playerSize[0] !== 'number' && typeof playerSize[0] !== 'string') { - utils.logInfo('OZONE: getWidthAndHeightFromVideoObject found non-number/string type inside the INNER array in playerSize. This is totally wrong - cannot continue.', playerSize[0]); + spec.logInfo('getWidthAndHeightFromVideoObject found non-number/string type inside the INNER array in playerSize. This is totally wrong - cannot continue.', playerSize[0]); return null; } } if (playerSize.length !== 2) { - utils.logInfo('OZONE: getWidthAndHeightFromVideoObject found playerSize with length of ' + playerSize.length + '. This is totally wrong - cannot continue.'); + spec.logInfo('getWidthAndHeightFromVideoObject found playerSize with length of ' + playerSize.length + '. This is totally wrong - cannot continue.'); return null; } return ({'w': playerSize[0], 'h': playerSize[1]}); @@ -1079,17 +1041,17 @@ export function playerSizeIsNestedArray(objVideo) { * @returns {*} */ function getPlayerSizeFromObject(objVideo) { - utils.logInfo('OZONE: getPlayerSizeFromObject received object', objVideo); + spec.logInfo('getPlayerSizeFromObject received object', objVideo); let playerSize = utils.deepAccess(objVideo, 'playerSize'); if (!playerSize) { playerSize = utils.deepAccess(objVideo, 'ext.playerSize'); } if (!playerSize) { - utils.logError('OZONE: getPlayerSizeFromObject FAILED: no playerSize in video object or ext', objVideo); + spec.logError('getPlayerSizeFromObject FAILED: no playerSize in video object or ext', objVideo); return null; } if (typeof playerSize !== 'object') { - utils.logError('OZONE: getPlayerSizeFromObject FAILED: playerSize is not an object/array', objVideo); + spec.logError('getPlayerSizeFromObject FAILED: playerSize is not an object/array', objVideo); return null; } return playerSize; @@ -1099,21 +1061,23 @@ function getPlayerSizeFromObject(objVideo) { The renderer function will not assume that the renderer script is loaded - it will push() the ultimate render function call */ function newRenderer(adUnitCode, rendererOptions = {}) { + let isLoaded = window.ozoneVideo; + spec.logInfo(`newRenderer going to set loaded to ${isLoaded ? 'true' : 'false'}`); const renderer = Renderer.install({ - url: OZONE_RENDERER_URL, + url: spec.getRendererUrl(), config: rendererOptions, - loaded: false, + loaded: isLoaded, adUnitCode }); try { renderer.setRender(outstreamRender); } catch (err) { - utils.logWarn('OZONE Prebid Error calling setRender on renderer', err); + spec.logError('Prebid Error when calling setRender on renderer', JSON.parse(JSON.stringify(renderer)), err); } return renderer; } function outstreamRender(bid) { - utils.logInfo('OZONE: outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid =', bid); + spec.logInfo('outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid =', JSON.parse(JSON.stringify(bid))); // push to render queue because ozoneVideo may not be loaded yet bid.renderer.push(() => { window.ozoneVideo.outstreamRender(bid); @@ -1121,4 +1085,4 @@ function outstreamRender(bid) { } registerBidder(spec); -utils.logInfo('OZONE: ozoneBidAdapter was loaded'); +utils.logInfo(`*BidAdapter ${OZONEVERSION} was loaded`); diff --git a/modules/ozoneBidAdapter.md b/modules/ozoneBidAdapter.md index bc8cb6a6102..ca18c962219 100644 --- a/modules/ozoneBidAdapter.md +++ b/modules/ozoneBidAdapter.md @@ -37,7 +37,6 @@ adUnits = [{ siteId: '4204204201', /* An ID used to identify a site within a publisher account - required */ placementId: '0420420421', /* an ID used to identify the piece of inventory - required - for appnexus test use 13144370. */ customData: [{"settings": {}, "targeting": {"key": "value", "key2": ["value1", "value2"]}}],/* optional array with 'targeting' placeholder for passing publisher specific key-values for targeting. */ - lotameData: {"Profile": {"tpid":"value","pid":"value","Audiences": {"Audience":[{"id":"value"},{"id":"value2"}]}}}, /* optional JSON placeholder for passing Lotame DMP data */ } }] }]; @@ -52,7 +51,7 @@ adUnits = [{ code: 'id-of-your-video-div', mediaTypes: { video: { - playerSize: [640, 480], + playerSize: [640, 360], mimes: ['video/mp4'], context: 'outstream', } @@ -64,7 +63,6 @@ adUnits = [{ siteId: '4204204201', /* An ID used to identify a site within a publisher account - required */ customData: [{"settings": {}, "targeting": { "key": "value", "key2": ["value1", "value2"]}}] placementId: '0440440442', /* an ID used to identify the piece of inventory - required - for unruly test use 0440440442. */ - lotameData: {"Profile": {"tpid":"value","pid":"value","Audiences": {"Audience":[{"id":"value"},{"id":"value2"}]}}}, /* optional JSON placeholder for passing Lotame DMP data */ video: { skippable: true, /* optional */ playback_method: ['auto_play_sound_off'], /* optional */ diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index c1022608b4a..10b8ce31d28 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -6,12 +6,13 @@ import {getGranularityKeyName, getGranularityObject} from '../../../modules/ozon import * as utils from '../../../src/utils.js'; const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; const BIDDER_CODE = 'ozone'; + /* NOTE - use firefox console to deep copy the objects to use here */ -var originalPropertyBag = {'lotameWasOverridden': 0, 'pageId': null}; +var originalPropertyBag = {'pageId': null}; var validBidRequests = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -21,7 +22,7 @@ var validBidRequests = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } @@ -36,7 +37,7 @@ var validBidRequestsMulti = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' }, @@ -49,11 +50,14 @@ var validBidRequestsMulti = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c0', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; +// use 'pubcid', 'tdid', 'id5id', 'parrableId', 'idl_env', 'criteoId', 'criteortus' +// NOTE THAT criteortus is no longer referenced anywhere - should be removed asap +// see http://prebid.org/dev-docs/modules/userId.html var validBidRequestsWithUserIdData = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -63,10 +67,83 @@ var validBidRequestsWithUserIdData = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87', - userId: {'pubcid': '12345678', 'id5id': { 'uid': 'ID5-someId' }, 'criteortus': {'ozone': {'userid': 'critId123'}}, 'idl_env': 'liverampId', 'lipb': {'lipbid': 'lipbidId123'}, 'parrableId': {eid: 'parrableid123'}} + userId: { + 'pubcid': '12345678', + 'tdid': '1111tdid', + 'id5id': 'ID5-someId', + 'criteortus': {'ozone': {'userid': 'critId123'}}, + 'criteoId': '1111criteoId', + 'idl_env': 'liverampId', + 'lipb': {'lipbid': 'lipbidId123'}, + 'parrableId': {'eid': '01.5678.parrableid'} + }, + userIdAsEids: [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '12345678', + 'atype': 1 + } + ] + }, + { + 'source': 'adserver.org', + 'uids': [{ + 'id': '1111tdid', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + }] + }, + { + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'ID5-someId', + 'atype': 1, + }] + }, + { + 'source': 'criteortus', + 'uids': [{ + 'id': {'ozone': {'userid': 'critId123'}}, + 'atype': 1, + }] + }, + { + 'source': 'criteoId', + 'uids': [{ + 'id': '1111criteoId', + 'atype': 1, + }] + }, + { + 'source': 'idl_env', + 'uids': [{ + 'id': 'liverampId', + 'atype': 1, + }] + }, + { + 'source': 'lipb', + 'uids': [{ + 'id': {'lipbid': 'lipbidId123'}, + 'atype': 1, + }] + }, + { + 'source': 'parrableId', + 'uids': [{ + 'id': {'eid': '01.5678.parrableid'}, + 'atype': 1, + }] + } + ] + } ]; var validBidRequestsMinimal = [ @@ -91,7 +168,7 @@ var validBidRequestsNoSizes = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } ]; @@ -105,7 +182,7 @@ var validBidRequestsWithBannerMediaType = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, banner: { format: [{ w: 300, h: 250 }, { w: 300, h: 600 }], h: 250, topframe: 1, w: 300 } } ] }, mediaTypes: {banner: {sizes: [[300, 250], [300, 600]]}}, transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } @@ -119,7 +196,7 @@ var validBidRequestsWithNonBannerMediaTypesAndValidOutstreamVideo = [ bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, video: {skippable: true, playback_method: ['auto_play_sound_off'], targetDiv: 'some-different-div-id-to-my-adunitcode'} } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { id: '2899ec066a91ff8', tagid: 'undefined', secure: 1, video: {skippable: true, playback_method: ['auto_play_sound_off'], targetDiv: 'some-different-div-id-to-my-adunitcode'} } ] }, mediaTypes: {video: {mimes: ['video/mp4'], 'context': 'outstream', 'sizes': [640, 480], playerSize: [640, 480]}, native: {info: 'dummy data'}}, transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' } @@ -161,43 +238,21 @@ var validBidRequests1OutstreamVideo2020 = [ } } ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } + 'userId': { + 'pubcid': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56' + }, + 'userIdAsEids': [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56', + 'atype': 1 + } + ] } - } - }, - 'userId': { - 'pubcid': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56' + ] }, - 'userIdAsEids': [ - { - 'source': 'pubcid.org', - 'uids': [ - { - 'id': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56', - 'atype': 1 - } - ] - } - ], 'mediaTypes': { 'video': { 'playerSize': [ @@ -272,32 +327,10 @@ var validBidderRequest1OutstreamVideo2020 = { 'pt9': '|k0xw2vqzp33kklb3j5w4|||' } } - ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } - } - } + ] }, 'userId': { - 'id5id': { uid: 'ID5-ZHMOpSv9CkZNiNd1oR4zc62AzCgSS73fPjmQ6Od7OA' }, + 'id5id': 'ID5-ZHMOpSv9CkZNiNd1oR4zc62AzCgSS73fPjmQ6Od7OA', 'pubcid': '2ada6ae6-aeca-4e07-8922-a99b3aaf8a56' }, 'userIdAsEids': [ @@ -370,7 +403,7 @@ var validBidderRequest = { bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' }], @@ -399,7 +432,7 @@ var bidderRequestWithFullGdpr = { bidder: 'ozone', bidderRequestId: '1c1586b27a1b5c8', crumbs: {pubcid: '203a0692-f728-4856-87f6-9a25a6b63715'}, - params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, + params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', auctionId: '27dcb421-95c6-4024-a624-3c03816c5f99', imp: [ { banner: { topframe: 1, w: 300, h: 250, format: [{ w: 300, h: 250 }, { w: 300, h: 600 }] }, id: '2899ec066a91ff8', secure: 1, tagid: 'undefined' } ] }, sizes: [[300, 250], [300, 600]], transactionId: '2e63c0ed-b10c-4008-aed5-84582cecfe87' }], @@ -491,17 +524,6 @@ var bidderRequestWithPartialGdpr = { params: { publisherId: '9876abcd12-3', customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], - lotameData: { - 'Profile': { - 'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', - 'Audiences': { - 'Audience': [{'id': '99999', 'abbr': 'sports'}, { - 'id': '88888', - 'abbr': 'movie' - }, {'id': '77777', 'abbr': 'blogger'}] - } - } - }, placementId: '1310000099', siteId: '1234567890', id: 'fea37168-78f1-4a23-a40e-88437a99377e', @@ -982,29 +1004,7 @@ var multiRequest1 = [ 'pt9': '|k0xw2vqzp33kklb3j5w4|||' } } - ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } - } - } + ] }, 'mediaTypes': { 'banner': { @@ -1069,29 +1069,7 @@ var multiRequest1 = [ 'pt9': '|k0xw2vqzp33kklb3j5w4|||' } } - ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } - } - } + ] }, 'mediaTypes': { 'banner': { @@ -1165,29 +1143,7 @@ var multiBidderRequest1 = { 'pt9': '|k0xw2vqzp33kklb3j5w4|||' } } - ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } - } - } + ] }, 'mediaTypes': { 'banner': { @@ -1252,29 +1208,7 @@ var multiBidderRequest1 = { 'pt9': '|k0xw2vqzp33kklb3j5w4|||' } } - ], - 'lotameData': { - 'Profile': { - 'tpid': '4e5c21fc7c181c2b1eb3a73d543a27f6', - 'pid': '3a45fd4872fa01f35c49586d8dcb7c60', - 'Audiences': { - 'Audience': [ - { - 'id': '439847', - 'abbr': 'all' - }, - { - 'id': '446197', - 'abbr': 'Arts, Culture & Literature' - }, - { - 'id': '446198', - 'abbr': 'Business' - } - ] - } - } - } + ] }, 'mediaTypes': { 'banner': { @@ -1588,8 +1522,7 @@ describe('ozone Adapter', function () { placementId: '1310000099', publisherId: '9876abcd12-3', siteId: '1234567890', - customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}], - lotameData: {'Profile': {'tpid': 'c8ef27a0d4ba771a81159f0d2e792db4', 'Audiences': {'Audience': [{'id': '99999', 'abbr': 'sports'}, {'id': '88888', 'abbr': 'movie'}, {'id': '77777', 'abbr': 'blogger'}]}}}, + customData: [{'settings': {}, 'targeting': {'gender': 'bart', 'age': 'low'}}] }, siteId: 1234567890 } @@ -1883,26 +1816,11 @@ describe('ozone Adapter', function () { it('should not validate customParams - this is a renamed key', function () { expect(spec.isBidRequestValid(xBadCustomParams)).to.equal(false); }); - - var xBadLotame = { - bidder: BIDDER_CODE, - params: { - 'placementId': '1234567890', - 'publisherId': '9876abcd12-3', - 'lotameData': 'this should be an object', - siteId: '1234567890' - } - }; - it('should not validate lotameData being sent', function () { - expect(spec.isBidRequestValid(xBadLotame)).to.equal(false); - }); - var xBadVideoContext2 = { bidder: BIDDER_CODE, params: { 'placementId': '1234567890', 'publisherId': '9876abcd12-3', - 'lotameData': {}, siteId: '1234567890' }, mediaTypes: { @@ -1937,35 +1855,6 @@ describe('ozone Adapter', function () { instreamVid.mediaTypes.video.context = 'instream'; expect(spec.isBidRequestValid(instreamVid)).to.equal(true); }); - // validate lotame override parameters - it('should validate lotame override params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': 'tpid123'}; - }; - expect(spec.isBidRequestValid(validBidReq)).to.equal(true); - }); - it('should validate missing lotame override params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123'}; - }; - expect(spec.isBidRequestValid(validBidReq)).to.equal(false); - }); - it('should validate invalid lotame override params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': '123 "this ain\\t right!" eee'}; - }; - expect(spec.isBidRequestValid(validBidReq)).to.equal(false); - }); - it('should validate no lotame override params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {}; - }; - expect(spec.isBidRequestValid(validBidReq)).to.equal(true); - }); }); describe('buildRequests', function () { @@ -1989,19 +1878,27 @@ describe('ozone Adapter', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); - expect(data.ext.ozone.lotameData).to.be.an('object'); + expect(data.imp[0].ext.ozone.customData).to.be.an('array'); + expect(request).not.to.have.key('lotameData'); + expect(request).not.to.have.key('customData'); + }); + + it('adds all parameters inside the ext object only - lightning', function () { + let localBidReq = JSON.parse(JSON.stringify(validBidRequests)); + const request = spec.buildRequests(localBidReq, validBidderRequest.bidderRequest); + expect(request.data).to.be.a('string'); + var data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(request).not.to.have.key('lotameData'); expect(request).not.to.have.key('customData'); }); it('ignores ozoneData in & after version 2.1.1', function () { - let validBidRequestsWithOzoneData = validBidRequests; + let validBidRequestsWithOzoneData = JSON.parse(JSON.stringify(validBidRequests)); validBidRequestsWithOzoneData[0].params.ozoneData = {'networkID': '3048', 'dfpSiteID': 'd.thesun', 'sectionID': 'homepage', 'path': '/', 'sec_id': 'null', 'sec': 'sec', 'topics': 'null', 'kw': 'null', 'aid': 'null', 'search': 'null', 'article_type': 'null', 'hide_ads': '', 'article_slug': 'null'}; - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = spec.buildRequests(validBidRequestsWithOzoneData, validBidderRequest.bidderRequest); expect(request.data).to.be.a('string'); var data = JSON.parse(request.data); - expect(data.ext.ozone.lotameData).to.be.an('object'); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.ozoneData).to.be.undefined; expect(request).not.to.have.key('lotameData'); @@ -2018,7 +1915,7 @@ describe('ozone Adapter', function () { expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); - it('handles no ozone, lotame or custom data', function () { + it('handles no ozone or custom data', function () { const request = spec.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); expect(request).to.have.all.keys(['bidderRequest', 'data', 'method', 'url']); }); @@ -2080,6 +1977,7 @@ describe('ozone Adapter', function () { expect(payload.regs.ext.gdpr).to.equal(1); expect(payload.user.ext.consent).to.equal(consentString); }); + it('should set regs.ext.gdpr flag to 0 when gdprApplies is false', function () { let consentString = 'BOcocyaOcocyaAfEYDENCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NphLgA=='; let bidderRequest = validBidderRequest.bidderRequest; @@ -2118,13 +2016,14 @@ describe('ozone Adapter', function () { bidRequests[0]['userId'] = { 'criteortus': '1111', 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - 'id5id': {'uid': '2222'}, + 'id5id': '2222', 'idl_env': '3333', 'lipb': {'lipbid': '4444'}, - 'parrableId': {eid: 'eidVersion.encryptionKeyReference.encryptedValue'}, + 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', 'pubcid': '5555', 'tdid': '6666' }; + bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; const request = spec.buildRequests(bidRequests, bidderRequest); const payload = JSON.parse(request.data); let firstBid = payload.imp[0].ext.ozone; @@ -2138,62 +2037,121 @@ describe('ozone Adapter', function () { bidRequests[0]['userId'] = { 'criteortus': '1111', 'digitrustid': {data: {id: 'DTID', keyv: 4, privacy: {optout: false}, producer: 'ABC', version: 2}}, - 'id5id': {'uid': '2222'}, + 'id5id': '2222', 'idl_env': '3333', 'lipb': {'lipbid': '4444'}, - 'parrableId': {eid: 'eidVersion.encryptionKeyReference.encryptedValue'}, + 'parrableid': 'eidVersion.encryptionKeyReference.encryptedValue', // 'pubcid': '5555', // remove pubcid from here to emulate the OLD module & cause the failover code to kick in 'tdid': '6666' }; + bidRequests[0]['userIdAsEids'] = validBidRequestsWithUserIdData[0]['userIdAsEids']; const request = spec.buildRequests(bidRequests, validBidderRequest.bidderRequest); const payload = JSON.parse(request.data); expect(payload.ext.ozone.pubcid).to.equal(bidRequests[0]['crumbs']['pubcid']); delete validBidRequests[0].userId; // tidy up now, else it will screw with other tests }); - it('should add a user.ext.eids object to contain user ID data in the new location (Nov 2019)', function() { + it('should add a user.ext.eids object to contain user ID data in the new location (Nov 2019) Updated Aug 2020', function() { const request = spec.buildRequests(validBidRequestsWithUserIdData, validBidderRequest.bidderRequest); + /* + 'pubcid': '12345678', + 'tdid': '1111tdid', + 'id5id': 'ID5-someId', + 'criteortus': {'ozone': {'userid': 'critId123'}}, + 'criteoId': '1111criteoId', + 'idl_env': 'liverampId', + 'lipb': {'lipbid': 'lipbidId123'}, + 'parrableId': {'eid': '01.5678.parrableid'} + */ + const payload = JSON.parse(request.data); expect(payload.user).to.exist; expect(payload.user.ext).to.exist; expect(payload.user.ext.eids).to.exist; - expect(payload.user.ext.eids[0]['source']).to.equal('pubcid'); + expect(payload.user.ext.eids[0]['source']).to.equal('pubcid.org'); expect(payload.user.ext.eids[0]['uids'][0]['id']).to.equal('12345678'); - expect(payload.user.ext.eids[1]['source']).to.equal('pubcommon'); - expect(payload.user.ext.eids[1]['uids'][0]['id']).to.equal('12345678'); + expect(payload.user.ext.eids[1]['source']).to.equal('adserver.org'); + expect(payload.user.ext.eids[1]['uids'][0]['id']).to.equal('1111tdid'); expect(payload.user.ext.eids[2]['source']).to.equal('id5-sync.com'); expect(payload.user.ext.eids[2]['uids'][0]['id']).to.equal('ID5-someId'); - expect(payload.user.ext.eids[3]['source']).to.equal('criteortus'); - expect(payload.user.ext.eids[3]['uids'][0]['id']).to.equal('critId123'); - expect(payload.user.ext.eids[4]['source']).to.equal('liveramp.com'); - expect(payload.user.ext.eids[4]['uids'][0]['id']).to.equal('liverampId'); - expect(payload.user.ext.eids[5]['source']).to.equal('liveintent.com'); - expect(payload.user.ext.eids[5]['uids'][0]['id']).to.equal('lipbidId123'); - expect(payload.user.ext.eids[6]['source']).to.equal('parrable.com'); - expect(payload.user.ext.eids[6]['uids'][0]['id']).to.equal('parrableid123'); + expect(payload.user.ext.eids[3]['source']).to.equal('criteortus'); // this is deprecated + expect(payload.user.ext.eids[3]['uids'][0]['id']['ozone']['userid']).to.equal('critId123'); + expect(payload.user.ext.eids[4]['source']).to.equal('criteoId'); + expect(payload.user.ext.eids[4]['uids'][0]['id']).to.equal('1111criteoId'); + expect(payload.user.ext.eids[5]['source']).to.equal('idl_env'); + expect(payload.user.ext.eids[5]['uids'][0]['id']).to.equal('liverampId'); + expect(payload.user.ext.eids[6]['source']).to.equal('lipb'); + expect(payload.user.ext.eids[6]['uids'][0]['id']['lipbid']).to.equal('lipbidId123'); + expect(payload.user.ext.eids[7]['source']).to.equal('parrableId'); + expect(payload.user.ext.eids[7]['uids'][0]['id']['eid']).to.equal('01.5678.parrableid'); + }); + + it('replaces the auction url for a config override', function () { + spec.propertyBag.whitelabel = null; + let fakeOrigin = 'http://sometestendpoint'; + config.setConfig({'ozone': {'endpointOverride': {'origin': fakeOrigin}}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + expect(request.url).to.equal(fakeOrigin + '/openrtb2/auction'); + expect(request.method).to.equal('POST'); + config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); + spec.propertyBag.whitelabel = null; }); + it('replaces the renderer url for a config override', function () { + spec.propertyBag.whitelabel = null; + let fakeUrl = 'http://renderer.com'; + config.setConfig({'ozone': {'endpointOverride': {'rendererUrl': fakeUrl}}}); + const request = spec.buildRequests(validBidRequests1OutstreamVideo2020, validBidderRequest1OutstreamVideo2020.bidderRequest); + const result = spec.interpretResponse(getCleanValidVideoResponse(), validBidderRequest1OutstreamVideo2020); + const bid = result[0]; + expect(bid.renderer).to.be.an.instanceOf(Renderer); + expect(bid.renderer.url).to.equal(fakeUrl); + config.setConfig({'ozone': {'kvpPrefix': null, 'endpointOverride': null}}); + spec.propertyBag.whitelabel = null; + }); + + it('replaces the kvp prefix ', function () { + spec.propertyBag.whitelabel = null; + config.setConfig({'ozone': {'kvpPrefix': 'test'}}); + const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const data = JSON.parse(request.data); + expect(data.ext.ozone).to.haveOwnProperty('test_rw'); + config.setConfig({'ozone': {'kvpPrefix': null}}); + spec.propertyBag.whitelabel = null; + }); + + it('handles an alias ', function () { + spec.propertyBag.whitelabel = null; + config.setConfig({'lmc': {'kvpPrefix': 'test'}}); + let br = JSON.parse(JSON.stringify(validBidRequests)); + br[0]['bidder'] = 'lmc'; + const request = spec.buildRequests(br, validBidderRequest.bidderRequest); + const data = JSON.parse(request.data); + expect(data.ext.lmc).to.haveOwnProperty('test_rw'); + config.setConfig({'lmc': {'kvpPrefix': null}}); // I cant remove the key so set the value to null + spec.propertyBag.whitelabel = null; + }); + var specMock = utils.deepClone(spec); it('should use oztestmode GET value if set', function() { // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: - spec.getGetParametersAsObject = function() { + specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; }; - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequests, validBidderRequest.bidderRequest); const data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); }); it('should use oztestmode GET value if set, even if there is no customdata in config', function() { // mock the getGetParametersAsObject function to simulate GET parameters for oztestmode: - spec.getGetParametersAsObject = function() { + specMock.getGetParametersAsObject = function() { return {'oztestmode': 'mytestvalue_123'}; }; - const request = spec.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); + const request = specMock.buildRequests(validBidRequestsMinimal, validBidderRequest.bidderRequest); const data = JSON.parse(request.data); expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); }); - var specMock = utils.deepClone(spec); it('should use a valid ozstoredrequest GET value if set to override the placementId values, and set oz_rw if we find it', function() { // mock the getGetParametersAsObject function to simulate GET parameters for ozstoredrequest: specMock.getGetParametersAsObject = function() { @@ -2215,54 +2173,6 @@ describe('ozone Adapter', function () { expect(data.imp[0].ext.prebid.storedrequest.id).to.equal('1310000099'); }); - it('should pick up the value of valid lotame override parameters when there is a lotame object', function () { - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': '123eee'}; - }; - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.ext.ozone.lotameData.Profile.Audiences.Audience[0].id).to.equal('123abc'); - expect(payload.ext.ozone.oz_lot_rw).to.equal(1); - }); - it('should pick up the value of valid lotame override parameters when there is an empty lotame object', function () { - let nolotameBidReq = JSON.parse(JSON.stringify(validBidRequests)); - nolotameBidReq[0].params.lotameData = {}; - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': '123eeetpid'}; - }; - const request = spec.buildRequests(nolotameBidReq, validBidderRequest.bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.ext.ozone.lotameData.Profile.Audiences.Audience[0].id).to.equal('123abc'); - expect(payload.ext.ozone.lotameData.Profile.tpid).to.equal('123eeetpid'); - expect(payload.ext.ozone.lotameData.Profile.pid).to.equal('pid123'); - expect(payload.ext.ozone.oz_lot_rw).to.equal(1); - }); - it('should pick up the value of valid lotame override parameters when there is NO "lotame" key at all', function () { - let nolotameBidReq = JSON.parse(JSON.stringify(validBidRequests)); - delete (nolotameBidReq[0].params['lotameData']); - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': '123eeetpid'}; - }; - const request = spec.buildRequests(nolotameBidReq, validBidderRequest.bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.ext.ozone.lotameData.Profile.Audiences.Audience[0].id).to.equal('123abc'); - expect(payload.ext.ozone.lotameData.Profile.tpid).to.equal('123eeetpid'); - expect(payload.ext.ozone.lotameData.Profile.pid).to.equal('pid123'); - expect(payload.ext.ozone.oz_lot_rw).to.equal(1); - spec.propertyBag = originalPropertyBag; // tidy up - }); - // NOTE - only one negative test case; - // you can't send invalid lotame params to buildRequests because 'validate' will have rejected them - it('should not use lotame override parameters if they dont exist', function () { - expect(spec.propertyBag.lotameWasOverridden).to.equal(0); - spec.getGetParametersAsObject = function() { - return {}; // no lotame override params - }; - const request = spec.buildRequests(validBidRequests, validBidderRequest.bidderRequest); - const payload = JSON.parse(request.data); - expect(payload.ext.ozone.oz_lot_rw).to.equal(0); - }); - it('should pick up the config value of coppa & set it in the request', function () { config.setConfig({'coppa': true}); const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest.bidderRequest); @@ -2609,6 +2519,14 @@ describe('ozone Adapter', function () { const result = playerSizeIsNestedArray(obj); expect(result).to.be.null; }); + it('should add oz_appnexus_dealid into ads request if dealid exists in the auction response', function () { + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest.bidderRequest); + let validres = JSON.parse(JSON.stringify(validResponse2Bids)); + validres.body.seatbid[0].bid[0].dealid = '1234'; + const result = spec.interpretResponse(validres, request); + expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_dealid')).to.equal('1234'); + expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_dealid', '')).to.equal(''); + }); }); describe('default size', function () { @@ -2660,44 +2578,6 @@ describe('ozone Adapter', function () { config.resetConfig(); }); }); - describe('makeLotameObjectFromOverride', function() { - it('should update an object with valid lotame data', function () { - let objLotameOverride = {'oz_lotametpid': '1234', 'oz_lotameid': '12345', 'oz_lotamepid': '123456'}; - let result = spec.makeLotameObjectFromOverride( - objLotameOverride, - {'Profile': {'pid': 'originalpid', 'tpid': 'originaltpid', 'Audiences': {'Audience': [{'id': 'aud1'}]}}} - ); - expect(result.Profile.Audiences.Audience).to.be.an('array'); - expect(result.Profile.Audiences.Audience[0]).to.be.an('object'); - expect(result.Profile.Audiences.Audience[0]).to.deep.include({'id': '12345', 'abbr': '12345'}); - }); - it('should return the original object if it seems weird', function () { - let objLotameOverride = {'oz_lotametpid': '1234', 'oz_lotameid': '12345', 'oz_lotamepid': '123456'}; - let objLotameOriginal = {'Profile': {'pid': 'originalpid', 'tpid': 'originaltpid', 'somethingstrange': [{'id': 'aud1'}]}}; - let result = spec.makeLotameObjectFromOverride( - objLotameOverride, - objLotameOriginal - ); - expect(result).to.equal(objLotameOriginal); - }); - }); - describe('lotameDataIsValid', function() { - it('should allow a valid minimum lotame object', function() { - let obj = {'Profile': {'pid': '', 'tpid': '', 'Audiences': {'Audience': []}}}; - let result = spec.isLotameDataValid(obj); - expect(result).to.be.true; - }); - it('should allow a valid lotame object', function() { - let obj = {'Profile': {'pid': '12345', 'tpid': '45678', 'Audiences': {'Audience': [{'id': '1234', 'abbr': '567'}, {'id': '9999', 'abbr': '1111'}]}}}; - let result = spec.isLotameDataValid(obj); - expect(result).to.be.true; - }); - it('should disallow a lotame object without an Audience.id', function() { - let obj = {'Profile': {'tpid': '', 'pid': '', 'Audiences': {'Audience': [{'abbr': 'marktest'}]}}}; - let result = spec.isLotameDataValid(obj); - expect(result).to.be.false; - }); - }); describe('getPageId', function() { it('should return the same Page ID for multiple calls', function () { let result = spec.getPageId(); @@ -2720,24 +2600,6 @@ describe('ozone Adapter', function () { expect(result).to.equal('outstream'); }); }); - describe('getLotameOverrideParams', function() { - it('should get 3 valid lotame params that exist in GET params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'oz_lotamepid': 'pid123', 'oz_lotametpid': 'tpid123'}; - }; - let result = spec.getLotameOverrideParams(); - expect(Object.keys(result).length).to.equal(3); - }); - it('should get only 1 valid lotame param that exists in GET params', function () { - // mock the getGetParametersAsObject function to simulate GET parameters for lotame overrides: - spec.getGetParametersAsObject = function() { - return {'oz_lotameid': '123abc', 'xoz_lotamepid': 'pid123', 'xoz_lotametpid': 'tpid123'}; - }; - let result = spec.getLotameOverrideParams(); - expect(Object.keys(result).length).to.equal(1); - }); - }); describe('unpackVideoConfigIntoIABformat', function() { it('should correctly unpack a usual video config', function () { let mediaTypes = {