diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index d554c0afeda1..9ca47c575c51 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -8,7 +8,7 @@ import { contains, mergeDeep, parseUrl, - generateUUID, isInteger, deepClone + generateUUID, isInteger, deepClone, getBidIdParameter } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; @@ -17,15 +17,15 @@ import {getPriceBucketString} from '../src/cpmBucketManager.js'; import { Renderer } from '../src/Renderer.js'; import {getRefererInfo} from '../src/refererDetection.js'; const BIDDER_CODE = 'ozone'; -const ORIGIN = 'https://elb.the-ozone-project.com' // applies only to auction & cookie +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 ORIGIN_DEV = 'https://test.ozpr.net'; -const OZONEVERSION = '2.9.4'; +const OZONEVERSION = '2.9.5'; export const spec = { gvlid: 524, - aliases: [{code: 'lmc', gvlid: 524}, {code: 'venatus', gvlid: 524}], + aliases: [{code: 'venatus', gvlid: 524}], version: OZONEVERSION, code: BIDDER_CODE, supportedMediaTypes: [VIDEO, BANNER], @@ -79,7 +79,7 @@ export const spec = { if (this.batchValueIsValid(bidderConfig.batchRequests)) { this.propertyBag.whitelabel.batchRequests = bidderConfig.batchRequests; } else { - logError('bidderConfig.batchRequests must be boolean or a number. Found & ignored data type: ' + typeof bidderConfig.batchRequests); + logError('invalid config: batchRequest'); } } if (bidderConfig.hasOwnProperty('videoParams')) { @@ -90,7 +90,7 @@ export const spec = { if (this.batchValueIsValid(getBatch)) { this.propertyBag.whitelabel.batchRequests = getBatch; } else { - logError('Ignoring query param: batchRequests - this must be a positive number'); + logError('invalid GET: batchRequests'); } } try { @@ -103,7 +103,7 @@ export const spec = { this.propertyBag.whitelabel.cookieSyncUrl = ORIGIN_DEV + OZONECOOKIESYNC; } } catch (e) {} - logInfo('set propertyBag.whitelabel to', this.propertyBag.whitelabel); + logInfo('whitelabel: ', this.propertyBag.whitelabel); }, batchValueIsValid(batch) { return typeof batch === 'boolean' || (typeof batch === 'number' && batch > 0); @@ -118,11 +118,10 @@ export const spec = { return this.propertyBag.whitelabel.rendererUrl; }, getVideoPlacementValue: function(context) { - if (['instream', 'outstream'].indexOf(context) < 0) return null; + if (['instream', 'outstream'].indexOf(context) < 0) return null; /* do not allow arbitrary strings */ return deepAccess(this.propertyBag, `whitelabel.videoParams.${context}`, null); }, getBatchRequests() { - logInfo('getBatchRequests going to return ', this.propertyBag.whitelabel.batchRequests); if (this.propertyBag.whitelabel.batchRequests === true) { return 10; } if (typeof this.propertyBag.whitelabel.batchRequests === 'number' && this.propertyBag.whitelabel.batchRequests > 0) { return this.propertyBag.whitelabel.batchRequests; @@ -130,63 +129,64 @@ export const spec = { return false; }, isBidRequestValid(bid) { + let vf = 'VALIDATION FAILED'; this.loadWhitelabelData(bid); logInfo('isBidRequestValid : ', config.getConfig(), bid); let adUnitCode = bid.adUnitCode; // adunit[n].code - let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED' - if (!(bid.params.hasOwnProperty('placementId'))) { + let err1 = `${vf} : missing {param} : siteId, placementId and publisherId are REQUIRED`; + if (!(getBidIdParameter('placementId', bid.params))) { logError(err1.replace('{param}', 'placementId'), adUnitCode); return false; } if (!this.isValidPlacementId(bid.params.placementId)) { - logError('VALIDATION FAILED : placementId must be exactly 10 numeric characters', adUnitCode); + logError(`${vf} : placementId must be exactly 10 numbers`, adUnitCode); return false; } - if (!(bid.params.hasOwnProperty('publisherId'))) { + if (!(getBidIdParameter('publisherId', bid.params))) { logError(err1.replace('{param}', 'publisherId'), adUnitCode); return false; } if (!(bid.params.publisherId).toString().match(/^[a-zA-Z0-9\-]{12}$/)) { - logError('VALIDATION FAILED : publisherId must be exactly 12 alphanumeric characters including hyphens', adUnitCode); + logError(`${vf} : publisherId must be /^[a-zA-Z0-9\\-]{12}$/`, adUnitCode); return false; } - if (!(bid.params.hasOwnProperty('siteId'))) { + if (!(getBidIdParameter('siteId', bid.params))) { logError(err1.replace('{param}', 'siteId'), adUnitCode); return false; } if (!(bid.params.siteId).toString().match(/^[0-9]{10}$/)) { - logError('VALIDATION FAILED : siteId must be exactly 10 numeric characters', adUnitCode); + logError(`${vf} : siteId must be /^[0-9]{10}$/`, adUnitCode); return false; } if (bid.params.hasOwnProperty('customParams')) { - logError('VALIDATION FAILED : customParams should be renamed to customData', adUnitCode); + logError(`${vf} : customParams should be renamed: customData`, adUnitCode); return false; } if (bid.params.hasOwnProperty('customData')) { - if (!Array.isArray(bid.params.customData)) { - logError('VALIDATION FAILED : customData is not an Array', adUnitCode); + if (!isArray(bid.params.customData)) { + logError(`${vf} : customData is not an Array`, adUnitCode); return false; } if (bid.params.customData.length < 1) { - logError('VALIDATION FAILED : customData is an array but does not contain any elements', adUnitCode); + logError(`${vf} : empty customData`, adUnitCode); return false; } if (!(bid.params.customData[0]).hasOwnProperty('targeting')) { - logError('VALIDATION FAILED : customData[0] does not contain "targeting"', adUnitCode); + logError(`${vf} :no customData[0].targeting`, adUnitCode); return false; } if (typeof bid.params.customData[0]['targeting'] != 'object') { - logError('VALIDATION FAILED : customData[0] targeting is not an object', adUnitCode); + logError(`${vf} : customData[0].targeting is not an Object`, adUnitCode); return false; } } if (bid.hasOwnProperty('mediaTypes') && bid.mediaTypes.hasOwnProperty(VIDEO)) { - if (!bid.mediaTypes[VIDEO].hasOwnProperty('context')) { - logError('No video context key/value in bid. Rejecting bid: ', bid); + if (!bid.mediaTypes?.[VIDEO]?.context) { + logError(`${vf} No video context key/value`); return false; } - if (bid.mediaTypes[VIDEO].context !== 'instream' && bid.mediaTypes[VIDEO].context !== 'outstream') { - logError('video.context is invalid. Only instream/outstream video is supported. Rejecting bid: ', bid); + if (['instream', 'outstream'].indexOf(bid.mediaTypes?.[VIDEO]?.context) < 0) { + logError(`${vf} video.context is invalid.`); return false; } } @@ -207,7 +207,7 @@ export const spec = { let fledgeEnabled = !!bidderRequest.fledgeEnabled; // IF true then this is added as each bid[].ext.ae=1 let htmlParams = {'publisherId': '', 'siteId': ''}; if (validBidRequests.length > 0) { - this.cookieSyncBag.userIdObject = Object.assign(this.cookieSyncBag.userIdObject, this.findAllUserIdsFromEids(validBidRequests[0])); + Object.assign(this.cookieSyncBag.userIdObject, this.findAllUserIdsFromEids(validBidRequests[0])); this.cookieSyncBag.siteId = deepAccess(validBidRequests[0], 'params.siteId'); this.cookieSyncBag.publisherId = deepAccess(validBidRequests[0], 'params.publisherId'); htmlParams = validBidRequests[0].params; @@ -215,11 +215,9 @@ export const spec = { logInfo('cookie sync bag', this.cookieSyncBag); let singleRequest = this.getWhitelabelConfigItem('ozone.singleRequest'); singleRequest = singleRequest !== false; // undefined & true will be true - logInfo(`config ${whitelabelBidder}.singleRequest : `, singleRequest); let ozoneRequest = {}; // we only want to set specific properties on this, not validBidRequests[0].params - logInfo('going to get ortb2 from bidder request...'); let fpd = deepAccess(bidderRequest, 'ortb2', null); - logInfo('got fpd: ', fpd); + logInfo('got ortb2 fpd: ', fpd); if (fpd && deepAccess(fpd, 'user')) { logInfo('added FPD user object'); ozoneRequest.user = fpd.user; @@ -227,7 +225,7 @@ export const spec = { const getParams = this.getGetParametersAsObject(); const wlOztestmodeKey = whitelabelPrefix + 'testmode'; const isTestMode = getParams[wlOztestmodeKey] || null; // this can be any string, it's used for testing ads - ozoneRequest.device = bidderRequest?.ortb2?.device || {}; + ozoneRequest.device = bidderRequest?.ortb2?.device || {}; // 20240925 rupesh changed this let placementIdOverrideFromGetParam = this.getPlacementIdOverrideFromGetParam(); // null or string let schain = null; var auctionId = deepAccess(validBidRequests, '0.ortb2.source.tid'); @@ -239,20 +237,18 @@ export const spec = { let placementId = placementIdOverrideFromGetParam || this.getPlacementId(ozoneBidRequest); // prefer to use a valid override param, else the bidRequest placement Id obj.id = ozoneBidRequest.bidId; // this causes an error if we change it to something else, even if you update the bidRequest object: "WARNING: Bidder ozone made bid for unknown request ID: mb7953.859498327448. Ignoring." obj.tagid = placementId; - let parsed = parseUrl(this.getRefererInfo().page); - obj.secure = parsed.protocol === 'https' ? 1 : 0; + obj.secure = parseUrl(getRefererInfo().page).protocol === 'https' ? 1 : 0; let arrBannerSizes = []; if (!ozoneBidRequest.hasOwnProperty('mediaTypes')) { if (ozoneBidRequest.hasOwnProperty('sizes')) { - logInfo('no mediaTypes detected - will use the sizes array in the config root'); arrBannerSizes = ozoneBidRequest.sizes; } else { - logInfo('no mediaTypes detected, no sizes array in the config root either. Cannot set sizes for banner type'); + logInfo('no mediaTypes or sizes array. 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 */ - logInfo('setting banner size from the mediaTypes.banner element for bidId ' + obj.id + ': ', arrBannerSizes); + logInfo('setting banner size from mediaTypes.banner for bidId ' + obj.id + ': ', arrBannerSizes); } if (ozoneBidRequest.mediaTypes.hasOwnProperty(VIDEO)) { logInfo('openrtb 2.5 compliant video'); @@ -262,31 +258,31 @@ export const spec = { obj.video = this.addVideoDefaults(obj.video, ozoneBidRequest.mediaTypes[VIDEO], childConfig); } let wh = getWidthAndHeightFromVideoObject(obj.video); - logInfo('setting video object from the mediaTypes.video element: ' + obj.id + ':', obj.video, 'wh=', wh); + logInfo(`setting video object ${obj.id} from mediaTypes.video: `, obj.video, 'wh=', wh); + let settingToBe = 'setting obj.video.format to be '; // partial, reusable phrase 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. - logInfo('setting obj.video.format to be an array of objects'); + logInfo(`${settingToBe} an array of objects`); obj.video.ext.format = [wh]; } else { - logInfo('setting obj.video.format to be an object'); + logInfo(`${settingToBe} an object`); obj.video.ext.format = wh; } } else { - logWarn('cannot set w, h & format values for video; the config is not right'); + logWarn(`Failed ${settingToBe} anything - bad config`); } } if (ozoneBidRequest.mediaTypes.hasOwnProperty(NATIVE)) { obj.native = ozoneBidRequest.mediaTypes[NATIVE]; - logInfo('setting native object from the mediaTypes.native element: ' + obj.id + ':', obj.native); + logInfo(`setting native object ${obj.id} from mediaTypes.native element:`, obj.native); } if (ozoneBidRequest.hasOwnProperty('getFloor')) { - logInfo('This bidRequest object has property: getFloor'); obj.floor = this.getFloorObjectForAuction(ozoneBidRequest); logInfo('obj.floor is : ', obj.floor); } else { - logInfo('This bidRequest object DOES NOT have property: getFloor'); + logInfo('no getFloor property'); } } if (arrBannerSizes.length > 0) { @@ -306,9 +302,17 @@ export const spec = { if (ozoneBidRequest.params.hasOwnProperty('customData')) { obj.ext[whitelabelBidder].customData = ozoneBidRequest.params.customData; } + if (ozoneBidRequest.params.hasOwnProperty('ozFloor')) { + let ozFloorParsed = parseFloat(ozoneBidRequest.params.ozFloor); + if (!isNaN(ozFloorParsed)) { + obj.ext[whitelabelBidder].ozFloor = ozFloorParsed; + } else { + logError(`Ignoring invalid ozFloor value for adunit code: ${ozoneBidRequest.adUnitCode}`); + } + } logInfo(`obj.ext.${whitelabelBidder} is `, obj.ext[whitelabelBidder]); if (isTestMode != null) { - logInfo('setting isTestMode to ', isTestMode); + logInfo(`setting isTestMode: ${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; @@ -320,10 +324,10 @@ export const spec = { } if (fpd && deepAccess(fpd, 'site')) { logInfo('adding fpd.site'); - if (deepAccess(obj, 'ext.' + whitelabelBidder + '.customData.0.targeting', false)) { - obj.ext[whitelabelBidder].customData[0].targeting = Object.assign(obj.ext[whitelabelBidder].customData[0].targeting, fpd.site); + if (deepAccess(obj, `ext.${whitelabelBidder}.customData.0.targeting`, false)) { + Object.assign(obj.ext[whitelabelBidder].customData[0].targeting, fpd.site); } else { - deepSetValue(obj, 'ext.' + whitelabelBidder + '.customData.0.targeting', fpd.site); + deepSetValue(obj, `ext.${whitelabelBidder}.customData.0.targeting`, fpd.site); } } if (!schain && deepAccess(ozoneBidRequest, 'schain')) { @@ -345,15 +349,15 @@ export const spec = { if (isInteger(auctionEnvironment)) { deepSetValue(obj, 'ext.ae', auctionEnvironment); } else { - logError('ortb2Imp.ext.ae is not an integer - ignoring it for obj.id=' + obj.id); + logError(`ignoring ortb2Imp.ext.ae - not an integer for obj.id=${obj.id}`); } } return obj; }); let extObj = {}; extObj[whitelabelBidder] = {}; - extObj[whitelabelBidder][whitelabelPrefix + '_pb_v'] = OZONEVERSION; - extObj[whitelabelBidder][whitelabelPrefix + '_rw'] = placementIdOverrideFromGetParam ? 1 : 0; + extObj[whitelabelBidder][`${whitelabelPrefix}_pb_v`] = OZONEVERSION; + extObj[whitelabelBidder][`${whitelabelPrefix}_rw`] = placementIdOverrideFromGetParam ? 1 : 0; if (validBidRequests.length > 0) { let userIds = this.cookieSyncBag.userIdObject; // 2021-01-06 - slight optimisation - we've already found this info if (userIds.hasOwnProperty('pubcid.org')) { @@ -364,9 +368,9 @@ export const spec = { let ozOmpFloorDollars = this.getWhitelabelConfigItem('ozone.oz_omp_floor'); // valid only if a dollar value (typeof == 'number') logInfo(`${whitelabelPrefix}_omp_floor dollar value = `, ozOmpFloorDollars); if (typeof ozOmpFloorDollars === 'number') { - extObj[whitelabelBidder][whitelabelPrefix + '_omp_floor'] = ozOmpFloorDollars; + extObj[whitelabelBidder][`${whitelabelPrefix}_omp_floor`] = ozOmpFloorDollars; } else if (typeof ozOmpFloorDollars !== 'undefined') { - 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)); + logError(`IF set, ${whitelabelPrefix}_omp_floor must be a number eg. 1.55. Found:` + (typeof ozOmpFloorDollars)); } let ozWhitelistAdserverKeys = this.getWhitelabelConfigItem('ozone.oz_whitelist_adserver_keys'); let useOzWhitelistAdserverKeys = isArray(ozWhitelistAdserverKeys) && ozWhitelistAdserverKeys.length > 0; @@ -375,26 +379,22 @@ export const spec = { logInfo('setting aliases object'); extObj.prebid = {aliases: {'ozone': whitelabelBidder}}; } - if (getParams.hasOwnProperty('ozf')) { extObj[whitelabelBidder]['ozf'] = getParams.ozf === 'true' || getParams.ozf === '1' ? 1 : 0; } - if (getParams.hasOwnProperty('ozpf')) { extObj[whitelabelBidder]['ozpf'] = getParams.ozpf === 'true' || getParams.ozpf === '1' ? 1 : 0; } - if (getParams.hasOwnProperty('ozrp') && getParams.ozrp.match(/^[0-3]$/)) { extObj[whitelabelBidder]['ozrp'] = parseInt(getParams.ozrp); } - if (getParams.hasOwnProperty('ozip') && getParams.ozip.match(/^\d+$/)) { extObj[whitelabelBidder]['ozip'] = parseInt(getParams.ozip); } if (this.propertyBag.endpointOverride != null) { extObj[whitelabelBidder]['origin'] = this.propertyBag.endpointOverride; } let userExtEids = deepAccess(validBidRequests, '0.userIdAsEids', []); // generate the UserIDs in the correct format for UserId module ozoneRequest.site = { 'publisher': {'id': htmlParams.publisherId}, - 'page': this.getRefererInfo().page, + 'page': getRefererInfo().page, 'id': htmlParams.siteId }; ozoneRequest.test = config.getConfig('debug') ? 1 : 0; if (bidderRequest && bidderRequest.gdprConsent) { - logInfo('ADDING GDPR info'); + logInfo('ADDING GDPR'); let apiVersion = deepAccess(bidderRequest, 'gdprConsent.apiVersion', 1); ozoneRequest.regs = {ext: {gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0, apiVersion: apiVersion}}; if (deepAccess(ozoneRequest, 'regs.ext.gdpr')) { deepSetValue(ozoneRequest, 'user.ext.consent', bidderRequest.gdprConsent.consentString); } else { - logInfo('**** Strange CMP info: bidderRequest.gdprConsent exists BUT bidderRequest.gdprConsent.gdprApplies is false. See bidderRequest logged above. ****'); + logWarn('**** Strange CMP info: bidderRequest.gdprConsent exists BUT bidderRequest.gdprConsent.gdprApplies is false. See bidderRequest logged above. ****'); } } else { logInfo('WILL NOT ADD GDPR info; no bidderRequest.gdprConsent object'); @@ -417,10 +417,10 @@ export const spec = { deepSetValue(ozoneRequest, 'regs.coppa', 1); } extObj[whitelabelBidder].cookieDeprecationLabel = deepAccess(bidderRequest, 'ortb2.device.ext.cdep', 'none'); - logInfo('cookieDeprecationLabel from bidderRequest object = ' + extObj[whitelabelBidder].cookieDeprecationLabel); + logInfo(`cookieDeprecationLabel ortb2.device.ext.cdep = ${extObj[whitelabelBidder].cookieDeprecationLabel}`); let batchRequestsVal = this.getBatchRequests(); // false|numeric if (typeof batchRequestsVal === 'number') { - logInfo('going to batch the requests'); + logInfo(`Batching = ${batchRequestsVal}`); let arrRet = []; // return an array of objects containing data describing max 10 bids for (let i = 0; i < tosendtags.length; i += batchRequestsVal) { ozoneRequest.id = generateUUID(); // Unique ID of the bid request, provided by the exchange. (REQUIRED) @@ -442,9 +442,8 @@ export const spec = { logInfo('batch request going to return : ', arrRet); return arrRet; } - logInfo('requests will not be batched.'); if (singleRequest) { - logInfo('buildRequests starting to generate response for a single request'); + logInfo('single request starting'); ozoneRequest.id = generateUUID(); // Unique ID of the bid request, provided by the exchange. (REQUIRED) ozoneRequest.imp = tosendtags; ozoneRequest.ext = extObj; @@ -458,13 +457,12 @@ export const spec = { data: JSON.stringify(ozoneRequest), bidderRequest: bidderRequest }; - logInfo('buildRequests request data for single = ', deepClone(ozoneRequest)); this.propertyBag.buildRequestsEnd = new Date().getTime(); - logInfo(`buildRequests going to return for single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, ret); + logInfo(`buildRequests going to return for single at time ${this.propertyBag.buildRequestsEnd} (took ${this.propertyBag.buildRequestsEnd - this.propertyBag.buildRequestsStart}ms): `, deepClone(ret)); return ret; } let arrRet = tosendtags.map(imp => { - logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); + logInfo('non-single response, working on imp : ', imp); let ozoneRequestSingle = Object.assign({}, ozoneRequest); ozoneRequestSingle.id = generateUUID(); // Unique ID of the bid request, provided by the exchange. (REQUIRED) ozoneRequestSingle.imp = [imp]; @@ -473,7 +471,6 @@ export const spec = { if (auctionId) { deepSetValue(ozoneRequestSingle, 'source.tid', auctionId); } - logInfo('buildRequests RequestSingle (for non-single) = ', ozoneRequestSingle); return { method: 'POST', url: this.getAuctionUrl(), @@ -494,13 +491,13 @@ export const spec = { logInfo('getFloorObjectForAuction mediaTypesSizes : ', mediaTypesSizes); let ret = {}; if (mediaTypesSizes.banner) { - ret.banner = bidRequestRef.getFloor({mediaType: 'banner', currency: 'USD', size: mediaTypesSizes.banner}) || {}; + ret.banner = bidRequestRef.getFloor({mediaType: 'banner', currency: 'USD', size: mediaTypesSizes.banner[0]}); } if (mediaTypesSizes.video) { - ret.video = bidRequestRef.getFloor({mediaType: 'video', currency: 'USD', size: mediaTypesSizes.video}) || {}; + ret.video = bidRequestRef.getFloor({mediaType: 'video', currency: 'USD', size: mediaTypesSizes.video[0]}); } if (mediaTypesSizes.native) { - ret.native = bidRequestRef.getFloor({mediaType: 'native', currency: 'USD', size: mediaTypesSizes.native}) || {}; + ret.native = bidRequestRef.getFloor({mediaType: 'native', currency: 'USD', size: mediaTypesSizes.native[0]}); } logInfo('getFloorObjectForAuction returning : ', deepClone(ret)); return ret; @@ -521,6 +518,7 @@ export const spec = { return []; } let arrAllBids = []; + let labels; let enhancedAdserverTargeting = this.getWhitelabelConfigItem('ozone.enhancedAdserverTargeting'); logInfo('enhancedAdserverTargeting', enhancedAdserverTargeting); if (typeof enhancedAdserverTargeting == 'undefined') { @@ -544,17 +542,17 @@ export const spec = { let videoContext = null; let isVideo = false; let bidType = deepAccess(thisBid, 'ext.prebid.type'); - logInfo(`this bid type is : ${bidType}`, j); + logInfo(`this bid type is : ${bidType}`); let adserverTargeting = {}; if (bidType === VIDEO) { isVideo = true; this.setBidMediaTypeIfNotExist(thisBid, VIDEO); videoContext = this.getVideoContextForBidId(thisBid.bidId, request.bidderRequest.bids); // should be instream or outstream (or null if error) if (videoContext === 'outstream') { - logInfo('going to set thisBid.mediaType = VIDEO & attach a renderer to OUTSTREAM video : ', j); + logInfo('setting thisBid.mediaType = VIDEO & attach a renderer to OUTSTREAM video'); thisBid.renderer = newRenderer(thisBid.bidId); } else { - logInfo('bid is not an outstream video, will set thisBid.mediaType = VIDEO and thisBid.vastUrl and not attach a renderer: ', j); + logInfo('not an outstream video, will set thisBid.mediaType = VIDEO and thisBid.vastUrl and not attach a renderer'); thisBid.vastUrl = `https://${deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_host', 'missing_host')}${deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_path', 'missing_path')}?id=${deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_id', 'missing_id')}`; // need to see if this works ok for ozone adserverTargeting['hb_cache_host'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_host', 'no-host'); adserverTargeting['hb_cache_path'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_path', 'no-path'); @@ -600,12 +598,17 @@ export const spec = { if (bidderName.match(/^ozappnexus/)) { adserverTargeting[whitelabelPrefix + '_' + bidderName + '_sid'] = String(allBidsForThisBidid[bidderName].cid); } + labels = deepAccess(allBidsForThisBidid[bidderName], 'ext.prebid.labels', null); + if (labels) { + adserverTargeting[whitelabelPrefix + '_' + bidderName + '_labels'] = labels.join(','); + } }); } else { + let perBidInfo = `${whitelabelBidder}.enhancedAdserverTargeting is set to false. No per-bid keys will be sent to adserver.`; if (useOzWhitelistAdserverKeys) { - 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.`); + logWarn(`Your adserver keys whitelist will be ignored - ${perBidInfo}`); } else { - logInfo(`${whitelabelBidder}.enhancedAdserverTargeting is set to false, so no per-bid keys will be sent to adserver.`); + logInfo(perBidInfo); } } let {seat: winningSeat, bid: winningBid} = ozoneGetWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); @@ -616,6 +619,10 @@ export const spec = { adserverTargeting[whitelabelPrefix + '_cache_id'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_id', 'no-id'); adserverTargeting[whitelabelPrefix + '_uuid'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_uuid', 'no-id'); if (enhancedAdserverTargeting) { + labels = deepAccess(winningBid, 'ext.prebid.labels', null); + if (labels) { + adserverTargeting[whitelabelPrefix + '_labels'] = labels.join(','); + } adserverTargeting[whitelabelPrefix + '_imp_id'] = String(winningBid.impid); adserverTargeting[whitelabelPrefix + '_pb_v'] = OZONEVERSION; adserverTargeting[whitelabelPrefix + '_pb'] = winningBid.price; @@ -624,7 +631,7 @@ export const spec = { adserverTargeting[whitelabelPrefix + '_size'] = `${winningBid.width}x${winningBid.height}`; } if (useOzWhitelistAdserverKeys) { // delete any un-whitelisted keys - logInfo('Going to filter out adserver targeting keys not in the whitelist: ', ozWhitelistAdserverKeys); + logInfo('Filtering 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; @@ -633,10 +640,10 @@ export const spec = { } let ret = arrAllBids; let fledgeAuctionConfigs = deepAccess(serverResponse, 'ext.igi') || []; // 20240606 standardising - if (Array.isArray(fledgeAuctionConfigs) && fledgeAuctionConfigs.length > 0) { + if (isArray(fledgeAuctionConfigs) && fledgeAuctionConfigs.length > 0) { fledgeAuctionConfigs = fledgeAuctionConfigs.filter(config => { if (!this.isValidAuctionConfig(config)) { - logWarn('Malformed auction config detected:', config); + logWarn('Removing malformed fledge auction config:', config); return false; } return true; @@ -648,7 +655,7 @@ export const spec = { } let endTime = new Date().getTime(); logInfo(`interpretResponse going to return at time ${endTime} (took ${endTime - startTime}ms) Time from buildRequests Start -> interpretRequests End = ${endTime - this.propertyBag.buildRequestsStart}ms`); - logInfo('interpretResponse arrAllBids (serialised): ', deepClone(ret)); // this is ok to log because the renderer has not been attached yet + logInfo('will return: ', deepClone(ret)); // this is ok to log because the renderer has not been attached yet return ret; }, isValidAuctionConfig(config) { @@ -706,7 +713,7 @@ export const spec = { arrQueryString.push('gdpr_consent=' + deepAccess(gdprConsent, 'consentString', '')); arrQueryString.push('usp_consent=' + (usPrivacy || '')); arrQueryString.push('gpp=' + gppString); - if (Array.isArray(applicableSections)) { + if (isArray(applicableSections)) { arrQueryString.push(`gpp_sid=${applicableSections.join()}`); } for (let keyname in this.cookieSyncBag.userIdObject) { @@ -745,7 +752,7 @@ export const spec = { findAllUserIdsFromEids(bidRequest) { let ret = {}; if (!bidRequest.hasOwnProperty('userIdAsEids')) { - logInfo('findAllUserIdsFromEids - no bidRequest.userIdAsEids object - will quit'); + logInfo('findAllUserIdsFromEids - no bidRequest.userIdAsEids object was found on the bid!'); this.tryGetPubCidFromOldLocation(ret, bidRequest); // legacy return ret; } @@ -771,7 +778,7 @@ export const spec = { let arr = this.getGetParametersAsObject(); if (arr.hasOwnProperty(whitelabelPrefix + 'storedrequest')) { if (this.isValidPlacementId(arr[whitelabelPrefix + 'storedrequest'])) { - logInfo(`using GET ${whitelabelPrefix}storedrequest ` + arr[whitelabelPrefix + 'storedrequest'] + ' to replace placementId'); + logInfo(`using GET ${whitelabelPrefix}storedrequest=` + arr[whitelabelPrefix + 'storedrequest'] + ' to replace placementId'); return arr[whitelabelPrefix + 'storedrequest']; } else { logError(`GET ${whitelabelPrefix}storedrequest FAILED VALIDATION - will not use it`); @@ -780,33 +787,14 @@ export const spec = { return null; }, getGetParametersAsObject() { - let parsed = parseUrl(this.getRefererInfo().location); + let parsed = parseUrl(getRefererInfo().location); logInfo('getGetParametersAsObject found:', parsed.search); return parsed.search; }, - getRefererInfo() { - if (getRefererInfo().hasOwnProperty('location')) { - logInfo('FOUND location on getRefererInfo OK (prebid >= 7); will use getRefererInfo for location & page'); - return getRefererInfo(); - } else { - logInfo('DID NOT FIND location on getRefererInfo (prebid < 7); will use legacy code that ALWAYS worked reliably to get location & page ;-)'); - try { - return { - page: top.location.href, - location: top.location.href - }; - } catch (e) { - return { - page: window.location.href, - location: window.location.href - }; - } - } - }, blockTheRequest() { let ozRequest = this.getWhitelabelConfigItem('ozone.oz_request'); - if (typeof ozRequest == 'boolean' && !ozRequest) { - logWarn(`Will not allow auction : ${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); + if (ozRequest === false) { + logWarn(`Will not allow the auction : ${this.propertyBag.whitelabel.keyPrefix}_request is set to false`); return true; } return false; @@ -843,7 +831,7 @@ export const spec = { } } if (objConfig.hasOwnProperty('ext') && typeof objConfig.ext === 'object') { - if (objConfig.hasOwnProperty('ext')) { + if (ret.hasOwnProperty('ext')) { ret.ext = mergeDeep(ret.ext, objConfig.ext); } else { ret.ext = objConfig.ext; @@ -864,7 +852,7 @@ export const spec = { let skippable = deepAccess(objConfig, 'skippable', null); if (skippable == null) { if (addIfMissing && !objRet.hasOwnProperty('skip')) { - objRet.skip = skippable ? 1 : 0; + objRet.skip = 0; } } else { objRet.skip = skippable ? 1 : 0; @@ -915,8 +903,8 @@ export function injectAdIdsIntoAllBidResponses(seatbid) { return seatbid; } export function checkDeepArray(Arr) { - if (Array.isArray(Arr)) { - if (Array.isArray(Arr[0])) { + if (isArray(Arr)) { + if (isArray(Arr[0])) { return Arr[0]; } else { return Arr; @@ -1047,12 +1035,12 @@ export function getWidthAndHeightFromVideoObject(objVideo) { logInfo('getWidthAndHeightFromVideoObject found nested array inside playerSize.', playerSize[0]); playerSize = playerSize[0]; if (typeof playerSize[0] !== 'number' && typeof playerSize[0] !== 'string') { - logInfo('getWidthAndHeightFromVideoObject found non-number/string type inside the INNER array in playerSize. This is totally wrong - cannot continue.', playerSize[0]); + logError('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) { - logInfo('getWidthAndHeightFromVideoObject found playerSize with length of ' + playerSize.length + '. This is totally wrong - cannot continue.'); + logError('getWidthAndHeightFromVideoObject found playerSize with length of ' + playerSize.length + '. This is totally wrong - cannot continue.'); return null; } return ({'w': playerSize[0], 'h': playerSize[1]}); @@ -1085,7 +1073,7 @@ function getPlayerSizeFromObject(objVideo) { } function newRenderer(adUnitCode, rendererOptions = {}) { let isLoaded = window.ozoneVideo; - logInfo(`newRenderer going to set loaded to ${isLoaded ? 'true' : 'false'}`); + logInfo(`newRenderer will set loaded to ${isLoaded ? 'true' : 'false'}`); const renderer = Renderer.install({ url: spec.getRendererUrl(), config: rendererOptions, @@ -1095,16 +1083,15 @@ function newRenderer(adUnitCode, rendererOptions = {}) { try { renderer.setRender(outstreamRender); } catch (err) { - logError('Prebid Error when calling setRender on renderer', renderer, err); + logError('Prebid Error calling renderer.setRender', renderer, err); } logInfo('returning renderer object'); return renderer; } function outstreamRender(bid) { - logInfo('outstreamRender called. Going to push the call to window.ozoneVideo.outstreamRender(bid) bid = (first static, then reference)'); - logInfo(deepClone(spec.getLoggableBidObject(bid))); + logInfo('outstreamRender got', deepClone(spec.getLoggableBidObject(bid))); bid.renderer.push(() => { - logInfo('Going to execute window.ozoneVideo.outstreamRender'); + logInfo('outstreamRender: Going to execute window.ozoneVideo.outstreamRender'); window.ozoneVideo.outstreamRender(bid); }); } diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index f504af91df50..b2b494b04c6e 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -7,6 +7,12 @@ import * as utils from '../../../src/utils.js'; import {deepSetValue} from '../../../src/utils.js'; const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; const BIDDER_CODE = 'ozone'; +spec.getGetParametersAsObject = function() { + return { + page: 'https://www.ardm.io/sometestPage/?qsParam1=123', + location: 'https://www.ardm.io/sometestPage/?qsParam1=123' + }; +} var validBidRequests = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -2748,18 +2754,18 @@ describe('ozone Adapter', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); expect(data.ext.ozone).to.haveOwnProperty('test_rw'); - config.setConfig({'ozone': {'kvpPrefix': null}}); + config.resetConfig(); spec.propertyBag.whitelabel = null; }); it('handles an alias ', function () { spec.propertyBag.whitelabel = null; - config.setConfig({'lmc': {'kvpPrefix': 'test'}}); + config.setConfig({'venatus': {'kvpPrefix': 've'}}); let br = JSON.parse(JSON.stringify(validBidRequests)); - br[0]['bidder'] = 'lmc'; + br[0]['bidder'] = 'venatus'; const request = spec.buildRequests(br, validBidderRequest); 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 + expect(data.ext.venatus).to.haveOwnProperty('ve_rw'); + config.resetConfig(); spec.propertyBag.whitelabel = null; }); it('should use oztestmode GET value if set', function() { @@ -2772,29 +2778,14 @@ describe('ozone Adapter', function () { 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 pass through GET params if present: ozf, ozpf, ozrp, ozip', function() { + it('should ignore these GET params if present (removed 202410): ozf, ozpf, ozrp, ozip', function() { var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { - return {ozf: '1', ozpf: '0', ozrp: '2', ozip: '123'}; + return {ozf: '1', ozpf: '10', ozrp: '2', ozip: '123'}; }; const request = specMock.buildRequests(validBidRequests, validBidderRequest); const data = JSON.parse(request.data); - expect(data.ext.ozone.ozf).to.equal(1); - expect(data.ext.ozone.ozpf).to.equal(0); - expect(data.ext.ozone.ozrp).to.equal(2); - expect(data.ext.ozone.ozip).to.equal(123); - }); - it('should pass through GET params if present: ozf, ozpf, ozrp, ozip with alternative values', function() { - var specMock = utils.deepClone(spec); - specMock.getGetParametersAsObject = function() { - return {ozf: 'false', ozpf: 'true', ozrp: 'xyz', ozip: 'hello'}; - }; - const request = specMock.buildRequests(validBidRequests, validBidderRequest); - const data = JSON.parse(request.data); - expect(data.ext.ozone.ozf).to.equal(0); - expect(data.ext.ozone.ozpf).to.equal(1); - expect(data.ext.ozone).to.not.haveOwnProperty('ozrp'); - expect(data.ext.ozone).to.not.haveOwnProperty('ozip'); + expect(data.ext.ozone).to.not.have.any.keys('zf', 'ozpf', 'ozrp', 'ozip'); }); it('should use oztestmode GET value if set, even if there is no customdata in config', function() { var specMock = utils.deepClone(spec); @@ -2950,24 +2941,49 @@ describe('ozone Adapter', function () { const payload = JSON.parse(request.data); expect(payload.regs).to.include.keys('coppa'); expect(payload.regs.coppa).to.equal(1); + config.resetConfig(); }); it('should pick up the config value of coppa & only set it in the request if its true', function () { config.setConfig({'coppa': false}); const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'regs.coppa')).to.be.undefined; + config.resetConfig(); }); it('should handle oz_omp_floor correctly', function () { config.setConfig({'ozone': {'oz_omp_floor': 1.56}}); const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'ext.ozone.oz_omp_floor')).to.equal(1.56); + config.resetConfig(); }); it('should ignore invalid oz_omp_floor values', function () { config.setConfig({'ozone': {'oz_omp_floor': '1.56'}}); const request = spec.buildRequests(validBidRequestsNoSizes, validBidderRequest); const payload = JSON.parse(request.data); expect(utils.deepAccess(payload, 'ext.ozone.oz_omp_floor')).to.be.undefined; + config.resetConfig(); + }); + it('should handle a valid ozFloor string value in the adunit correctly', function () { + let cloneBidRequests = JSON.parse(JSON.stringify(validBidRequests)); + cloneBidRequests[0].params.ozFloor = '0.1234'; // string or float - doesnt matter + const request = spec.buildRequests(cloneBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + expect(utils.deepAccess(payload, 'imp.0.ext.ozone.ozFloor')).to.equal(0.1234); + }); + it('should handle a valid ozFloor float value in the adunit correctly', function () { + let cloneBidRequests = JSON.parse(JSON.stringify(validBidRequests)); + cloneBidRequests[0].params.ozFloor = 0.1234; // string or float - doesnt matter + const request = spec.buildRequests(cloneBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + expect(utils.deepAccess(payload, 'imp.0.ext.ozone.ozFloor')).to.equal(0.1234); + }); + it('should ignore an invalid ozFloor string value in the adunit correctly', function () { + let cloneBidRequests = JSON.parse(JSON.stringify(validBidRequests)); + cloneBidRequests[0].params.ozFloor = 'this is no good!'; // string or float - doesnt matter + const request = spec.buildRequests(cloneBidRequests, validBidderRequest); + const payload = JSON.parse(request.data); + expect(utils.deepAccess(payload, 'imp.0.ext.ozone.ozFloor', null)).to.be.null; }); it('should should contain a unique page view id in the auction request which persists across calls', function () { let request = spec.buildRequests(validBidRequests, validBidderRequest); @@ -3098,6 +3114,30 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(payload, 'imp.0.floor.banner.currency')).to.equal('USD'); expect(utils.deepAccess(payload, 'imp.0.floor.banner.floor')).to.equal(0.8); }); + it(' (getFloorObjectForAuction) should handle advanced/custom floor config function correctly (note you cant fully test floor functionality because it relies on the floor module - only our code that interacts with it; we must extract the first w/h pair)', function () { + let testBidObject = { + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + }, + video: { + playerSize: [[640, 360]] + }, + native: { + image: { + sizes: [[300, 250], [640, 480]] + } + } + }, + getFloor: function(obj) { + return obj.size; // we just want to look at the size that was sent + } + }; + let floorObject = spec.getFloorObjectForAuction(testBidObject); + expect(floorObject.banner).to.deep.equal([300, 250]); + expect(floorObject.video).to.deep.equal([640, 360]); + expect(floorObject.native).to.deep.equal([300, 250]); + }); it('handles schain object in each bidrequest (will be the same in each br)', function () { let br = JSON.parse(JSON.stringify(validBidRequests)); let schainConfigObject = { @@ -3178,6 +3218,27 @@ describe('ozone Adapter', function () { expect(payload.imp[0].ext.ozone.transactionId).to.equal(valid6BidRequestsWithAuctionIdTransactionId[0].ortb2Imp.ext.tid); config.resetConfig(); }); + it('should handle ortb2 device data', function () { + const bidderRequest = JSON.parse(JSON.stringify(validBidderRequest)); + bidderRequest.ortb2 = { + device: { + w: 980, + h: 1720, + dnt: 0, + ua: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/125.0.6422.80 Mobile/15E148 Safari/604.1', + language: 'en', + devicetype: 1, + make: 'Apple', + model: 'iPhone 12 Pro Max', + os: 'iOS', + osv: '17.4', + ext: {fiftyonedegrees_deviceId: '17595-133085-133468-18092'}, + }, + }; + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.device).to.deep.equal(bidderRequest.ortb2.device); + }); }); describe('interpretResponse', function () { beforeEach(function () { @@ -3279,6 +3340,14 @@ describe('ozone Adapter', function () { expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr')).to.equal(1); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid')).to.equal('ZjbsYE1q'); }); + it('Alias venatus: should handle ext.bidder.venatus.floor correctly, setting flr & rid as necessary', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + let vres = JSON.parse(JSON.stringify(validResponse)); + vres.body.seatbid[0].bid[0].ext.bidder.ozone = {floor: 1, ruleId: 'ZjbsYE1q'}; + const result = spec.interpretResponse(vres, request); + expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_flr')).to.equal(1); + expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_rid')).to.equal('ZjbsYE1q'); + }); it('should handle ext.bidder.ozone.floor correctly, inserting 0 as necessary', function () { const request = spec.buildRequests(validBidRequests, validBidderRequest); let vres = JSON.parse(JSON.stringify(validResponse)); @@ -3406,6 +3475,50 @@ describe('ozone Adapter', function () { expect(result).to.be.an('object'); expect(result.fledgeAuctionConfigs[0]['impid']).to.equal('1'); }); + it('should add labels in the adserver request if they are present in the auction response', function () { + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); + let validres = JSON.parse(JSON.stringify(validResponse2Bids)); + validres.body.seatbid.push(JSON.parse(JSON.stringify(validres.body.seatbid[0]))); // add another bidder + validres.body.seatbid[1].seat = 'marktest'; + validres.body.seatbid[1].bid[0].ext.prebid.labels = ['b1', 'b2', 'b3']; + validres.body.seatbid[1].bid[0].price = 10; // will win + validres.body.seatbid[1].bid[1].price = 0; // will lose + validres.body.seatbid[0].bid[0].ext.prebid.labels = ['bid1label1', 'bid1label2', 'bid1label3']; + validres.body.seatbid[0].bid[1].ext.prebid.labels = ['bid2label']; + const result = spec.interpretResponse(validres, request); + expect(result.length).to.equal(4); // 4 bids will be returned; 2 from each bidder. All will have the winning keys attached. + expect(utils.deepAccess(result[0].adserverTargeting, 'oz_winner')).to.equal('marktest'); // the first bid + expect(utils.deepAccess(result[0].adserverTargeting, 'oz_labels')).to.equal('b1,b2,b3'); // the winner + expect(utils.deepAccess(result[0].adserverTargeting, 'oz_appnexus_labels')).to.equal('bid1label1,bid1label2,bid1label3'); + expect(utils.deepAccess(result[1].adserverTargeting, 'oz_winner')).to.equal('appnexus'); // the second bid + expect(utils.deepAccess(result[1].adserverTargeting, 'oz_appnexus_labels')).to.equal('bid2label'); + expect(utils.deepAccess(result[1].adserverTargeting, 'oz_labels')).to.equal('bid2label'); // the second adslot winning label + expect(utils.deepAccess(result[2].adserverTargeting, 'oz_labels')).to.equal('b1,b2,b3'); // we're back to the first of the 2 bids again + expect(utils.deepAccess(result[3].adserverTargeting, 'oz_labels')).to.equal('bid2label'); // the second adslot winning label + }); + it('should not add labels in the adserver request if they are present in the auction response when config contains ozone.enhancedAdserverTargeting', function () { + config.setConfig({'ozone': {'enhancedAdserverTargeting': false}}); + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); + let validres = JSON.parse(JSON.stringify(validResponse2Bids)); + validres.body.seatbid.push(JSON.parse(JSON.stringify(validres.body.seatbid[0]))); // add another bidder + validres.body.seatbid[1].seat = 'marktest'; + validres.body.seatbid[1].bid[0].ext.prebid.labels = ['b1', 'b2', 'b3']; + validres.body.seatbid[1].bid[0].price = 10; // will win + validres.body.seatbid[1].bid[1].price = 0; // will lose + validres.body.seatbid[0].bid[0].ext.prebid.labels = ['bid1label1', 'bid1label2', 'bid1label3']; + validres.body.seatbid[0].bid[1].ext.prebid.labels = ['bid2label']; + const result = spec.interpretResponse(validres, request); + expect(result.length).to.equal(4); // 4 bids will be returned; 2 from each bidder. All will have the winning keys attached. + expect(utils.deepAccess(result[0].adserverTargeting, 'oz_winner')).to.equal('marktest'); // the first bid + expect(result[0].adserverTargeting).to.not.have.property('oz_labels'); + expect(result[0].adserverTargeting).to.not.have.property('oz_appnexus_labels'); + expect(utils.deepAccess(result[1].adserverTargeting, 'oz_winner')).to.equal('appnexus'); // the second bid + expect(result[1].adserverTargeting).to.not.have.property('oz_appnexus_labels'); + expect(result[1].adserverTargeting).to.not.have.property('oz_labels'); // the second adslot winning label + expect(result[2].adserverTargeting).to.not.have.property('oz_labels'); // we're back to the first of the 2 bids again + expect(result[3].adserverTargeting).to.not.have.property('oz_labels'); // the second adslot winning label + config.resetConfig(); + }); }); describe('userSyncs', function () { it('should fail gracefully if no server response', function () {