From 7178f8c30d35214cd89e556020b0905de849f136 Mon Sep 17 00:00:00 2001 From: slimkrazy Date: Tue, 20 Jun 2023 14:34:17 +0100 Subject: [PATCH 1/6] Yahoo bid adapter rebrand --- modules/yahooAdvertisingBidAdapter.js | 692 +++++++++++++++ modules/yahooAdvertisingBidAdapter.md | 833 ++++++++++++++++++ ...pec.js => yahooAdvertisingAdapter_spec.js} | 641 ++++++++------ 3 files changed, 1874 insertions(+), 292 deletions(-) create mode 100644 modules/yahooAdvertisingBidAdapter.js create mode 100644 modules/yahooAdvertisingBidAdapter.md rename test/spec/modules/{yahoosspBidAdapter_spec.js => yahooAdvertisingAdapter_spec.js} (76%) diff --git a/modules/yahooAdvertisingBidAdapter.js b/modules/yahooAdvertisingBidAdapter.js new file mode 100644 index 00000000000..effeb845e21 --- /dev/null +++ b/modules/yahooAdvertisingBidAdapter.js @@ -0,0 +1,692 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { deepAccess, isFn, isStr, isNumber, isArray, isEmpty, isPlainObject, generateUUID, logInfo, logWarn } from '../src/utils.js'; +import { config } from '../src/config.js'; +import { Renderer } from '../src/Renderer.js'; +import {hasPurpose1Consent} from '../src/utils/gpdr.js'; + +const INTEGRATION_METHOD = 'prebid.js'; +const BIDDER_CODE = 'yahooAdvertising'; +const BIDDER_ALIASES = ['yahoossp'] +const GVLID = 25; +const PREBID_VERSION = '$prebid.version$'; +const DEFAULT_BID_TTL = 300; +const TEST_MODE_DCN = '8a969516017a7a396ec539d97f540011'; +const TEST_MODE_PUBID_DCN = '1234567'; +const TEST_MODE_BANNER_POS = '8a969978017a7aaabab4ab0bc01a0009'; +const TEST_MODE_VIDEO_POS = '8a96958a017a7a57ac375d50c0c700cc'; +const DEFAULT_RENDERER_TIMEOUT = 700; +const DEFAULT_CURRENCY = 'USD'; +const SSP_ENDPOINT_DCN_POS = 'https://c2shb.pubgw.yahoo.com/bidRequest'; +const SSP_ENDPOINT_PUBID = 'https://c2shb.pubgw.yahoo.com/admax/bid/partners/PBJS'; +const SUPPORTED_USER_ID_SOURCES = [ + 'admixer.net', + 'adserver.org', + 'adtelligent.com', + 'akamai.com', + 'amxdt.net', + 'audigent.com', + 'britepool.com', + 'criteo.com', + 'crwdcntrl.net', + 'deepintent.com', + 'epsilon.com', + 'hcn.health', + 'id5-sync.com', + 'idx.lat', + 'intentiq.com', + 'intimatemerger.com', + 'liveintent.com', + 'liveramp.com', + 'mediawallahscript.com', + 'merkleinc.com', + 'netid.de', + 'neustar.biz', + 'nextroll.com', + 'novatiq.com', + 'parrable.com', + 'pubcid.org', + 'quantcast.com', + 'tapad.com', + 'uidapi.com', + 'yahoo.com', + 'zeotap.com' +]; + +/* Utility functions */ + +function getConfigValue(bid, key) { + const bidderCode = bid.bidder || bid.bidderCode; + return config.getConfig(`${bidderCode}.${key}`); +} + +function getSize(size) { + return { + w: parseInt(size[0]), + h: parseInt(size[1]) + } +} + +function transformSizes(sizes) { + if (isArray(sizes) && sizes.length === 2 && !isArray(sizes[0])) { + return [ getSize(sizes) ]; + } + return sizes.map(getSize); +} + +function extractUserSyncUrls(syncOptions, pixels) { + let itemsRegExp = /(img|iframe)[\s\S]*?src\s*=\s*("|')(.*?)\2/gi; + let tagNameRegExp = /\w*(?=\s)/; + let srcRegExp = /src=("|')(.*?)\1/; + let userSyncObjects = []; + + if (pixels) { + let matchedItems = pixels.match(itemsRegExp); + if (matchedItems) { + matchedItems.forEach(item => { + let tagName = item.match(tagNameRegExp)[0]; + let url = item.match(srcRegExp)[2]; + + if (tagName && url) { + let tagType = tagName.toLowerCase() === 'img' ? 'image' : 'iframe'; + if ((!syncOptions.iframeEnabled && tagType === 'iframe') || + (!syncOptions.pixelEnabled && tagType === 'image')) { + return; + } + userSyncObjects.push({ + type: tagType, + url: url + }); + } + }); + } + } + + return userSyncObjects; +} + +/** + * @param {string} url + * @param {object} consentData + * @param {object} consentData.gpp + * @param {string} consentData.gpp.gppConsent + * @param {array} consentData.gpp.applicableSections + * @param {object} consentData.gdpr + * @param {object} consentData.gdpr.consentString + * @param {object} consentData.gdpr.gdprApplies + * @param {string} consentData.uspConsent + */ +function updateConsentQueryParams(url, consentData) { + const parameterMap = { + 'gdpr_consent': consentData.gdpr.consentString, + 'gdpr': consentData.gdpr.gdprApplies ? '1' : '0', + 'us_privacy': consentData.uspConsent, + 'gpp': consentData.gpp.gppString, + 'gpp_sid': consentData.gpp.applicableSections ? consentData.gpp.applicableSections.join(',') : '' + } + + const existingUrl = new URL(url); + const params = existingUrl.searchParams; + + for (const [key, value] of Object.entries(parameterMap)) { + params.set(key, value); + } + + existingUrl.search = params.toString(); + return existingUrl.toString(); +}; + +function getSupportedEids(bid) { + if (isArray(deepAccess(bid, 'userIdAsEids'))) { + return bid.userIdAsEids.filter(eid => { + return SUPPORTED_USER_ID_SOURCES.indexOf(eid.source) !== -1; + }); + } + return []; +} + +function isSecure(bid) { + return deepAccess(bid, 'params.bidOverride.imp.secure') || (document.location.protocol === 'https:') ? 1 : 0; +}; + +function getPubIdMode(bid) { + let pubIdMode; + if (deepAccess(bid, 'params.pubId')) { + pubIdMode = true; + } else if (deepAccess(bid, 'params.dcn') && deepAccess(bid, 'params.pos')) { + pubIdMode = false; + }; + return pubIdMode; +}; + +function getAdapterMode(bid) { + let adapterMode = getConfigValue(bid, 'mode'); + adapterMode = adapterMode ? adapterMode.toLowerCase() : undefined; + if (typeof adapterMode === 'undefined' || adapterMode === BANNER) { + return BANNER; + } else if (adapterMode === VIDEO) { + return VIDEO; + } else if (adapterMode === 'all') { + return '*'; + } +}; + +function getResponseFormat(bid) { + const adm = bid.adm; + if (adm.indexOf('o2playerSettings') !== -1 || adm.indexOf('YAHOO.VideoPlatform.VideoPlayer') !== -1 || adm.indexOf('AdPlacement') !== -1) { + return BANNER; + } else if (adm.indexOf('VAST') !== -1) { + return VIDEO; + } +}; + +function getFloorModuleData(bid) { + const adapterMode = getAdapterMode(bid); + const getFloorRequestObject = { + currency: deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CURRENCY, + mediaType: adapterMode, + size: '*' + }; + return (isFn(bid.getFloor)) ? bid.getFloor(getFloorRequestObject) : false; +}; + +function filterBidRequestByMode(validBidRequests) { + const mediaTypesMode = getAdapterMode(validBidRequests[0]); + let result = []; + if (mediaTypesMode === BANNER) { + result = validBidRequests.filter(bid => { + return Object.keys(bid.mediaTypes).some(item => item === BANNER); + }); + } else if (mediaTypesMode === VIDEO) { + result = validBidRequests.filter(bid => { + return Object.keys(bid.mediaTypes).some(item => item === VIDEO); + }); + } else if (mediaTypesMode === '*') { + result = validBidRequests.filter(bid => { + return Object.keys(bid.mediaTypes).some(item => item === BANNER || item === VIDEO); + }); + }; + return result; +}; + +function validateAppendObject(validationType, allowedKeys, inputObject, appendToObject) { + const outputObject = { + ...appendToObject + }; + + for (const objectKey in inputObject) { + switch (validationType) { + case 'string': + if (allowedKeys.indexOf(objectKey) !== -1 && isStr(inputObject[objectKey])) { + outputObject[objectKey] = inputObject[objectKey]; + }; + break; + case 'number': + if (allowedKeys.indexOf(objectKey) !== -1 && isNumber(inputObject[objectKey])) { + outputObject[objectKey] = inputObject[objectKey]; + }; + break; + + case 'array': + if (allowedKeys.indexOf(objectKey) !== -1 && isArray(inputObject[objectKey])) { + outputObject[objectKey] = inputObject[objectKey]; + }; + break; + case 'object': + if (allowedKeys.indexOf(objectKey) !== -1 && isPlainObject(inputObject[objectKey])) { + outputObject[objectKey] = inputObject[objectKey]; + }; + break; + case 'objectAllKeys': + if (isPlainObject(inputObject)) { + outputObject[objectKey] = inputObject[objectKey]; + }; + break; + }; + }; + return outputObject; +}; + +function getTtl(bidderRequest) { + const globalTTL = getConfigValue(bidderRequest, 'ttl'); + return globalTTL ? validateTTL(globalTTL) : validateTTL(deepAccess(bidderRequest, 'params.ttl')); +}; + +function validateTTL(ttl) { + return (isNumber(ttl) && ttl > 0 && ttl < 3600) ? ttl : DEFAULT_BID_TTL +}; + +function isNotEmptyStr(value) { + return (isStr(value) && value.length > 0); +}; + +function generateOpenRtbObject(bidderRequest, bid) { + if (bidderRequest) { + let outBoundBidRequest = { + id: generateUUID(), + cur: [getFloorModuleData(bidderRequest).currency || deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CURRENCY], + imp: [], + site: { + page: deepAccess(bidderRequest, 'refererInfo.page'), + }, + device: { + dnt: 0, + ua: navigator.userAgent, + ip: deepAccess(bid, 'params.bidOverride.device.ip') || deepAccess(bid, 'params.ext.ip') || undefined, + w: window.screen.width, + h: window.screen.height + }, + regs: { + ext: { + 'us_privacy': bidderRequest.uspConsent ? bidderRequest.uspConsent : '', + gdpr: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies ? 1 : 0, + gpp: bidderRequest.gppConsent.gppString, + gpp_sid: bidderRequest.gppConsent.applicableSections + } + }, + source: { + ext: { + hb: 1, + prebidver: PREBID_VERSION, + integration: { + name: INTEGRATION_METHOD, + ver: PREBID_VERSION + } + }, + fd: 1 + }, + user: { + ext: { + consent: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies + ? bidderRequest.gdprConsent.consentString : '', + eids: getSupportedEids(bid) + } + } + }; + + if (getPubIdMode(bid) === true) { + outBoundBidRequest.site.publisher = { + id: bid.params.pubId + } + if (deepAccess(bid, 'params.bidOverride.site.id') || deepAccess(bid, 'params.siteId')) { + outBoundBidRequest.site.id = deepAccess(bid, 'params.bidOverride.site.id') || bid.params.siteId; + } + } else { + outBoundBidRequest.site.id = bid.params.dcn; + }; + + if (bidderRequest.ortb2?.regs?.gpp) { + outBoundBidRequest.regs.ext.gpp = bidderRequest.ortb2.regs.gpp; + outBoundBidRequest.regs.ext.gpp_sid = bidderRequest.ortb2.regs.gpp_sid + }; + + if (bidderRequest.ortb2) { + outBoundBidRequest = appendFirstPartyData(outBoundBidRequest, bid); + }; + + const schainData = deepAccess(bid, 'schain.nodes'); + if (isArray(schainData) && schainData.length > 0) { + outBoundBidRequest.source.ext.schain = bid.schain; + outBoundBidRequest.source.ext.schain.nodes[0].rid = outBoundBidRequest.id; + }; + + return outBoundBidRequest; + }; +}; + +function appendImpObject(bid, openRtbObject) { + const mediaTypeMode = getAdapterMode(bid); + + if (openRtbObject && bid) { + const impObject = { + id: bid.bidId, + secure: isSecure(bid), + bidfloor: getFloorModuleData(bid).floor || deepAccess(bid, 'params.bidOverride.imp.bidfloor') + }; + + if (bid.mediaTypes.banner && (typeof mediaTypeMode === 'undefined' || mediaTypeMode === BANNER || mediaTypeMode === '*')) { + impObject.banner = { + mimes: bid.mediaTypes.banner.mimes || ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: transformSizes(bid.sizes) + }; + if (bid.mediaTypes.banner.pos) { + impObject.banner.pos = bid.mediaTypes.banner.pos; + }; + }; + + if (bid.mediaTypes.video && (mediaTypeMode === VIDEO || mediaTypeMode === '*')) { + const playerSize = transformSizes(bid.mediaTypes.video.playerSize); + impObject.video = { + mimes: deepAccess(bid, 'params.bidOverride.imp.video.mimes') || bid.mediaTypes.video.mimes || ['video/mp4', 'application/javascript'], + w: deepAccess(bid, 'params.bidOverride.imp.video.w') || playerSize[0].w, + h: deepAccess(bid, 'params.bidOverride.imp.video.h') || playerSize[0].h, + maxbitrate: deepAccess(bid, 'params.bidOverride.imp.video.maxbitrate') || bid.mediaTypes.video.maxbitrate || undefined, + maxduration: deepAccess(bid, 'params.bidOverride.imp.video.maxduration') || bid.mediaTypes.video.maxduration || undefined, + minduration: deepAccess(bid, 'params.bidOverride.imp.video.minduration') || bid.mediaTypes.video.minduration || undefined, + api: deepAccess(bid, 'params.bidOverride.imp.video.api') || bid.mediaTypes.video.api || [2], + delivery: deepAccess(bid, 'params.bidOverride.imp.video.delivery') || bid.mediaTypes.video.delivery || undefined, + pos: deepAccess(bid, 'params.bidOverride.imp.video.pos') || bid.mediaTypes.video.pos || undefined, + playbackmethod: deepAccess(bid, 'params.bidOverride.imp.video.playbackmethod') || bid.mediaTypes.video.playbackmethod || undefined, + placement: deepAccess(bid, 'params.bidOverride.imp.video.placement') || bid.mediaTypes.video.placement || undefined, + linearity: deepAccess(bid, 'params.bidOverride.imp.video.linearity') || bid.mediaTypes.video.linearity || 1, + protocols: deepAccess(bid, 'params.bidOverride.imp.video.protocols') || bid.mediaTypes.video.protocols || [2, 5], + startdelay: deepAccess(bid, 'params.bidOverride.imp.video.startdelay') || bid.mediaTypes.video.startdelay || 0, + rewarded: deepAccess(bid, 'params.bidOverride.imp.video.rewarded') || undefined, + } + } + + impObject.ext = { + dfp_ad_unit_code: bid.adUnitCode + }; + + if (deepAccess(bid, 'params.kvp') && isPlainObject(bid.params.kvp)) { + impObject.ext.kvs = {}; + for (const key in bid.params.kvp) { + if (isStr(bid.params.kvp[key]) || isNumber(bid.params.kvp[key])) { + impObject.ext.kvs[key] = bid.params.kvp[key]; + } else if (isArray(bid.params.kvp[key])) { + const array = bid.params.kvp[key]; + if (array.every(value => isStr(value)) || array.every(value => isNumber(value))) { + impObject.ext.kvs[key] = bid.params.kvp[key]; + } + } + } + }; + + if (deepAccess(bid, 'ortb2Imp.ext.data') && isPlainObject(bid.ortb2Imp.ext.data)) { + impObject.ext.data = bid.ortb2Imp.ext.data; + }; + + if (deepAccess(bid, 'ortb2Imp.instl') && isNumber(bid.ortb2Imp.instl) && (bid.ortb2Imp.instl === 1)) { + impObject.instl = bid.ortb2Imp.instl; + }; + + if (getPubIdMode(bid) === false) { + impObject.tagid = bid.params.pos; + impObject.ext.pos = bid.params.pos; + } else if (deepAccess(bid, 'params.placementId')) { + impObject.tagid = bid.params.placementId + }; + + openRtbObject.imp.push(impObject); + }; +}; + +function appendFirstPartyData(outBoundBidRequest, bid) { + const ortb2Object = bid.ortb2; + const siteObject = deepAccess(ortb2Object, 'site') || undefined; + const siteContentObject = deepAccess(siteObject, 'content') || undefined; + const sitePublisherObject = deepAccess(siteObject, 'publisher') || undefined; + const siteContentDataArray = deepAccess(siteObject, 'content.data') || undefined; + const appContentObject = deepAccess(ortb2Object, 'app.content') || undefined; + const appContentDataArray = deepAccess(ortb2Object, 'app.content.data') || undefined; + const userObject = deepAccess(ortb2Object, 'user') || undefined; + + if (siteObject && isPlainObject(siteObject)) { + const allowedSiteStringKeys = ['name', 'domain', 'page', 'ref', 'keywords', 'search']; + const allowedSiteArrayKeys = ['cat', 'sectioncat', 'pagecat']; + const allowedSiteObjectKeys = ['ext']; + outBoundBidRequest.site = validateAppendObject('string', allowedSiteStringKeys, siteObject, outBoundBidRequest.site); + outBoundBidRequest.site = validateAppendObject('array', allowedSiteArrayKeys, siteObject, outBoundBidRequest.site); + outBoundBidRequest.site = validateAppendObject('object', allowedSiteObjectKeys, siteObject, outBoundBidRequest.site); + }; + + if (sitePublisherObject && isPlainObject(sitePublisherObject)) { + const allowedPublisherObjectKeys = ['ext']; + outBoundBidRequest.site.publisher = validateAppendObject('object', allowedPublisherObjectKeys, sitePublisherObject, outBoundBidRequest.site.publisher); + } + + if (siteContentObject && isPlainObject(siteContentObject)) { + const allowedContentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language']; + const allowedContentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; + const allowedContentArrayKeys = ['cat']; + const allowedContentObjectKeys = ['ext']; + outBoundBidRequest.site.content = validateAppendObject('string', allowedContentStringKeys, siteContentObject, outBoundBidRequest.site.content); + outBoundBidRequest.site.content = validateAppendObject('number', allowedContentNumberkeys, siteContentObject, outBoundBidRequest.site.content); + outBoundBidRequest.site.content = validateAppendObject('array', allowedContentArrayKeys, siteContentObject, outBoundBidRequest.site.content); + outBoundBidRequest.site.content = validateAppendObject('object', allowedContentObjectKeys, siteContentObject, outBoundBidRequest.site.content); + + if (siteContentDataArray && isArray(siteContentDataArray)) { + siteContentDataArray.every(dataObject => { + let newDataObject = {}; + const allowedContentDataStringKeys = ['id', 'name']; + const allowedContentDataArrayKeys = ['segment']; + const allowedContentDataObjectKeys = ['ext']; + newDataObject = validateAppendObject('string', allowedContentDataStringKeys, dataObject, newDataObject); + newDataObject = validateAppendObject('array', allowedContentDataArrayKeys, dataObject, newDataObject); + newDataObject = validateAppendObject('object', allowedContentDataObjectKeys, dataObject, newDataObject); + outBoundBidRequest.site.content.data = []; + outBoundBidRequest.site.content.data.push(newDataObject); + }); + }; + }; + + if (appContentObject && isPlainObject(appContentObject)) { + if (appContentDataArray && isArray(appContentDataArray)) { + appContentDataArray.every(dataObject => { + let newDataObject = {}; + const allowedContentDataStringKeys = ['id', 'name']; + const allowedContentDataArrayKeys = ['segment']; + const allowedContentDataObjectKeys = ['ext']; + newDataObject = validateAppendObject('string', allowedContentDataStringKeys, dataObject, newDataObject); + newDataObject = validateAppendObject('array', allowedContentDataArrayKeys, dataObject, newDataObject); + newDataObject = validateAppendObject('object', allowedContentDataObjectKeys, dataObject, newDataObject); + outBoundBidRequest.app = { + content: { + data: [] + } + }; + outBoundBidRequest.app.content.data.push(newDataObject); + }); + }; + }; + + if (userObject && isPlainObject(userObject)) { + const allowedUserStrings = ['id', 'buyeruid', 'gender', 'keywords', 'customdata']; + const allowedUserNumbers = ['yob']; + const allowedUserArrays = ['data']; + const allowedUserObjects = ['ext']; + outBoundBidRequest.user = validateAppendObject('string', allowedUserStrings, userObject, outBoundBidRequest.user); + outBoundBidRequest.user = validateAppendObject('number', allowedUserNumbers, userObject, outBoundBidRequest.user); + outBoundBidRequest.user = validateAppendObject('array', allowedUserArrays, userObject, outBoundBidRequest.user); + outBoundBidRequest.user.ext = validateAppendObject('object', allowedUserObjects, userObject, outBoundBidRequest.user.ext); + }; + + return outBoundBidRequest; +}; + +function generateServerRequest({payload, requestOptions, bidderRequest}) { + const pubIdMode = getPubIdMode(bidderRequest); + const overrideEndpoint = getConfigValue(bidderRequest, 'endpoint'); + let sspEndpoint = overrideEndpoint || SSP_ENDPOINT_DCN_POS; + + if (pubIdMode === true) { + sspEndpoint = overrideEndpoint || SSP_ENDPOINT_PUBID; + }; + + if (deepAccess(bidderRequest, 'params.testing.e2etest') === true) { + logInfo('Adapter e2etest mode is active'); + requestOptions.withCredentials = false; + + if (pubIdMode === true) { + payload.site.id = TEST_MODE_PUBID_DCN; + } else { + const mediaTypeMode = getAdapterMode(bidderRequest); + payload.site.id = TEST_MODE_DCN; + payload.imp.forEach(impObject => { + impObject.ext.e2eTestMode = true; + if (mediaTypeMode === BANNER) { + impObject.tagid = TEST_MODE_BANNER_POS; // banner passback + } else if (mediaTypeMode === VIDEO) { + impObject.tagid = TEST_MODE_VIDEO_POS; // video passback + } else { + const bidderCode = bidderRequest.bidderCode; + logWarn(`e2etest mode does not support ${bidderCode}.mode="all". \n Please specify either "banner" or "video"`); + logWarn(`Adapter e2etest mode: Please make sure your adUnit matches the ${bidderCode}.mode video or banner`); + } + }); + } + }; + + return { + url: sspEndpoint, + method: 'POST', + data: payload, + options: requestOptions, + bidderRequest // Additional data for use in interpretResponse() + }; +}; + +function createRenderer(bidderRequest, bidResponse) { + const renderer = Renderer.install({ + url: 'https://s.yimg.com/kp/prebid-outstream-renderer/renderer.js', + loaded: false, + adUnitCode: bidderRequest.adUnitCode + }) + + try { + renderer.setRender(function(bidResponse) { + setTimeout(function() { + // eslint-disable-next-line no-undef + o2PlayerRender(bidResponse); + }, deepAccess(bidderRequest, 'params.testing.renderer.setTimeout') || DEFAULT_RENDERER_TIMEOUT); + }); + } catch (error) { + logWarn('Renderer error: setRender() failed', error); + } + return renderer; +} + +/* Utility functions */ + +export const spec = { + code: BIDDER_CODE, + gvlid: GVLID, + aliases: BIDDER_ALIASES, + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid: function(bid) { + const params = bid.params; + if (deepAccess(params, 'testing.e2etest') === true) { + return true; + } else if ( + isPlainObject(params) && + (isNotEmptyStr(params.pubId) || (isNotEmptyStr(params.dcn) && isNotEmptyStr(params.pos))) + ) { + return true; + } else { + logWarn('Bidder params missing or incorrect, please pass object with either: dcn & pos OR pubId'); + return false; + } + }, + + buildRequests: function(validBidRequests, bidderRequest) { + if (isEmpty(validBidRequests) || isEmpty(bidderRequest)) { + logWarn('buildRequests called with either empty "validBidRequests" or "bidderRequest"'); + return undefined; + }; + + const requestOptions = { + contentType: 'application/json', + customHeaders: { + 'x-openrtb-version': '2.5' + } + }; + + requestOptions.withCredentials = hasPurpose1Consent(bidderRequest.gdprConsent); + + const filteredBidRequests = filterBidRequestByMode(validBidRequests); + + if (getConfigValue(bidderRequest, 'singleRequestMode') === true) { + const payload = generateOpenRtbObject(bidderRequest, filteredBidRequests[0]); + filteredBidRequests.forEach(bid => { + appendImpObject(bid, payload); + }); + return [generateServerRequest({payload, requestOptions, bidderRequest})]; + } + + return filteredBidRequests.map(bid => { + const payloadClone = generateOpenRtbObject(bidderRequest, bid); + appendImpObject(bid, payloadClone); + return generateServerRequest({payload: payloadClone, requestOptions, bidderRequest: bid}); + }); + }, + + interpretResponse: function(serverResponse, { bidderRequest }) { + const response = []; + if (!serverResponse.body || !Array.isArray(serverResponse.body.seatbid)) { + return response; + } + let seatbids = serverResponse.body.seatbid; + seatbids.forEach(seatbid => { + let bid; + + try { + bid = seatbid.bid[0]; + } catch (e) { + return response; + } + + let cpm = (bid.ext && bid.ext.encp) ? bid.ext.encp : bid.price; + + let bidResponse = { + adId: deepAccess(bid, 'adId') ? bid.adId : bid.impid || bid.crid, + requestId: bid.impid, + cpm: cpm, + width: bid.w, + height: bid.h, + creativeId: bid.crid || 0, + currency: bid.cur || DEFAULT_CURRENCY, + dealId: bid.dealid ? bid.dealid : null, + netRevenue: true, + ttl: getTtl(bidderRequest), + meta: { + advertiserDomains: bid.adomain, + } + }; + + const responseAdmFormat = getResponseFormat(bid); + if (responseAdmFormat === BANNER) { + bidResponse.mediaType = BANNER; + bidResponse.ad = bid.adm; + bidResponse.meta.mediaType = BANNER; + } else if (responseAdmFormat === VIDEO) { + bidResponse.mediaType = VIDEO; + bidResponse.meta.mediaType = VIDEO; + bidResponse.vastXml = bid.adm; + + if (bid.nurl) { + bidResponse.vastUrl = bid.nurl; + }; + } + + if (deepAccess(bidderRequest, 'mediaTypes.video.context') === 'outstream' && !bidderRequest.renderer) { + bidResponse.renderer = createRenderer(bidderRequest, bidResponse) || undefined; + } + + response.push(bidResponse); + }); + + return response; + }, + + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { + const bidResponse = !isEmpty(serverResponses) && serverResponses[0].body; + + if (bidResponse && bidResponse.ext && bidResponse.ext.pixels) { + const userSyncObjects = extractUserSyncUrls(syncOptions, bidResponse.ext.pixels); + userSyncObjects.forEach(userSyncObject => { + userSyncObject.url = updateConsentQueryParams(userSyncObject.url, { + gpp: gppConsent, + gdpr: gdprConsent, + uspConsent: uspConsent + }); + }); + return userSyncObjects; + } + + return []; + } +}; + +registerBidder(spec); diff --git a/modules/yahooAdvertisingBidAdapter.md b/modules/yahooAdvertisingBidAdapter.md new file mode 100644 index 00000000000..68e8791d99e --- /dev/null +++ b/modules/yahooAdvertisingBidAdapter.md @@ -0,0 +1,833 @@ +# Overview +**Module Name:** Yahoo Advertising Bid Adapter +**Module Type:** Bidder Adapter +**Maintainer:** hb-fe-tech@yahooinc.com + +# Description +The Yahoo Advertising Bid Adapter is an OpenRTB interface that consolidates all previous "Oath.inc" adapters such as: "aol", "oneMobile", "oneDisplay" & "oneVideo" supply-side platforms. + +# Supported Features: +* Media Types: Banner & Video +* Outstream renderer +* Multi-format adUnits +* Schain module +* Price floors module +* Advertiser domains +* End-2-End self-served testing mode +* Outstream renderer/Player +* User ID Modules - ConnectId and others +* First Party Data (ortb2 & ortb2Imp) +* Custom TTL (time to live) + + +# Adapter Request mode +Since the `yahooAdvertising` bid adapter supports both Banner and Video adUnits, a controller was needed to allow you to define when the adapter should generate a bid-requests to the Yahoo bid endpoint. + +**Important!** By default the adapter mode is set to "banner" only. +This means that you do not need to explicitly declare the `yahooAdvertising.mode` property in the global config to initiate banner adUnit requests. + +## Request modes: +* **undefined** - (Default) Will generate bid-requests for "Banner" formats only. +* **banner** - Will generate bid-requests for "Banner" formats only (Explicit declaration). +* **video** - Will generate bid-requests for "Video" formats only (Explicit declaration). +* **all** - Will generate bid-requests for both "Banner" & "Video" formats. + +**Important!** When setting `yahooAdvertising.mode` to `'all'`, make sure your Yahoo Placement (pos id) supports both Banner & Video placements. +If it does not, the Yahoo bid server will respond only in the format it is set too. + +### Example: explicitly setting the request mode +```javascript +pbjs.setConfig({ + yahooAdvertising: { + mode: 'banner' // 'all', 'video', 'banner' (default) + } +}); +``` + +# Integration Options +The `yahooAdvertising` bid adapter supports 2 types of integration: +1. **dcn & pos** DEFAULT (Site/App & Position targeting) - For Display partners/publishers. +2. **pubId** (Publisher ID) - For legacy "oneVideo" AND new partners/publishers. +**Important:** pubId integration (option 2) is only possible when your seller account is setup for "Inventory Mapping". + +**Please Note:** Most examples in this file are using dcn & pos. + +## Who is currently eligible for "pubId" integration +At this time, only the following partners/publishers are eligble for pubId integration: +1. New partners/publishers that do not have an existing account with Yahoo Advertising (aka: aol, oneMobile, oneDisplay). +2. Video SSP (oneVideo) partners/publishers that + A. Do not have any display/banner inventory. + B. Do not have an existing account with Yahoo Advertising (aka: aol, oneMobile, oneDisplay). + +# Mandatory Bidder Parameters +## dcn & pos (DEFAULT) +The minimal requirements for the `yahooAdvertising` bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: +1. At least 1 adUnit including mediaTypes: banner or video +2. **bidder.params** object must include: + A. **dcn:** Yahoo Advertising Site/App inventory parameter. + B. **pos:** Yahoo Advertising position inventory parameter. + +### Example: dcn & pos Mandatory Parameters (Single banner adUnit) +```javascript +const adUnits = [{ + code: 'your-placement', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising + pos: '8a969978017a7aaabab4ab0bc01a0009' // Placement ID provided by Yahoo Advertising + } + } + ] +}]; +``` + +## pubId +The minimal requirements for the `yahooAdvertising` bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: +1. At least 1 adUnit including mediaTypes: banner or video +2. **bidder.params** object must include: + A. **pubId:** Yahoo Advertising Publisher ID (AKA oneVideo pubId/Exchange name) + +### Example: pubId Mandatory Parameters (Single banner adUnit) +```javascript +const adUnits = [{ + code: 'your-placement', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'yahooAdvertising', + params: { + pubId: 'DemoPublisher', // Publisher defined external ID as configured by Yahoo Advertising. + } + } + ] +}]; +``` + +# Advanced adUnit Examples: +## Banner +```javascript +const adUnits = [{ + code: 'banner-adUnit', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising + pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided by Yahoo Advertising + } + } + }] +}]; +``` + +## Video Instream +**Important!** Make sure that the Yahoo Advertising Placement type (in-stream) matches the adUnit video inventory type. +**Note:** Make sure to set the adapter mode to allow video requests by setting it to mode: 'video' OR mode: 'all'. +```javascript +pbjs.setConfig({ + yahooAdvertising: { + mode: 'video' + } +}); + +const adUnits = [{ + code: 'video-adUnit', + mediaTypes: { + video: { + context: 'instream', + playerSize: [ + [300, 250] + ], + mimes: ['video/mp4','application/javascript'], + api: [2] + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising + pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided by Yahoo Advertising + } + }] +}]; + +``` +## Video Outstream +**Important!** Make sure that the Yahoo Advertsing placement type (in-feed/ in-article) matches the adUnit video inventory type. +**Note:** Make sure to set the adapter mode to allow video requests by setting it to mode: 'video' OR mode: 'all' +```javascript +pbjs.setConfig({ + yahooAdvertising: { + mode: 'video' + } +}); + +const adUnits = [{ + code: 'video-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + mimes: ['video/mp4','application/javascript'], + api: [2] + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising + pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided by Yahoo Advertising + } + }] +}]; + +``` +## Multi-Format +**Important!** If you intend to use the `yahooAdvertising` bidder for both Banner and Video formats please make sure: +1. Set the adapter as mode: 'all' - to configure the bid adapter to call the bid endpoint for both banner & video formats. +2. Make sure the Yahoo Advertising placement (pos id) supports both banner & video format requests. +```javascript +pbjs.setConfig({ + yahooAdvertising: { + mode: 'all' + } +}); + +const adUnits = [{ + code: 'video-adUnit', + mediaTypes: { + banner: { + sizes: [[300, 250]] + }, + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + mimes: ['video/mp4','application/javascript'], + api: [2] + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising + pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided by Yahoo Advertising + } + }] +}]; +``` + +# Optional: Schain module support +The `yahooAdvertising` bid adapter supports the Prebid.org Schain module and will pass it through to our bid endpoint. +For further details please see, https://docs.prebid.org/dev-docs/modules/schain.html +## Global Schain Example: +```javascript + pbjs.setConfig({ + "schain": { + "validation": "off", + "config": { + "ver": "1.0", + "complete": 1, + "nodes": [{ + "asi": "some-platform.com", + "sid": "111111", + "hp": 1 + }] + } + } + }); +``` +## Bidder Specific Schain Example: +```javascript + pbjs.setBidderConfig({ + "bidders": ['yahooAdvertising'], // can list more bidders here if they share the same config + "config": { + "schain": { + "validation": "strict", + "config": { + "ver": "1.0", + "complete": 1, + "nodes": [{ + "asi": "other-platform.com", + "sid": "222222", + "hp": 0 + }] + } + } + } + }); +``` + +# Optional: Price floors module & bidfloor +The `yahooAdvertising` bid adapter supports the Prebid.org Price Floors module and will use it to define the outbound bidfloor and currency, if the relevant floors have been defined in the configuration. +A cusom method for defining bid floors is also supported, this can be enabled by setting the `params.bidOverride.imp.bidfloor` bidder parameter. + +**Note:** All override params apply to all requests generated using this configuration regardless of format type. +```javascript +const adUnits = [{ + code: 'override-pricefloor', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising + pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided by Yahoo Advertising + bidOverride :{ + imp: { + bidfloor: 5.00 // bidOverride priceFloor + } + } + } + } + }] +}]; +``` + +For further details please see, https://docs.prebid.org/dev-docs/modules/floors.html + +# Optional: Self-served E2E testing mode +If you want to see how the `yahooAdvertising` bid adapter works and loads you are invited to try it out using our testing mode. +This is useful for integration testing and response parsing when checking banner vs video capabilities. + +## How to use E2E test mode: +1. Set the `yahooAdvertising` global config mode to either `'banner'` or `'video'` - depending on the adUnit you want to test. +2. Add params.testing.e2etest: true to your adUnit bidder config - See examples below. + +**Note:** When using E2E Test Mode you do not need to pass mandatory bidder params dcn or pos. + +**Important!** E2E Testing Mode only works when the Bidder Request Mode is set explicitly to either `'banner'` or `'video'`. + +## Activating E2E Test for "Banner" + ```javascript +pbjs.setConfig({ + yahooAdvertising: { + mode: 'banner' // select 'banner' or 'video' to define what response to load + } +}); + +const adUnits = [{ + code: 'your-placement', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [ + { + bidder: 'yahooAdvertising', + params: { + testing: { + e2etest: true // Activate E2E Test mode + } + } + } + ] +}]; + +``` +## Activating E2E Test for "Video" +**Note:** We recommend using Video Outstream as it would load the video response using our Outstream Renderer feature + ```javascript +pbjs.setConfig({ + yahooAdvertising: { + mode: 'video' + } +}); + +const adUnits = [{ + code: 'video-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + mimes: ['video/mp4','application/javascript'], + api: [2] + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + testing: { + e2etest: true // Activate E2E Test mode + } + } + }] +}]; +``` + +# Optional: First Party Data +The `yahooAdvertising` bid adapter supports first party data passed via: +1. Global ortb2 object using `pbjs.setConfig()` +2. adUnit ortb2Imp object declared within an adUnit. +For further details please see, https://docs.prebid.org/features/firstPartyData.html +## Global First Party Data "ortb2" +### Passing First Party "site" data: +```javascript +pbjs.setConfig({ + ortb2: { + site: { + name: 'Yahoo Advertising', + domain: 'yahooadvertising.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'], + page: 'https://page.yahooadvertising.com.com/here.html', + ref: 'https://ref.yahooadvertising.com.com/there.html', + keywords:'yahoo, ad, tech', + search: 'header bidding', + content: { + id: '1234', + title: 'Title', + series: 'Series', + season: 'Season', + episode: 1, + cat: ['IAB1', 'IAB1-1', 'IAB1-2', 'IAB2', 'IAB2-1'], + genre: 'Fantase', + contentrating: 'C-Rating', + language: 'EN', + prodq: 1, + context: 1, + len: 200, + data: [{ + name: "www.dataprovider1.com", + ext: { segtax: 4 }, + segment: [ + { id: "687" }, + { id: "123" } + ] + }], + ext: { + network: 'ext-network', + channel: 'ext-channel', + data: { + pageType: "article", + category: "repair" + } + } + } + } + } + }); +``` + +Notes: The first party site info is filtered and only the following specific keys are allowed in the bidRequests: + +| Field | Type | +|-----------------------------|--------| +| site.name | String | +| site.domain | String | +| site.page | String | +| site.ref | String | +| site.keywords | String | +| site.search | String | +| site.cat | Array | +| site.sectioncat | Array | +| site.pagecat | Array | +| site.ext | Object | +| site.publisher.ext | Object | +| site.content.id | String | +| site.content.title | String | +| site.content.series | String | +| site.content.season | String | +| site.content.genre | String | +| site.content.contentrating | String | +| site.content.language | String | +| site.content.episode | Number | +| site.content.prodq | Number | +| site.content.context | Number | +| site.content.livestream | Number | +| site.content.len | Number | +| site.content.cat | Array | +| site.content.ext | Object | +| site.content.data | Array | +| site.content.data[].id | String | +| site.content.data[].name | String | +| site.content.data[].segment | Array | +| site.content.data[].ext | Object | + + +### Passing First Party "user" data: +```javascript +pbjs.setConfig({ + ortb2: { + user: { + yob: 1985, + gender: 'm', + keywords: 'a,b', + data: [{ + name: "www.dataprovider1.com", + ext: { segtax: 4 }, + segment: [ + { id: "687" }, + { id: "123" } + ] + }], + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + } + }); +``` +### Passing First Party "app.content.data" data: +```javascript +pbjs.setConfig({ + ortb2: { + app: { + content: { + data: [{ + name: "www.dataprovider1.com", + ext: { segtax: 4 }, + segment: [ + { id: "687" }, + { id: "123" } + ] + }], + } + } + } + }); +``` + + +## AdUnit First Party Data "ortb2Imp" +Most DSPs are adopting the Global Placement ID (GPID). +Please pass your placement specific GPID value to Yahoo SSP using `adUnit.ortb2Imp.ext.data.pbadslot`. +```javascript +const adUnits = [{ + code: 'placement', + mediaTypes: { + banner: { + sizes: [ + [300, 250] + ] + }, + }, + ortb2Imp: { + ext: { + data: { + pbadslot: "homepage-top-rect", + adUnitSpecificAttribute: "123" + } + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + pubdId: 'DemoPublisher' + } + }] + } + ] +``` + +# Optional: Bidder bidOverride Parameters +The `yahooAdvertising` bid adapter allows passing override data to the outbound bid-request that overrides First Party Data. +**Important!** We highly recommend using prebid modules to pass data instead of bidder speicifc overrides. +The use of these parameters are a last resort to force a specific feature or use case in your implementation. + +Currently the bidOverride object only accepts the following: +* imp + * video + * mimes + * w + * h + * maxbitrate + * maxduration + * minduration + * api + * delivery + * pos + * playbackmethod + * placement + * linearity + * protocols + * rewarded +* site + * page +* device + * ip + +```javascript +const adUnits = [{ + code: 'bidOverride-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', + pos: '8a96958a017a7a57ac375d50c0c700cc', + bidOverride: { + imp: { + video: { + mimes: ['video/mp4', 'javascript/application'], + w: 300, + h: 200, + maxbitrate: 4000, + maxduration: 30, + minduration: 10, + api: [1,2], + delivery: 1, + pos: 1, + playbackmethod: 0, + placement: 1, + linearity: 1, + protocols: [2,5], + startdelay: 0, + rewarded: 0 + } + }, + site: { + page: 'https://yahooAdvertising-bid-adapter.com', + }, + device: { + ip: "1.2.3.4" + } + } + } + }] +}] +``` + +# Optional: Custom Key-Value Pairs +Custom key-value paris can be used for both inventory targeting and reporting. +You must set up key-value pairs in the Yahoo SSP before sending them via the adapter otherwise the Ad Server will not be listening and picking them up. + +Important! Key-value pairs can only contain values of the following data types: String, Number, Array of strings OR Array of numbers + +```javascript +const adUnits = [{ + code: 'key-value-pairs', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', + pos: '8a96958a017a7a57ac375d50c0c700cc', + kvp: { + key1: 'value', // String + key2: 123456, // Number + key3: ['string1','string2', 'string3'], // Array of strings + key4: [1, 23, 456, 7890] // Array of Numbers + } + } + }] +}] +``` + +# Optional: Custom Cache Time To Live (ttl): +The `yahooAdvertising` bid adapter supports passing of "Time To Live" (ttl) to indicate to prebid how long the bid response from Yahoo Advertising should be retained by Prebid for. This configuration value must be a Number in seconds, with the valid range being 1 - 3600 inclusive. +The setting can be defined globally using `setConfig` or within the adUnit.params. +Global level `setConfig` overrides adUnit.params. +If no value is being passed default is 300 seconds. +## Global TTL +```javascript +pbjs.setConfig({ + yahooAdvertising: { + ttl: 300 + } +}); + +const adUnits = [{ + code: 'global-ttl', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', + pos: '8a96958a017a7a57ac375d50c0c700cc', + } + }] +}] +``` + +## Ad Unit Params TTL +```javascript +const adUnits = [{ + code: 'adUnit-ttl', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', + pos: '8a96958a017a7a57ac375d50c0c700cc', + ttl: 300, + } + }] +}] + +``` +# Optional: Video Features +## Rewarded video flag +To indicate to Yahoo Advertising that this adUnit is a rewarded video you can set the `params.bidOverride.imp.video.rewarded` property to `1` + +```javascript +const adUnits = [{ + code: 'rewarded-video-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + dcn: '8a969516017a7a396ec539d97f540011', + pos: '8a96958a017a7a57ac375d50c0c700cc', + bidOverride: { + imp: { + video: { + rewarded: 1 + } + } + } + } + }] +}] +``` + +## Site/App Targeting for "pubId" Inventory Mapping +To target your adUnit explicitly to a specific Site/App Object in Yahoo Advertising, you can pass one of the following: +1. params.siteId = External Site ID || Video SSP RTBIS Id (in String format). +2. params.bidOverride.site.id = External Site ID || Video SSP RTBIS Id (in String format). +**Important:** Site override is a only supported when using "pubId" mode. +**Important:** If you are switching from the oneVideo adapter, please make sure to pass inventoryid as a String instead of Integer. + +```javascript +const adUnits = [{ + code: 'pubId-site-targeting-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + pubId: 'DemoPublisher', + siteId: '1234567'; + } + }] +}] +``` + +## Placement Targeting for "pubId" Inventory Mapping +To target your adUnit explicitly to a specific Placement within a Site/App Object in Yahoo Advertising, you can pass the following params.placementId = External Placement ID || Placement Alias + +**Important!** Placement override is a only supported when using "pubId" mode. +**Important!** It is highly recommended that you pass both `siteId` AND `placementId` together to avoid inventory mismatching. + +### Site & Placement override +**Important!** If the placement ID does not reside under the defined Site/App object, the request will not resolve and no response will be sent back from the bid-server. +```javascript +const adUnits = [{ + code: 'pubId-site-targeting-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + pubId: 'DemoPublisher', + siteId: '1234567', + placementId: 'header-250x300' + } + }] +}] +``` +### Placement only override +**Important!** Using this method is not advised if you have multiple Site/Apps that are broken out of a Run Of Network (RON) Site/App. If the placement ID does not reside under a matching Site/App object, the request will not resolve and no response will be sent back from the bid-server. +```javascript +const adUnits = [{ + code: 'pubId-site-targeting-adUnit', + mediaTypes: { + video: { + context: 'outstream', + playerSize: [ + [300, 250] + ], + } + }, + bids: [{ + bidder: 'yahooAdvertising', + params: { + pubId: 'DemoPublisher', + placementId: 'header-250x300' + } + }] +}] +``` + +# Optional: Legacy override Parameters +This adapter does not support passing legacy overrides via `bidder.params.ext` since most of the data should be passed using prebid modules (First Party Data, Schain, Price Floors etc.). +If you do not know how to pass a custom parameter that you previously used, please contact us using the information provided above. + +Thank you, +Yahoo Advertsing diff --git a/test/spec/modules/yahoosspBidAdapter_spec.js b/test/spec/modules/yahooAdvertisingAdapter_spec.js similarity index 76% rename from test/spec/modules/yahoosspBidAdapter_spec.js rename to test/spec/modules/yahooAdvertisingAdapter_spec.js index b0030c89371..4ba8829cbe5 100644 --- a/test/spec/modules/yahoosspBidAdapter_spec.js +++ b/test/spec/modules/yahooAdvertisingAdapter_spec.js @@ -1,9 +1,8 @@ import { expect } from 'chai'; import { config } from 'src/config.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; -import { spec } from 'modules/yahoosspBidAdapter.js'; +import { spec } from 'modules/yahooAdvertisingBidAdapter.js'; import {createEidsArray} from '../../../modules/userId/eids'; -import {deepClone} from '../../../src/utils'; const DEFAULT_BID_ID = '84ab500420319d'; const DEFAULT_BID_DCN = '2093845709823475'; @@ -13,18 +12,19 @@ const DEFAULT_AD_UNIT_CODE = '/19968336/header-bid-tag-1'; const DEFAULT_AD_UNIT_TYPE = 'banner'; const DEFAULT_PARAMS_BID_OVERRIDE = {}; const DEFAULT_VIDEO_CONTEXT = 'instream'; -const ADAPTER_VERSION = '1.0.2'; +const DEFAULT_BIDDER_CODE = 'yahooAdvertising'; +const VALID_BIDDER_CODES = [DEFAULT_BIDDER_CODE, 'yahoossp']; const PREBID_VERSION = '$prebid.version$'; const INTEGRATION_METHOD = 'prebid.js'; // Utility functions -const generateBidRequest = ({bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode, ortb2}) => { +const generateBidRequest = ({bidderCode, bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode, ortb2}) => { const bidRequest = { adUnitCode, auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', bidId, bidderRequestsCount: 1, - bidder: 'yahoossp', + bidder: bidderCode, bidderRequestId: '7101db09af0db2', bidderWinsCount: 0, mediaTypes: {}, @@ -79,7 +79,7 @@ let generateBidderRequest = (bidRequestArray, adUnitCode, ortb2 = {}) => { adUnitCode: adUnitCode || 'default-adUnitCode', auctionId: 'd4c83a3b-18e4-4208-b98b-63848449c7aa', auctionStart: new Date().getTime(), - bidderCode: 'yahoossp', + bidderCode: bidRequestArray[0].bidder, bidderRequestId: '112f1c7c5d399a', bids: bidRequestArray, refererInfo: { @@ -107,8 +107,9 @@ let generateBidderRequest = (bidRequestArray, adUnitCode, ortb2 = {}) => { return bidderRequest; }; -const generateBuildRequestMock = ({bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode, ortb2}) => { +const generateBuildRequestMock = ({bidderCode, bidId, pos, adUnitCode, adUnitType, bidOverrideObject, videoContext, pubIdMode, ortb2}) => { const bidRequestConfig = { + bidderCode: bidderCode || DEFAULT_BIDDER_CODE, bidId: bidId || DEFAULT_BID_ID, pos: pos || DEFAULT_BID_POS, adUnitCode: adUnitCode || DEFAULT_AD_UNIT_CODE, @@ -174,21 +175,28 @@ const generateResponseMock = (admPayloadType, vastVersion, videoContext) => { seatbid: [{ bid: [ bidResponse ], seat: 13107 }] } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: admPayloadType, videoContext: videoContext}); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: admPayloadType, videoContext}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - return {serverResponse, data, bidderRequest}; } // Unit tests -describe('YahooSSP Bid Adapter:', () => { +describe('Yahoo Advertising Bid Adapter:', () => { + beforeEach(() => { + config.resetConfig(); + }); + describe('Validate basic properties', () => { it('should define the correct bidder code', () => { - expect(spec.code).to.equal('yahoossp') + expect(spec.code).to.equal('yahooAdvertising'); + }); + + it('should define the correct bidder aliases', () => { + expect(spec.aliases).to.deep.equal(['yahoossp']); }); it('should define the correct vendor ID', () => { - expect(spec.gvlid).to.equal(25) + expect(spec.gvlid).to.equal(25); }); }); @@ -647,9 +655,9 @@ describe('YahooSSP Bid Adapter:', () => { }; const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.user[param]).to.be.a('object'); - expect(data.user[param]).to.be.deep.include({[param]: {a: '123', b: '456'}}); - config.setConfig({ortb2: {}}); + const user = data.user; + expect(user[param]).to.be.a('object'); + expect(user[param]).to.be.deep.include({[param]: {a: '123', b: '456'}}); }); }); @@ -669,12 +677,14 @@ describe('YahooSSP Bid Adapter:', () => { }; const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.user.data[0][param]).to.exist; - expect(data.user.data[0][param]).to.be.a('string'); - expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); - expect(data.site.content.data[0][param]).to.exist; - expect(data.site.content.data[0][param]).to.be.a('string'); - expect(data.site.content.data[0][param]).to.be.equal(ortb2.site.content.data[0][param]); + const user = data.user; + const site = data.site; + expect(user.data[0][param]).to.exist; + expect(user.data[0][param]).to.be.a('string'); + expect(user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); + expect(site.content.data[0][param]).to.exist; + expect(site.content.data[0][param]).to.be.a('string'); + expect(site.content.data[0][param]).to.be.equal(ortb2.site.content.data[0][param]); }); }); @@ -688,9 +698,10 @@ describe('YahooSSP Bid Adapter:', () => { }; const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.user.data[0][param]).to.exist; - expect(data.user.data[0][param]).to.be.a('array'); - expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); + const user = data.user; + expect(user.data[0][param]).to.exist; + expect(user.data[0][param]).to.be.a('array'); + expect(user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); }); }); @@ -704,10 +715,10 @@ describe('YahooSSP Bid Adapter:', () => { }; const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.user.data[0][param]).to.exist; - expect(data.user.data[0][param]).to.be.a('object'); - expect(data.user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); - config.setConfig({ortb2: {}}); + const user = data.user; + expect(user.data[0][param]).to.exist; + expect(user.data[0][param]).to.be.a('object'); + expect(user.data[0][param]).to.be.equal(ortb2.user.data[0][param]); }); }); @@ -815,26 +826,33 @@ describe('YahooSSP Bid Adapter:', () => { }); describe('Endpoint & Impression Request Mode:', () => { - it('should route request to config override endpoint', () => { - const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const testOverrideEndpoint = 'http://foo.bar.baz.com/bidderRequest'; + afterEach(() => { config.setConfig({ - yahoossp: { - endpoint: testOverrideEndpoint + yahooAdvertising: { + singleRequestMode: undefined } }); - const response = spec.buildRequests(validBidRequests, bidderRequest)[0]; - expect(response).to.deep.include( - { - method: 'POST', - url: testOverrideEndpoint - }); }); - it('should route request to /bidRequest endpoint when dcn & pos present', () => { - config.setConfig({ - yahoossp: {} + VALID_BIDDER_CODES.forEach(bidderCode => { + it(`should route request to config override endpoint for ${bidderCode} override config`, () => { + const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode}); + const testOverrideEndpoint = 'http://foo.bar.baz.com/bidderRequest'; + const cfg = {}; + cfg[bidderCode] = { + endpoint: testOverrideEndpoint + }; + config.setConfig(cfg); + const response = spec.buildRequests(validBidRequests, bidderRequest)[0]; + expect(response).to.deep.include( + { + method: 'POST', + url: testOverrideEndpoint + }); }); + }); + + it('should route request to /bidRequest endpoint when dcn & pos present', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); const response = spec.buildRequests(validBidRequests, bidderRequest); expect(response[0]).to.deep.include({ @@ -862,15 +880,15 @@ describe('YahooSSP Bid Adapter:', () => { bidderRequest.bids = validBidRequests; config.setConfig({ - yahoossp: { + yahooAdvertising: { singleRequestMode: true } }); - const data = spec.buildRequests(validBidRequests, bidderRequest).data; - expect(data.imp).to.be.an('array').with.lengthOf(2); + const responsePayload = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(responsePayload.imp).to.be.an('array').with.lengthOf(2); - expect(data.imp[0]).to.deep.include({ + expect(responsePayload.imp[0]).to.deep.include({ id: DEFAULT_BID_ID, ext: { pos: DEFAULT_BID_POS, @@ -878,7 +896,7 @@ describe('YahooSSP Bid Adapter:', () => { } }); - expect(data.imp[1]).to.deep.include({ + expect(responsePayload.imp[1]).to.deep.include({ id: BID_ID_2, ext: { pos: BID_POS_2, @@ -896,8 +914,9 @@ describe('YahooSSP Bid Adapter:', () => { it('buildRequests(): should return an array with the correct amount of request objects', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const response = spec.buildRequests(validBidRequests, bidderRequest).bidderRequest; - expect(response.bids).to.be.an('array').to.have.lengthOf(1); + const reqs = spec.buildRequests(validBidRequests, bidderRequest); + expect(reqs).to.be.an('array').to.have.lengthOf(1); + expect(reqs[0]).to.be.an('object').that.has.keys('method', 'url', 'data', 'options', 'bidderRequest'); }); }); @@ -905,7 +924,7 @@ describe('YahooSSP Bid Adapter:', () => { it('should return request objects with the relevant custom headers and content type declaration', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); bidderRequest.gdprConsent.gdprApplies = false; - const options = spec.buildRequests(validBidRequests, bidderRequest).options; + const options = spec.buildRequests(validBidRequests, bidderRequest)[0].options; expect(options).to.deep.equal( { contentType: 'application/json', @@ -921,6 +940,7 @@ describe('YahooSSP Bid Adapter:', () => { it('should set the allowed sources user eids', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); validBidRequests[0].userIdAsEids = createEidsArray({ + connectId: 'connectId_FROM_USER_ID_MODULE', admixerId: 'admixerId_FROM_USER_ID_MODULE', adtelligentId: 'adtelligentId_FROM_USER_ID_MODULE', amxId: 'amxId_FROM_USER_ID_MODULE', @@ -933,9 +953,10 @@ describe('YahooSSP Bid Adapter:', () => { criteoId: 'criteoId_FROM_USER_ID_MODULE', fabrickId: 'fabrickId_FROM_USER_ID_MODULE', }); - const data = spec.buildRequests(validBidRequests, bidderRequest).data; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.user.ext.eids).to.deep.equal([ + {source: 'yahoo.com', uids: [{id: 'connectId_FROM_USER_ID_MODULE', atype: 3}]}, {source: 'admixer.net', uids: [{id: 'admixerId_FROM_USER_ID_MODULE', atype: 3}]}, {source: 'adtelligent.com', uids: [{id: 'adtelligentId_FROM_USER_ID_MODULE', atype: 3}]}, {source: 'amxdt.net', uids: [{id: 'amxId_FROM_USER_ID_MODULE', atype: 1}]}, @@ -955,7 +976,7 @@ describe('YahooSSP Bid Adapter:', () => { validBidRequests[0].userIdAsEids = createEidsArray({ justId: 'justId_FROM_USER_ID_MODULE' }); - const data = spec.buildRequests(validBidRequests, bidderRequest).data; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.user.ext.eids).to.deep.equal([]); }); @@ -964,7 +985,7 @@ describe('YahooSSP Bid Adapter:', () => { describe('Request Payload oRTB bid validation:', () => { it('should generate a valid openRTB bid-request object in the data field', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const data = spec.buildRequests(validBidRequests, bidderRequest).data; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.site).to.deep.equal({ id: bidderRequest.bids[0].params.dcn, page: bidderRequest.refererInfo.page @@ -990,7 +1011,6 @@ describe('YahooSSP Bid Adapter:', () => { expect(data.source).to.deep.equal({ ext: { hb: 1, - adapterver: ADAPTER_VERSION, prebidver: PREBID_VERSION, integration: { name: INTEGRATION_METHOD, @@ -1013,7 +1033,7 @@ describe('YahooSSP Bid Adapter:', () => { it('should generate a valid openRTB imp.ext object in the bid-request', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); const bid = validBidRequests[0]; - const data = spec.buildRequests(validBidRequests, bidderRequest).data; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.imp[0].ext).to.deep.equal({ pos: bid.params.pos, dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE @@ -1023,7 +1043,7 @@ describe('YahooSSP Bid Adapter:', () => { it('should use siteId value as site.id in the outbound bid-request when using "pubId" integration mode', () => { let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true}); validBidRequests[0].params.siteId = '1234567'; - const data = spec.buildRequests(validBidRequests, bidderRequest).data; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.site.id).to.equal('1234567'); }); @@ -1039,7 +1059,7 @@ describe('YahooSSP Bid Adapter:', () => { } } let { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest).data; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.site.publisher).to.deep.equal({ ext: { publisherblob: 'pblob', @@ -1060,7 +1080,7 @@ describe('YahooSSP Bid Adapter:', () => { } } let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true, ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest).data; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.site.publisher).to.deep.equal({ id: DEFAULT_PUBID, ext: { @@ -1073,17 +1093,13 @@ describe('YahooSSP Bid Adapter:', () => { it('should use placementId value as imp.tagid in the outbound bid-request when using "pubId" integration mode', () => { let { validBidRequests, bidderRequest } = generateBuildRequestMock({pubIdMode: true}); validBidRequests[0].params.placementId = 'header-300x250'; - const data = spec.buildRequests(validBidRequests, bidderRequest).data; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.imp[0].tagid).to.deep.equal('header-300x250'); }); }); describe('Request Payload oRTB bid.imp validation:', () => { - // Validate Banner imp imp when yahoossp.mode=undefined - it('should generate a valid "Banner" imp object', () => { - config.setConfig({ - yahoossp: {} - }); + it('should generate a valid "Banner" imp object when mode config override is undefined', () => { const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; expect(data.imp[0].video).to.not.exist; @@ -1093,74 +1109,82 @@ describe('YahooSSP Bid Adapter:', () => { }); }); - // Validate Banner imp when yahoossp.mode="banner" - it('should generate a valid "Banner" imp object', () => { - config.setConfig({ - yahoossp: { mode: 'banner' } - }); - const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].video).to.not.exist; - expect(data.imp[0].banner).to.deep.equal({ - mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], - format: [{w: 300, h: 250}, {w: 300, h: 600}] + // Validate Banner imp when config value for mode="banner" + VALID_BIDDER_CODES.forEach(bidderCode => { + it(`should generate a valid "Banner" imp object for ${bidderCode} config override`, () => { + const cfg = {}; + cfg[bidderCode] = { + mode: BANNER + }; + config.setConfig(cfg); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].video).to.not.exist; + expect(data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}, {w: 300, h: 600}] + }); }); - }); - // Validate Video imp - it('should generate a valid "Video" only imp object', () => { - config.setConfig({ - yahoossp: { mode: 'video' } - }); - const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].banner).to.not.exist; - expect(data.imp[0].video).to.deep.equal({ - mimes: ['video/mp4', 'application/javascript'], - w: 300, - h: 250, - api: [2], - protocols: [2, 5], - startdelay: 0, - linearity: 1, - maxbitrate: undefined, - maxduration: undefined, - minduration: undefined, - delivery: undefined, - pos: undefined, - playbackmethod: undefined, - rewarded: undefined, - placement: undefined + // Validate Video imp + it(`should generate a valid "Video" only imp object for ${bidderCode} config override`, () => { + const cfg = {}; + cfg[bidderCode] = { + mode: VIDEO + }; + config.setConfig(cfg); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode, adUnitType: 'video'}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].banner).to.not.exist; + expect(data.imp[0].video).to.deep.equal({ + mimes: ['video/mp4', 'application/javascript'], + w: 300, + h: 250, + api: [2], + protocols: [2, 5], + startdelay: 0, + linearity: 1, + maxbitrate: undefined, + maxduration: undefined, + minduration: undefined, + delivery: undefined, + pos: undefined, + playbackmethod: undefined, + rewarded: undefined, + placement: undefined + }); }); - }); - // Validate multi-format Video+banner imp - it('should generate a valid multi-format "Video + Banner" imp object', () => { - config.setConfig({ - yahoossp: { mode: 'all' } - }); - const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'multi-format'}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].banner).to.deep.equal({ - mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], - format: [{w: 300, h: 250}, {w: 300, h: 600}] - }); - expect(data.imp[0].video).to.deep.equal({ - mimes: ['video/mp4', 'application/javascript'], - w: 300, - h: 250, - api: [2], - protocols: [2, 5], - startdelay: 0, - linearity: 1, - maxbitrate: undefined, - maxduration: undefined, - minduration: undefined, - delivery: undefined, - pos: undefined, - playbackmethod: undefined, - rewarded: undefined, - placement: undefined + // Validate multi-format Video+banner imp + it(`should generate a valid multi-format "Video + Banner" imp object for ${bidderCode} config override`, () => { + const cfg = {}; + cfg[bidderCode] = { + mode: 'all' + }; + config.setConfig(cfg); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode, adUnitType: 'multi-format'}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].banner).to.deep.equal({ + mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: [{w: 300, h: 250}, {w: 300, h: 600}] + }); + expect(data.imp[0].video).to.deep.equal({ + mimes: ['video/mp4', 'application/javascript'], + w: 300, + h: 250, + api: [2], + protocols: [2, 5], + startdelay: 0, + linearity: 1, + maxbitrate: undefined, + maxduration: undefined, + minduration: undefined, + delivery: undefined, + pos: undefined, + playbackmethod: undefined, + rewarded: undefined, + placement: undefined + }); }); }); @@ -1179,7 +1203,6 @@ describe('YahooSSP Bid Adapter:', () => { invalidKey5: undefined }; const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].ext.kvs).to.deep.equal({ key1: 'String', key2: 123456, @@ -1192,10 +1215,6 @@ describe('YahooSSP Bid Adapter:', () => { describe('Multiple adUnit validations:', () => { // Multiple banner adUnits it('should generate multiple bid-requests for each adUnit - 2 banner only', () => { - config.setConfig({ - yahoossp: { mode: 'banner' } - }); - const BID_ID_2 = '84ab50xxxxx'; const BID_POS_2 = 'footer'; const AD_UNIT_CODE_2 = 'test-ad-unit-code-123'; @@ -1209,12 +1228,12 @@ describe('YahooSSP Bid Adapter:', () => { validBidRequests = [bidRequest, bidRequest2, bidRequest3]; bidderRequest.bids = validBidRequests; - const response = spec.buildRequests(validBidRequests, bidderRequest) - expect(response).to.be.a('array'); - expect(response.length).to.equal(2); - response.forEach((obj) => { - expect(obj.data.imp[0].video).to.not.exist - expect(obj.data.imp[0].banner).to.deep.equal({ + const reqs = spec.buildRequests(validBidRequests, bidderRequest) + expect(reqs).to.be.a('array'); + expect(reqs.length).to.equal(2); + reqs.forEach(req => { + expect(req.data.imp[0].video).to.not.exist + expect(req.data.imp[0].banner).to.deep.equal({ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], format: [{w: 300, h: 250}, {w: 300, h: 600}] }); @@ -1223,9 +1242,11 @@ describe('YahooSSP Bid Adapter:', () => { // Multiple video adUnits it('should generate multiple bid-requests for each adUnit - 2 video only', () => { - config.setConfig({ - yahoossp: { mode: 'video' } - }); + const cfg = {}; + cfg[DEFAULT_BIDDER_CODE] = { + mode: VIDEO + }; + config.setConfig(cfg); const BID_ID_2 = '84ab50xxxxx'; const BID_POS_2 = 'footer'; const AD_UNIT_CODE_2 = 'test-ad-unit-code-123'; @@ -1233,18 +1254,18 @@ describe('YahooSSP Bid Adapter:', () => { const BID_POS_3 = 'hero'; const AD_UNIT_CODE_3 = 'video-ad-unit'; - let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'}); // video - const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2, adUnitType: 'video'}); // video - const { bidRequest: bidRequest3 } = generateBuildRequestMock({bidId: BID_ID_3, pos: BID_POS_3, adUnitCode: AD_UNIT_CODE_3}); // banner (should be filtered) + let {bidRequest, validBidRequests, bidderRequest} = generateBuildRequestMock({adUnitType: 'video'}); // video + const {bidRequest: bidRequest2} = generateBuildRequestMock({bidId: BID_ID_2, pos: BID_POS_2, adUnitCode: AD_UNIT_CODE_2, adUnitType: 'video'}); // video + const {bidRequest: bidRequest3} = generateBuildRequestMock({bidId: BID_ID_3, pos: BID_POS_3, adUnitCode: AD_UNIT_CODE_3}); // banner (should be filtered) validBidRequests = [bidRequest, bidRequest2, bidRequest3]; bidderRequest.bids = validBidRequests; - const response = spec.buildRequests(validBidRequests, bidderRequest) - expect(response).to.be.a('array'); - expect(response.length).to.equal(2); - response.forEach((obj) => { - expect(obj.data.imp[0].banner).to.not.exist - expect(obj.data.imp[0].video).to.deep.equal({ + const reqs = spec.buildRequests(validBidRequests, bidderRequest) + expect(reqs).to.be.a('array'); + expect(reqs.length).to.equal(2); + reqs.forEach(req => { + expect(req.data.imp[0].banner).to.not.exist + expect(req.data.imp[0].video).to.deep.equal({ mimes: ['video/mp4', 'application/javascript'], w: 300, h: 250, @@ -1265,9 +1286,9 @@ describe('YahooSSP Bid Adapter:', () => { }); // Mixed adUnits 1-banner, 1-video, 1-native (should filter out native) it('should generate multiple bid-requests for both "video & banner" adUnits', () => { - config.setConfig({ - yahoossp: { mode: 'all' } - }); + const cfg = {}; + cfg[DEFAULT_BIDDER_CODE] = { mode: 'all' }; + config.setConfig(cfg); const BID_ID_2 = '84ab50xxxxx'; const BID_POS_2 = 'footer'; const AD_UNIT_CODE_2 = 'video-ad-unit'; @@ -1281,21 +1302,21 @@ describe('YahooSSP Bid Adapter:', () => { validBidRequests = [bidRequest, bidRequest2, bidRequest3]; bidderRequest.bids = validBidRequests; - const response = spec.buildRequests(validBidRequests, bidderRequest); - expect(response).to.be.a('array'); - expect(response.length).to.equal(2); - response.forEach((obj) => { - expect(obj.data.imp[0].native).to.not.exist; + const reqs = spec.buildRequests(validBidRequests, bidderRequest); + expect(reqs).to.be.a('array'); + expect(reqs.length).to.equal(2); + reqs.forEach(req => { + expect(req.data.imp[0].native).to.not.exist; }); - const data1 = response[0].data; + const data1 = reqs[0].data; expect(data1.imp[0].video).to.not.exist; expect(data1.imp[0].banner).to.deep.equal({ mimes: ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], format: [{w: 300, h: 250}, {w: 300, h: 600}] }); - const data2 = response[1].data; + const data2 = reqs[1].data; expect(data2.imp[0].banner).to.not.exist; expect(data2.imp[0].video).to.deep.equal({ mimes: ['video/mp4', 'application/javascript'], @@ -1318,90 +1339,98 @@ describe('YahooSSP Bid Adapter:', () => { }); describe('Video params firstlook & bidOverride validations:', () => { - it('should first look at params.bidOverride for video placement data', () => { - config.setConfig({ - yahoossp: { mode: 'video' } - }); - const bidOverride = { - imp: { - video: { - mimes: ['video/mp4'], - w: 400, - h: 350, - api: [1], - protocols: [1, 3], - startdelay: 0, - linearity: 1, - maxbitrate: 400000, - maxduration: 3600, - minduration: 1500, - delivery: 1, - pos: 123456, - playbackmethod: 1, - rewarded: 1, - placement: 1 + VALID_BIDDER_CODES.forEach(bidderCode => { + it(`should first look at params.bidOverride for video placement data for ${bidderCode} config override`, () => { + const cfg = {}; + cfg[bidderCode] = { + mode: VIDEO + }; + config.setConfig(cfg); + const bidOverride = { + imp: { + video: { + mimes: ['video/mp4'], + w: 400, + h: 350, + api: [1], + protocols: [1, 3], + startdelay: 0, + linearity: 1, + maxbitrate: 400000, + maxduration: 3600, + minduration: 1500, + delivery: 1, + pos: 123456, + playbackmethod: 1, + rewarded: 1, + placement: 1 + } } } - } - const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video', bidOverrideObject: bidOverride}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].video).to.deep.equal(bidOverride.imp.video); - }); - - it('should second look at bid.mediaTypes.video for video placement data', () => { - config.setConfig({ - yahoossp: { mode: 'video' } - }); - let { bidRequest, bidderRequest } = generateBuildRequestMock({adUnitType: 'video'}); - bidRequest.mediaTypes.video = { - mimes: ['video/mp4'], - playerSize: [400, 350], - api: [1], - protocols: [1, 3], - startdelay: 0, - linearity: 1, - maxbitrate: 400000, - maxduration: 3600, - minduration: 1500, - delivery: 1, - pos: 123456, - playbackmethod: 1, - placement: 1 - } - const validBidRequests = [bidRequest]; - bidderRequest.bids = validBidRequests; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].video).to.deep.equal({ - mimes: ['video/mp4'], - w: 400, - h: 350, - api: [1], - protocols: [1, 3], - startdelay: 0, - linearity: 1, - maxbitrate: 400000, - maxduration: 3600, - minduration: 1500, - delivery: 1, - pos: 123456, - playbackmethod: 1, - placement: 1, - rewarded: undefined + const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode, adUnitType: 'video', bidOverrideObject: bidOverride}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].video).to.deep.equal(bidOverride.imp.video); }); - }); - it('should use params.bidOverride.device.ip override', () => { - config.setConfig({ - yahoossp: { mode: 'all' } + it(`should second look at bid.mediaTypes.video for video placement data for ${bidderCode} config override`, () => { + const cfg = {}; + cfg[bidderCode] = { + mode: VIDEO + }; + config.setConfig(cfg); + let { bidRequest, bidderRequest } = generateBuildRequestMock({bidderCode, adUnitType: 'video'}); + bidRequest.mediaTypes.video = { + mimes: ['video/mp4'], + playerSize: [400, 350], + api: [1], + protocols: [1, 3], + startdelay: 0, + linearity: 1, + maxbitrate: 400000, + maxduration: 3600, + minduration: 1500, + delivery: 1, + pos: 123456, + playbackmethod: 1, + placement: 1 + } + const validBidRequests = [bidRequest]; + bidderRequest.bids = validBidRequests; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.imp[0].video).to.deep.equal({ + mimes: ['video/mp4'], + w: 400, + h: 350, + api: [1], + protocols: [1, 3], + startdelay: 0, + linearity: 1, + maxbitrate: 400000, + maxduration: 3600, + minduration: 1500, + delivery: 1, + pos: 123456, + playbackmethod: 1, + placement: 1, + rewarded: undefined + }); }); - const bidOverride = { - device: { - ip: '1.2.3.4' + + it(`should use params.bidOverride.device.ip override for ${bidderCode} config override`, () => { + const cfg = {}; + cfg[bidderCode] = { + mode: 'all' + }; + config.setConfig(cfg); + const bidOverride = { + device: { + ip: '1.2.3.4' + } } - } - const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: 'video', bidOverrideObject: bidOverride}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.device.ip).to.deep.equal(bidOverride.device.ip); + const { validBidRequests, bidderRequest } = generateBuildRequestMock({bidderCode, adUnitType: 'video', bidOverrideObject: bidOverride}); + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + expect(data.device.ip).to.deep.equal(bidOverride.device.ip); + }); }); }); // #endregion buildRequests(): @@ -1417,6 +1446,22 @@ describe('YahooSSP Bid Adapter:', () => { }); describe('for mediaTypes: "video"', () => { + beforeEach(() => { + config.setConfig({ + yahooAdvertising: { + mode: VIDEO + } + }); + }); + + afterEach(() => { + config.setConfig({ + yahooAdvertising: { + mode: undefined + } + }); + }); + it('should insert video VPAID payload into vastXml', () => { const { serverResponse, bidderRequest } = generateResponseMock('video'); const response = spec.interpretResponse(serverResponse, {bidderRequest}); @@ -1434,28 +1479,38 @@ describe('YahooSSP Bid Adapter:', () => { expect(response[0].mediaType).to.equal('video'); }) - it('should insert video DAP O2 Player into ad', () => { - const { serverResponse, bidderRequest } = generateResponseMock('dap-o2', 'vpaid'); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ad).to.equal(''); - expect(response[0].vastUrl).to.be.undefined; - expect(response[0].vastXml).to.be.undefined; - expect(response[0].mediaType).to.equal('banner'); - }); + describe('wrapped in video players for display inventory', () => { + beforeEach(() => { + config.setConfig({ + yahooAdvertising: { + mode: undefined + } + }); + }); - it('should insert video DAP Unified Player into ad', () => { - const { serverResponse, bidderRequest } = generateResponseMock('dap-up', 'vpaid'); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ad).to.equal(''); - expect(response[0].vastUrl).to.be.undefined; - expect(response[0].vastXml).to.be.undefined; - expect(response[0].mediaType).to.equal('banner'); - }) + it('should insert video DAP O2 Player into ad', () => { + const { serverResponse, bidderRequest } = generateResponseMock('dap-o2', 'vpaid'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.equal(''); + expect(response[0].vastUrl).to.be.undefined; + expect(response[0].vastXml).to.be.undefined; + expect(response[0].mediaType).to.equal('banner'); + }); + + it('should insert video DAP Unified Player into ad', () => { + const { serverResponse, bidderRequest } = generateResponseMock('dap-up', 'vpaid'); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ad).to.equal(''); + expect(response[0].vastUrl).to.be.undefined; + expect(response[0].vastXml).to.be.undefined; + expect(response[0].mediaType).to.equal('banner'); + }) + }); }); describe('Support Advertiser domains', () => { it('should append bid-response adomain to meta.advertiserDomains', () => { - const { serverResponse, bidderRequest } = generateResponseMock('video', 'vpaid'); + const { serverResponse, bidderRequest } = generateResponseMock('banner'); const response = spec.interpretResponse(serverResponse, {bidderRequest}); expect(response[0].meta.advertiserDomains).to.be.a('array'); expect(response[0].meta.advertiserDomains[0]).to.equal('advertiser-domain.com'); @@ -1490,53 +1545,55 @@ describe('YahooSSP Bid Adapter:', () => { }); describe('Time To Live (ttl)', () => { - const UNSUPPORTED_TTL_FORMATS = ['string', [1, 2, 3], true, false, null, undefined]; - UNSUPPORTED_TTL_FORMATS.forEach(param => { - it('should not allow unsupported global yahoossp.ttl formats and default to 300', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - config.setConfig({ - yahoossp: { ttl: param } + VALID_BIDDER_CODES.forEach(bidderCode => { + const UNSUPPORTED_TTL_FORMATS = ['string', [1, 2, 3], true, false, null, undefined]; + UNSUPPORTED_TTL_FORMATS.forEach(param => { + it(`should not allow unsupported global ${bidderCode}.ttl formats and default to 300`, () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const cfg = {}; + cfg['yahooAdvertising'] = { ttl: param }; + config.setConfig(cfg); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); }); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ttl).to.equal(300); - }); - it('should not allow unsupported params.ttl formats and default to 300', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - bidderRequest.bids[0].params.ttl = param; - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ttl).to.equal(300); + it('should not allow unsupported params.ttl formats and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + bidderRequest.bids[0].params.ttl = param; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); }); - }); - const UNSUPPORTED_TTL_VALUES = [-1, 3601]; - UNSUPPORTED_TTL_VALUES.forEach(param => { - it('should not allow invalid global yahoossp.ttl values 3600 < ttl < 0 and default to 300', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - config.setConfig({ - yahoossp: { ttl: param } + const UNSUPPORTED_TTL_VALUES = [-1, 3601]; + UNSUPPORTED_TTL_VALUES.forEach(param => { + it('should not allow invalid global config ttl values 3600 < ttl < 0 and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + config.setConfig({ + yahooAdvertising: { ttl: param } + }); + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); + }); + + it('should not allow invalid params.ttl values 3600 < ttl < 0 and default to 300', () => { + const { serverResponse, bidderRequest } = generateResponseMock('banner'); + bidderRequest.bids[0].params.ttl = param; + const response = spec.interpretResponse(serverResponse, {bidderRequest}); + expect(response[0].ttl).to.equal(300); }); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ttl).to.equal(300); }); - it('should not allow invalid params.ttl values 3600 < ttl < 0 and default to 300', () => { + it('should give presedence to Gloabl ttl over params.ttl ', () => { const { serverResponse, bidderRequest } = generateResponseMock('banner'); - bidderRequest.bids[0].params.ttl = param; + config.setConfig({ + yahooAdvertising: { ttl: 500 } + }); + bidderRequest.bids[0].params.ttl = 400; const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ttl).to.equal(300); + expect(response[0].ttl).to.equal(500); }); }); - - it('should give presedence to Gloabl ttl over params.ttl ', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - config.setConfig({ - yahoossp: { ttl: 500 } - }); - bidderRequest.bids[0].params.ttl = 400; - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ttl).to.equal(500); - }); }); describe('Aliasing support', () => { From 98ac9ebbaac9be8ac742e6bf17c482a3dd0921d3 Mon Sep 17 00:00:00 2001 From: slimkrazy Date: Tue, 20 Jun 2023 20:41:59 +0100 Subject: [PATCH 2/6] Add deleted modules --- modules/yahoosspBidAdapter.js | 690 ---------------------------- modules/yahoosspBidAdapter.md | 832 ---------------------------------- 2 files changed, 1522 deletions(-) delete mode 100644 modules/yahoosspBidAdapter.js delete mode 100644 modules/yahoosspBidAdapter.md diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js deleted file mode 100644 index 7a414c0d2ee..00000000000 --- a/modules/yahoosspBidAdapter.js +++ /dev/null @@ -1,690 +0,0 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { deepAccess, isFn, isStr, isNumber, isArray, isEmpty, isPlainObject, generateUUID, logInfo, logWarn } from '../src/utils.js'; -import { config } from '../src/config.js'; -import { Renderer } from '../src/Renderer.js'; -import {hasPurpose1Consent} from '../src/utils/gpdr.js'; - -const INTEGRATION_METHOD = 'prebid.js'; -const BIDDER_CODE = 'yahoossp'; -const GVLID = 25; -const ADAPTER_VERSION = '1.0.2'; -const PREBID_VERSION = '$prebid.version$'; -const DEFAULT_BID_TTL = 300; -const TEST_MODE_DCN = '8a969516017a7a396ec539d97f540011'; -const TEST_MODE_PUBID_DCN = '1234567'; -const TEST_MODE_BANNER_POS = '8a969978017a7aaabab4ab0bc01a0009'; -const TEST_MODE_VIDEO_POS = '8a96958a017a7a57ac375d50c0c700cc'; -const DEFAULT_RENDERER_TIMEOUT = 700; -const DEFAULT_CURRENCY = 'USD'; -const SSP_ENDPOINT_DCN_POS = 'https://c2shb.pubgw.yahoo.com/bidRequest'; -const SSP_ENDPOINT_PUBID = 'https://c2shb.pubgw.yahoo.com/admax/bid/partners/PBJS'; -const SUPPORTED_USER_ID_SOURCES = [ - 'admixer.net', - 'adserver.org', - 'adtelligent.com', - 'akamai.com', - 'amxdt.net', - 'audigent.com', - 'britepool.com', - 'criteo.com', - 'crwdcntrl.net', - 'deepintent.com', - 'epsilon.com', - 'hcn.health', - 'id5-sync.com', - 'idx.lat', - 'intentiq.com', - 'intimatemerger.com', - 'liveintent.com', - 'liveramp.com', - 'mediawallahscript.com', - 'merkleinc.com', - 'netid.de', - 'neustar.biz', - 'nextroll.com', - 'novatiq.com', - 'parrable.com', - 'pubcid.org', - 'quantcast.com', - 'tapad.com', - 'uidapi.com', - 'verizonmedia.com', - 'yahoo.com', - 'zeotap.com' -]; - -/* Utility functions */ - -function getSize(size) { - return { - w: parseInt(size[0]), - h: parseInt(size[1]) - } -} - -function transformSizes(sizes) { - if (isArray(sizes) && sizes.length === 2 && !isArray(sizes[0])) { - return [ getSize(sizes) ]; - } - return sizes.map(getSize); -} - -function extractUserSyncUrls(syncOptions, pixels) { - let itemsRegExp = /(img|iframe)[\s\S]*?src\s*=\s*("|')(.*?)\2/gi; - let tagNameRegExp = /\w*(?=\s)/; - let srcRegExp = /src=("|')(.*?)\1/; - let userSyncObjects = []; - - if (pixels) { - let matchedItems = pixels.match(itemsRegExp); - if (matchedItems) { - matchedItems.forEach(item => { - let tagName = item.match(tagNameRegExp)[0]; - let url = item.match(srcRegExp)[2]; - - if (tagName && url) { - let tagType = tagName.toLowerCase() === 'img' ? 'image' : 'iframe'; - if ((!syncOptions.iframeEnabled && tagType === 'iframe') || - (!syncOptions.pixelEnabled && tagType === 'image')) { - return; - } - userSyncObjects.push({ - type: tagType, - url: url - }); - } - }); - } - } - - return userSyncObjects; -} - -/** - * @param {string} url - * @param {object} consentData - * @param {object} consentData.gpp - * @param {string} consentData.gpp.gppConsent - * @param {array} consentData.gpp.applicableSections - * @param {object} consentData.gdpr - * @param {object} consentData.gdpr.consentString - * @param {object} consentData.gdpr.gdprApplies - * @param {string} consentData.uspConsent - */ -function updateConsentQueryParams(url, consentData) { - const parameterMap = { - 'gdpr_consent': consentData.gdpr.consentString, - 'gdpr': consentData.gdpr.gdprApplies ? '1' : '0', - 'us_privacy': consentData.uspConsent, - 'gpp': consentData.gpp.gppString, - 'gpp_sid': consentData.gpp.applicableSections ? consentData.gpp.applicableSections.join(',') : '' - } - - const existingUrl = new URL(url); - const params = existingUrl.searchParams; - - for (const [key, value] of Object.entries(parameterMap)) { - params.set(key, value); - } - - existingUrl.search = params.toString(); - return existingUrl.toString(); -}; - -function getSupportedEids(bid) { - if (isArray(deepAccess(bid, 'userIdAsEids'))) { - return bid.userIdAsEids.filter(eid => { - return SUPPORTED_USER_ID_SOURCES.indexOf(eid.source) !== -1; - }); - } - return []; -} - -function isSecure(bid) { - return deepAccess(bid, 'params.bidOverride.imp.secure') || (document.location.protocol === 'https:') ? 1 : 0; -}; - -function getPubIdMode(bid) { - let pubIdMode; - if (deepAccess(bid, 'params.pubId')) { - pubIdMode = true; - } else if (deepAccess(bid, 'params.dcn') && deepAccess(bid, 'params.pos')) { - pubIdMode = false; - }; - return pubIdMode; -}; - -function getAdapterMode() { - let adapterMode = config.getConfig('yahoossp.mode'); - adapterMode = adapterMode ? adapterMode.toLowerCase() : undefined; - if (typeof adapterMode === 'undefined' || adapterMode === BANNER) { - return BANNER; - } else if (adapterMode === VIDEO) { - return VIDEO; - } else if (adapterMode === 'all') { - return '*'; - } -}; - -function getResponseFormat(bid) { - const adm = bid.adm; - if (adm.indexOf('o2playerSettings') !== -1 || adm.indexOf('YAHOO.VideoPlatform.VideoPlayer') !== -1 || adm.indexOf('AdPlacement') !== -1) { - return BANNER; - } else if (adm.indexOf('VAST') !== -1) { - return VIDEO; - } -}; - -function getFloorModuleData(bid) { - const adapterMode = getAdapterMode(); - const getFloorRequestObject = { - currency: deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CURRENCY, - mediaType: adapterMode, - size: '*' - }; - return (isFn(bid.getFloor)) ? bid.getFloor(getFloorRequestObject) : false; -}; - -function filterBidRequestByMode(validBidRequests) { - const mediaTypesMode = getAdapterMode(); - let result = []; - if (mediaTypesMode === BANNER) { - result = validBidRequests.filter(bid => { - return Object.keys(bid.mediaTypes).some(item => item === BANNER); - }); - } else if (mediaTypesMode === VIDEO) { - result = validBidRequests.filter(bid => { - return Object.keys(bid.mediaTypes).some(item => item === VIDEO); - }); - } else if (mediaTypesMode === '*') { - result = validBidRequests.filter(bid => { - return Object.keys(bid.mediaTypes).some(item => item === BANNER || item === VIDEO); - }); - }; - return result; -}; - -function validateAppendObject(validationType, allowedKeys, inputObject, appendToObject) { - const outputObject = { - ...appendToObject - }; - - for (const objectKey in inputObject) { - switch (validationType) { - case 'string': - if (allowedKeys.indexOf(objectKey) !== -1 && isStr(inputObject[objectKey])) { - outputObject[objectKey] = inputObject[objectKey]; - }; - break; - case 'number': - if (allowedKeys.indexOf(objectKey) !== -1 && isNumber(inputObject[objectKey])) { - outputObject[objectKey] = inputObject[objectKey]; - }; - break; - - case 'array': - if (allowedKeys.indexOf(objectKey) !== -1 && isArray(inputObject[objectKey])) { - outputObject[objectKey] = inputObject[objectKey]; - }; - break; - case 'object': - if (allowedKeys.indexOf(objectKey) !== -1 && isPlainObject(inputObject[objectKey])) { - outputObject[objectKey] = inputObject[objectKey]; - }; - break; - case 'objectAllKeys': - if (isPlainObject(inputObject)) { - outputObject[objectKey] = inputObject[objectKey]; - }; - break; - }; - }; - return outputObject; -}; - -function getTtl(bidderRequest) { - const globalTTL = config.getConfig('yahoossp.ttl'); - return globalTTL ? validateTTL(globalTTL) : validateTTL(deepAccess(bidderRequest, 'params.ttl')); -}; - -function validateTTL(ttl) { - return (isNumber(ttl) && ttl > 0 && ttl < 3600) ? ttl : DEFAULT_BID_TTL -}; - -function isNotEmptyStr(value) { - return (isStr(value) && value.length > 0); -}; - -function generateOpenRtbObject(bidderRequest, bid) { - if (bidderRequest) { - let outBoundBidRequest = { - id: generateUUID(), - cur: [getFloorModuleData(bidderRequest).currency || deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CURRENCY], - imp: [], - site: { - page: deepAccess(bidderRequest, 'refererInfo.page'), - }, - device: { - dnt: 0, - ua: navigator.userAgent, - ip: deepAccess(bid, 'params.bidOverride.device.ip') || deepAccess(bid, 'params.ext.ip') || undefined, - w: window.screen.width, - h: window.screen.height - }, - regs: { - ext: { - 'us_privacy': bidderRequest.uspConsent ? bidderRequest.uspConsent : '', - gdpr: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies ? 1 : 0, - gpp: bidderRequest.gppConsent.gppString, - gpp_sid: bidderRequest.gppConsent.applicableSections - } - }, - source: { - ext: { - hb: 1, - adapterver: ADAPTER_VERSION, - prebidver: PREBID_VERSION, - integration: { - name: INTEGRATION_METHOD, - ver: PREBID_VERSION - } - }, - fd: 1 - }, - user: { - ext: { - consent: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies - ? bidderRequest.gdprConsent.consentString : '', - eids: getSupportedEids(bid) - } - } - }; - - if (getPubIdMode(bid) === true) { - outBoundBidRequest.site.publisher = { - id: bid.params.pubId - } - if (deepAccess(bid, 'params.bidOverride.site.id') || deepAccess(bid, 'params.siteId')) { - outBoundBidRequest.site.id = deepAccess(bid, 'params.bidOverride.site.id') || bid.params.siteId; - } - } else { - outBoundBidRequest.site.id = bid.params.dcn; - }; - - if (bidderRequest.ortb2?.regs?.gpp) { - outBoundBidRequest.regs.ext.gpp = bidderRequest.ortb2.regs.gpp; - outBoundBidRequest.regs.ext.gpp_sid = bidderRequest.ortb2.regs.gpp_sid - }; - - if (bidderRequest.ortb2) { - outBoundBidRequest = appendFirstPartyData(outBoundBidRequest, bid); - }; - - const schainData = deepAccess(bid, 'schain.nodes'); - if (isArray(schainData) && schainData.length > 0) { - outBoundBidRequest.source.ext.schain = bid.schain; - outBoundBidRequest.source.ext.schain.nodes[0].rid = outBoundBidRequest.id; - }; - - return outBoundBidRequest; - }; -}; - -function appendImpObject(bid, openRtbObject) { - const mediaTypeMode = getAdapterMode(); - - if (openRtbObject && bid) { - const impObject = { - id: bid.bidId, - secure: isSecure(bid), - bidfloor: getFloorModuleData(bid).floor || deepAccess(bid, 'params.bidOverride.imp.bidfloor') - }; - - if (bid.mediaTypes.banner && (typeof mediaTypeMode === 'undefined' || mediaTypeMode === BANNER || mediaTypeMode === '*')) { - impObject.banner = { - mimes: bid.mediaTypes.banner.mimes || ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], - format: transformSizes(bid.sizes) - }; - if (bid.mediaTypes.banner.pos) { - impObject.banner.pos = bid.mediaTypes.banner.pos; - }; - }; - - if (bid.mediaTypes.video && (mediaTypeMode === VIDEO || mediaTypeMode === '*')) { - const playerSize = transformSizes(bid.mediaTypes.video.playerSize); - impObject.video = { - mimes: deepAccess(bid, 'params.bidOverride.imp.video.mimes') || bid.mediaTypes.video.mimes || ['video/mp4', 'application/javascript'], - w: deepAccess(bid, 'params.bidOverride.imp.video.w') || playerSize[0].w, - h: deepAccess(bid, 'params.bidOverride.imp.video.h') || playerSize[0].h, - maxbitrate: deepAccess(bid, 'params.bidOverride.imp.video.maxbitrate') || bid.mediaTypes.video.maxbitrate || undefined, - maxduration: deepAccess(bid, 'params.bidOverride.imp.video.maxduration') || bid.mediaTypes.video.maxduration || undefined, - minduration: deepAccess(bid, 'params.bidOverride.imp.video.minduration') || bid.mediaTypes.video.minduration || undefined, - api: deepAccess(bid, 'params.bidOverride.imp.video.api') || bid.mediaTypes.video.api || [2], - delivery: deepAccess(bid, 'params.bidOverride.imp.video.delivery') || bid.mediaTypes.video.delivery || undefined, - pos: deepAccess(bid, 'params.bidOverride.imp.video.pos') || bid.mediaTypes.video.pos || undefined, - playbackmethod: deepAccess(bid, 'params.bidOverride.imp.video.playbackmethod') || bid.mediaTypes.video.playbackmethod || undefined, - placement: deepAccess(bid, 'params.bidOverride.imp.video.placement') || bid.mediaTypes.video.placement || undefined, - linearity: deepAccess(bid, 'params.bidOverride.imp.video.linearity') || bid.mediaTypes.video.linearity || 1, - protocols: deepAccess(bid, 'params.bidOverride.imp.video.protocols') || bid.mediaTypes.video.protocols || [2, 5], - startdelay: deepAccess(bid, 'params.bidOverride.imp.video.startdelay') || bid.mediaTypes.video.startdelay || 0, - rewarded: deepAccess(bid, 'params.bidOverride.imp.video.rewarded') || undefined, - } - } - - impObject.ext = { - dfp_ad_unit_code: bid.adUnitCode - }; - - if (deepAccess(bid, 'params.kvp') && isPlainObject(bid.params.kvp)) { - impObject.ext.kvs = {}; - for (const key in bid.params.kvp) { - if (isStr(bid.params.kvp[key]) || isNumber(bid.params.kvp[key])) { - impObject.ext.kvs[key] = bid.params.kvp[key]; - } else if (isArray(bid.params.kvp[key])) { - const array = bid.params.kvp[key]; - if (array.every(value => isStr(value)) || array.every(value => isNumber(value))) { - impObject.ext.kvs[key] = bid.params.kvp[key]; - } - } - } - }; - - if (deepAccess(bid, 'ortb2Imp.ext.data') && isPlainObject(bid.ortb2Imp.ext.data)) { - impObject.ext.data = bid.ortb2Imp.ext.data; - }; - - if (deepAccess(bid, 'ortb2Imp.instl') && isNumber(bid.ortb2Imp.instl) && (bid.ortb2Imp.instl === 1)) { - impObject.instl = bid.ortb2Imp.instl; - }; - - if (getPubIdMode(bid) === false) { - impObject.tagid = bid.params.pos; - impObject.ext.pos = bid.params.pos; - } else if (deepAccess(bid, 'params.placementId')) { - impObject.tagid = bid.params.placementId - }; - - openRtbObject.imp.push(impObject); - }; -}; - -function appendFirstPartyData(outBoundBidRequest, bid) { - const ortb2Object = bid.ortb2; - const siteObject = deepAccess(ortb2Object, 'site') || undefined; - const siteContentObject = deepAccess(siteObject, 'content') || undefined; - const sitePublisherObject = deepAccess(siteObject, 'publisher') || undefined; - const siteContentDataArray = deepAccess(siteObject, 'content.data') || undefined; - const appContentObject = deepAccess(ortb2Object, 'app.content') || undefined; - const appContentDataArray = deepAccess(ortb2Object, 'app.content.data') || undefined; - const userObject = deepAccess(ortb2Object, 'user') || undefined; - - if (siteObject && isPlainObject(siteObject)) { - const allowedSiteStringKeys = ['name', 'domain', 'page', 'ref', 'keywords', 'search']; - const allowedSiteArrayKeys = ['cat', 'sectioncat', 'pagecat']; - const allowedSiteObjectKeys = ['ext']; - outBoundBidRequest.site = validateAppendObject('string', allowedSiteStringKeys, siteObject, outBoundBidRequest.site); - outBoundBidRequest.site = validateAppendObject('array', allowedSiteArrayKeys, siteObject, outBoundBidRequest.site); - outBoundBidRequest.site = validateAppendObject('object', allowedSiteObjectKeys, siteObject, outBoundBidRequest.site); - }; - - if (sitePublisherObject && isPlainObject(sitePublisherObject)) { - const allowedPublisherObjectKeys = ['ext']; - outBoundBidRequest.site.publisher = validateAppendObject('object', allowedPublisherObjectKeys, sitePublisherObject, outBoundBidRequest.site.publisher); - } - - if (siteContentObject && isPlainObject(siteContentObject)) { - const allowedContentStringKeys = ['id', 'title', 'series', 'season', 'genre', 'contentrating', 'language']; - const allowedContentNumberkeys = ['episode', 'prodq', 'context', 'livestream', 'len']; - const allowedContentArrayKeys = ['cat']; - const allowedContentObjectKeys = ['ext']; - outBoundBidRequest.site.content = validateAppendObject('string', allowedContentStringKeys, siteContentObject, outBoundBidRequest.site.content); - outBoundBidRequest.site.content = validateAppendObject('number', allowedContentNumberkeys, siteContentObject, outBoundBidRequest.site.content); - outBoundBidRequest.site.content = validateAppendObject('array', allowedContentArrayKeys, siteContentObject, outBoundBidRequest.site.content); - outBoundBidRequest.site.content = validateAppendObject('object', allowedContentObjectKeys, siteContentObject, outBoundBidRequest.site.content); - - if (siteContentDataArray && isArray(siteContentDataArray)) { - siteContentDataArray.every(dataObject => { - let newDataObject = {}; - const allowedContentDataStringKeys = ['id', 'name']; - const allowedContentDataArrayKeys = ['segment']; - const allowedContentDataObjectKeys = ['ext']; - newDataObject = validateAppendObject('string', allowedContentDataStringKeys, dataObject, newDataObject); - newDataObject = validateAppendObject('array', allowedContentDataArrayKeys, dataObject, newDataObject); - newDataObject = validateAppendObject('object', allowedContentDataObjectKeys, dataObject, newDataObject); - outBoundBidRequest.site.content.data = []; - outBoundBidRequest.site.content.data.push(newDataObject); - }); - }; - }; - - if (appContentObject && isPlainObject(appContentObject)) { - if (appContentDataArray && isArray(appContentDataArray)) { - appContentDataArray.every(dataObject => { - let newDataObject = {}; - const allowedContentDataStringKeys = ['id', 'name']; - const allowedContentDataArrayKeys = ['segment']; - const allowedContentDataObjectKeys = ['ext']; - newDataObject = validateAppendObject('string', allowedContentDataStringKeys, dataObject, newDataObject); - newDataObject = validateAppendObject('array', allowedContentDataArrayKeys, dataObject, newDataObject); - newDataObject = validateAppendObject('object', allowedContentDataObjectKeys, dataObject, newDataObject); - outBoundBidRequest.app = { - content: { - data: [] - } - }; - outBoundBidRequest.app.content.data.push(newDataObject); - }); - }; - }; - - if (userObject && isPlainObject(userObject)) { - const allowedUserStrings = ['id', 'buyeruid', 'gender', 'keywords', 'customdata']; - const allowedUserNumbers = ['yob']; - const allowedUserArrays = ['data']; - const allowedUserObjects = ['ext']; - outBoundBidRequest.user = validateAppendObject('string', allowedUserStrings, userObject, outBoundBidRequest.user); - outBoundBidRequest.user = validateAppendObject('number', allowedUserNumbers, userObject, outBoundBidRequest.user); - outBoundBidRequest.user = validateAppendObject('array', allowedUserArrays, userObject, outBoundBidRequest.user); - outBoundBidRequest.user.ext = validateAppendObject('object', allowedUserObjects, userObject, outBoundBidRequest.user.ext); - }; - - return outBoundBidRequest; -}; - -function generateServerRequest({payload, requestOptions, bidderRequest}) { - const pubIdMode = getPubIdMode(bidderRequest); - let sspEndpoint = config.getConfig('yahoossp.endpoint') || SSP_ENDPOINT_DCN_POS; - - if (pubIdMode === true) { - sspEndpoint = config.getConfig('yahoossp.endpoint') || SSP_ENDPOINT_PUBID; - }; - - if (deepAccess(bidderRequest, 'params.testing.e2etest') === true) { - logInfo('yahoossp adapter e2etest mode is active'); - requestOptions.withCredentials = false; - - if (pubIdMode === true) { - payload.site.id = TEST_MODE_PUBID_DCN; - } else { - const mediaTypeMode = getAdapterMode(); - payload.site.id = TEST_MODE_DCN; - payload.imp.forEach(impObject => { - impObject.ext.e2eTestMode = true; - if (mediaTypeMode === BANNER) { - impObject.tagid = TEST_MODE_BANNER_POS; // banner passback - } else if (mediaTypeMode === VIDEO) { - impObject.tagid = TEST_MODE_VIDEO_POS; // video passback - } else { - logWarn('yahoossp adapter e2etest mode does not support yahoossp.mode="all". \n Please specify either "banner" or "video"'); - logWarn('yahoossp adapter e2etest mode: Please make sure your adUnit matches the yahoossp.mode video or banner'); - } - }); - } - }; - - return { - url: sspEndpoint, - method: 'POST', - data: payload, - options: requestOptions, - bidderRequest: bidderRequest - }; -}; - -function createRenderer(bidderRequest, bidResponse) { - const renderer = Renderer.install({ - url: 'https://s.yimg.com/kp/prebid-outstream-renderer/renderer.js', - loaded: false, - adUnitCode: bidderRequest.adUnitCode - }) - - try { - renderer.setRender(function(bidResponse) { - setTimeout(function() { - // eslint-disable-next-line no-undef - o2PlayerRender(bidResponse); - }, deepAccess(bidderRequest, 'params.testing.renderer.setTimeout') || DEFAULT_RENDERER_TIMEOUT); - }); - } catch (error) { - logWarn('yahoossp renderer error: setRender() failed', error); - } - return renderer; -} - -/* Utility functions */ - -export const spec = { - code: BIDDER_CODE, - gvlid: GVLID, - aliases: [], - supportedMediaTypes: [BANNER, VIDEO], - - isBidRequestValid: function(bid) { - const params = bid.params; - if (deepAccess(params, 'testing.e2etest') === true) { - return true; - } else if ( - isPlainObject(params) && - (isNotEmptyStr(params.pubId) || (isNotEmptyStr(params.dcn) && isNotEmptyStr(params.pos))) - ) { - return true; - } else { - logWarn('yahoossp bidder params missing or incorrect, please pass object with either: dcn & pos OR pubId'); - return false; - } - }, - - buildRequests: function(validBidRequests, bidderRequest) { - if (isEmpty(validBidRequests) || isEmpty(bidderRequest)) { - logWarn('yahoossp Adapter: buildRequests called with either empty "validBidRequests" or "bidderRequest"'); - return undefined; - }; - - const requestOptions = { - contentType: 'application/json', - customHeaders: { - 'x-openrtb-version': '2.5' - } - }; - - requestOptions.withCredentials = hasPurpose1Consent(bidderRequest.gdprConsent); - - const filteredBidRequests = filterBidRequestByMode(validBidRequests); - - if (config.getConfig('yahoossp.singleRequestMode') === true) { - const payload = generateOpenRtbObject(bidderRequest, filteredBidRequests[0]); - filteredBidRequests.forEach(bid => { - appendImpObject(bid, payload); - }); - - return generateServerRequest({payload, requestOptions, bidderRequest}); - } - - return filteredBidRequests.map(bid => { - const payloadClone = generateOpenRtbObject(bidderRequest, bid); - appendImpObject(bid, payloadClone); - return generateServerRequest({payload: payloadClone, requestOptions, bidderRequest: bid}); - }); - }, - - interpretResponse: function(serverResponse, { data, bidderRequest }) { - const response = []; - if (!serverResponse.body || !Array.isArray(serverResponse.body.seatbid)) { - return response; - } - - let seatbids = serverResponse.body.seatbid; - seatbids.forEach(seatbid => { - let bid; - - try { - bid = seatbid.bid[0]; - } catch (e) { - return response; - } - - let cpm = (bid.ext && bid.ext.encp) ? bid.ext.encp : bid.price; - - let bidResponse = { - adId: deepAccess(bid, 'adId') ? bid.adId : bid.impid || bid.crid, - adUnitCode: bidderRequest.adUnitCode, - requestId: bid.impid, - cpm: cpm, - width: bid.w, - height: bid.h, - creativeId: bid.crid || 0, - currency: bid.cur || DEFAULT_CURRENCY, - dealId: bid.dealid ? bid.dealid : null, - netRevenue: true, - ttl: getTtl(bidderRequest), - meta: { - advertiserDomains: bid.adomain, - } - }; - - const responseAdmFormat = getResponseFormat(bid); - if (responseAdmFormat === BANNER) { - bidResponse.mediaType = BANNER; - bidResponse.ad = bid.adm; - bidResponse.meta.mediaType = BANNER; - } else if (responseAdmFormat === VIDEO) { - bidResponse.mediaType = VIDEO; - bidResponse.meta.mediaType = VIDEO; - bidResponse.vastXml = bid.adm; - - if (bid.nurl) { - bidResponse.vastUrl = bid.nurl; - }; - } - - if (deepAccess(bidderRequest, 'mediaTypes.video.context') === 'outstream' && !bidderRequest.renderer) { - bidResponse.renderer = createRenderer(bidderRequest, bidResponse) || undefined; - } - - response.push(bidResponse); - }); - - return response; - }, - - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { - const bidResponse = !isEmpty(serverResponses) && serverResponses[0].body; - - if (bidResponse && bidResponse.ext && bidResponse.ext.pixels) { - const userSyncObjects = extractUserSyncUrls(syncOptions, bidResponse.ext.pixels); - userSyncObjects.forEach(userSyncObject => { - userSyncObject.url = updateConsentQueryParams(userSyncObject.url, { - gpp: gppConsent, - gdpr: gdprConsent, - uspConsent: uspConsent - }); - }); - return userSyncObjects; - } - - return []; - } -}; - -registerBidder(spec); diff --git a/modules/yahoosspBidAdapter.md b/modules/yahoosspBidAdapter.md deleted file mode 100644 index 35a640b92b1..00000000000 --- a/modules/yahoosspBidAdapter.md +++ /dev/null @@ -1,832 +0,0 @@ -# Overview -**Module Name:** yahoossp Bid Adapter -**Module Type:** Bidder Adapter -**Maintainer:** hb-fe-tech@yahooinc.com - -# Description -The Yahoo SSP Bid Adapter is an OpenRTB interface that consolidates all previous "Oath.inc" adapters such as: "aol", "oneMobile", "oneDisplay" & "oneVideo" supply-side platforms. - -# Supported Features: -* Media Types: Banner & Video -* Outstream renderer -* Multi-format adUnits -* Schain module -* Price floors module -* Advertiser domains -* End-2-End self-served testing mode -* Outstream renderer/Player -* User ID Modules - ConnectId and others -* First Party Data (ortb2 & ortb2Imp) -* Custom TTL (time to live) - - -# Adapter Request mode -Since the yahoossp adapter now supports both Banner and Video adUnits a controller was needed to allow you to define when the adapter should generate a bid-requests to our Yahoo SSP. - -**Important!** By default the adapter mode is set to "banner" only. -This means that you do not need to explicitly declare the yahoossp.mode in the Global config to initiate banner adUnit requests. - -## Request modes: -* **undefined** - (Default) Will generate bid-requests for "Banner" formats only. -* **banner** - Will generate bid-requests for "Banner" formats only (Explicit declaration). -* **video** - Will generate bid-requests for "Video" formats only (Explicit declaration). -* **all** - Will generate bid-requests for both "Banner" & "Video" formats - -**Important!** When setting yahoossp.mode = 'all' Make sure your Yahoo SSP Placement (pos id) supports both Banner & Video placements. -If it does not, the Yahoo SSP will respond only in the format it is set too. - -```javascript -pbjs.setConfig({ - yahoossp: { - mode: 'banner' // 'all', 'video', 'banner' (default) - } -}); -``` -# Integration Options -The `yahoossp` bid adapter supports 2 types of integration: -1. **dcn & pos** DEFAULT (Site/App & Position targeting) - For Display partners/publishers. -2. **pubId** (Publisher ID) - For legacy "oneVideo" AND New partners/publishers. -**Important:** pubId integration (option 2) is only possible when your Seller account is setup for "Inventory Mapping". - -**Please Note:** Most examples in this file are using dcn & pos. - -## Who is currently eligible for "pubId" integration -At this time, only the following partners/publishers are eligble for pubId integration: -1. New partners/publishers that do not have any existing accounts on Yahoo SSP (aka: aol, oneMobile, oneDisplay). -2. Video SSP (oneVideo) partners/publishers that - A. Do not have any display/banner inventory. - B. Do not have any existing accounts on Yahoo SSP (aka: aol, oneMobile, oneDisplay). - -# Mandatory Bidder Parameters -## dcn & pos (DEFAULT) -The minimal requirements for the 'yahoossp' bid adapter to generate an outbound bid-request to our Yahoo SSP are: -1. At least 1 adUnit including mediaTypes: banner or video -2. **bidder.params** object must include: - A. **dcn:** Yahoo SSP Site/App inventory parameter. - B. **pos:** Yahoo SSP position inventory parameter. - -### Example: dcn & pos Mandatory Parameters (Single banner adUnit) -```javascript -const adUnits = [{ - code: 'your-placement', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - bids: [ - { - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from SSP - pos: '8a969978017a7aaabab4ab0bc01a0009' // Placement ID provided from SSP - } - } - ] -}]; -``` - -## pubId -The minimal requirements for the 'yahoossp' bid adapter to generate an outbound bid-request to our Yahoo SSP are: -1. At least 1 adUnit including mediaTypes: banner or video -2. **bidder.params** object must include: - A. **pubId:** Yahoo SSP Publisher ID (AKA oneVideo pubId/Exchange name) - -### Example: pubId Mandatory Parameters (Single banner adUnit) -```javascript -const adUnits = [{ - code: 'your-placement', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - bids: [ - { - bidder: 'yahoossp', - params: { - pubId: 'DemoPublisher', // Publisher External ID provided from Yahoo SSP. - } - } - ] -}]; -``` -# Advanced adUnit Examples: -## Banner -```javascript -const adUnits = [{ - code: 'banner-adUnit', - mediaTypes: { - banner: { - sizes: [ - [300, 250] - ] - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP - pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided from Yahoo SSP - } - } - }] -}]; -``` -## Video Instream -**Important!** Make sure that the Yahoo SSP Placement type (in-stream) matches the adUnit video inventory type. -**Note:** Make sure to set the adapter mode to allow video requests by setting it to mode: 'video' OR mode: 'all'. -```javascript -pbjs.setConfig({ - yahoossp: { - mode: 'video' - } -}); - -const adUnits = [{ - code: 'video-adUnit', - mediaTypes: { - video: { - context: 'instream', - playerSize: [ - [300, 250] - ], - mimes: ['video/mp4','application/javascript'], - api: [2] - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP - pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP - } - }] -}]; -``` -## Video Outstream -**Important!** Make sure that the Yahoo SSP Placement type (in-feed/ in-article) matches the adUnit video inventory type. -**Note:** Make sure to set the adapter mode to allow video requests by setting it to mode: 'video' OR mode: 'all' -```javascript -pbjs.setConfig({ - yahoossp: { - mode: 'video' - } -}); - -const adUnits = [{ - code: 'video-adUnit', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [ - [300, 250] - ], - mimes: ['video/mp4','application/javascript'], - api: [2] - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP - pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP - } - }] -}]; -``` -## Multi-Format -**Important!** If you intend to use the yahoossp bidder for both Banner and Video formats please make sure: -1. Set the adapter as mode: 'all' - to call the Yahoo SSP for both banner & video formats. -2. Make sure the Yahoo SSP placement (pos id) supports both banner & video format requests. - -```javascript -pbjs.setConfig({ - yahoossp: { - mode: 'all' - } -}); - -const adUnits = [{ - code: 'video-adUnit', - mediaTypes: { - banner: { - sizes: [[300, 250]] - }, - video: { - context: 'outstream', - playerSize: [ - [300, 250] - ], - mimes: ['video/mp4','application/javascript'], - api: [2] - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP - pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP - } - }] -}]; -``` - -# Optional: Schain module support -The yahoossp adapter supports the Prebid.org Schain module and will pass it through to our Yahoo SSP -For further details please see, https://docs.prebid.org/dev-docs/modules/schain.html - -## Global Schain Example: -```javascript - pbjs.setConfig({ - "schain": { - "validation": "off", - "config": { - "ver": "1.0", - "complete": 1, - "nodes": [{ - "asi": "some-platform.com", - "sid": "111111", - "hp": 1 - }] - } - } - }); -``` -## Bidder Specific Schain Example: -```javascript - pbjs.setBidderConfig({ - "bidders": ['yahoossp'], // can list more bidders here if they share the same config - "config": { - "schain": { - "validation": "strict", - "config": { - "ver": "1.0", - "complete": 1, - "nodes": [{ - "asi": "other-platform.com", - "sid": "222222", - "hp": 0 - }] - } - } - } - }); -``` - -# Optional: Price floors module & bidfloor -The yahoossp adapter supports the Prebid.org Price Floors module and will use it to define the outbound bidfloor and currency. -By default the adapter will always check the existance of Module price floor. -If a module price floor does not exist you can set a custom bid floor for your impression using "params.bidOverride.imp.bidfloor". - -**Note:** All override params apply to all requests generated using this configuration regardless of format type. - -```javascript -const adUnits = [{ - code: 'override-pricefloor', - mediaTypes: { - banner: { - sizes: [ - [300, 250] - ] - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP - pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided from Yahoo SSP - bidOverride :{ - imp: { - bidfloor: 5.00 // bidOverride priceFloor - } - } - } - } - }] -}]; -``` - -For further details please see, https://docs.prebid.org/dev-docs/modules/floors.html - -# Optional: Self-served E2E testing mode -If you want to see how the yahoossp adapter works and loads you are invited to try it out using our testing mode. -This is useful for integration testing and response parsing when checking banner vs video capabilities. - -## How to use E2E test mode: -1. Set the yahoossp global config mode to either 'banner' or 'video' - depending on the adUnit you want to test. -2. Add params.testing.e2etest: true to your adUnit bidder config - See examples below. - -**Note:** When using E2E Test Mode you do not need to pass mandatory bidder params dcn or pos. - -**Important!** E2E Testing Mode only works when the Bidder Request Mode is set explicitly to either 'banner' or 'video'. - -## Activating E2E Test for "Banner" - ```javascript -pbjs.setConfig({ - yahoossp: { - mode: 'banner' // select 'banner' or 'video' to define what response to load - } -}); - -const adUnits = [{ - code: 'your-placement', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - bids: [ - { - bidder: 'yahoossp', - params: { - testing: { - e2etest: true // Activate E2E Test mode - } - } - } - ] -}]; - -``` -## Activating E2E Test for "Video" -**Note:** We recommend using Video Outstream as it would load the video response using our Outstream Renderer feature - ```javascript -pbjs.setConfig({ - yahoossp: { - mode: 'video' - } -}); - -const adUnits = [{ - code: 'video-adUnit', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [ - [300, 250] - ], - mimes: ['video/mp4','application/javascript'], - api: [2] - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - testing: { - e2etest: true // Activate E2E Test mode - } - } - }] -}]; -``` - -# Optional: First Party Data -The yahoossp adapter now supports first party data passed via: -1. Global ortb2 object using pbjs.setConfig() -2. adUnit ortb2Imp object declared within an adUnit. -For further details please see, https://docs.prebid.org/features/firstPartyData.html -## Global First Party Data "ortb2" -### Passing First Party "site" data: -```javascript -pbjs.setConfig({ - ortb2: { - site: { - name: 'yahooAdTech', - domain: 'yahooadtech.com', - cat: ['IAB2'], - sectioncat: ['IAB2-2'], - pagecat: ['IAB2-2'], - page: 'https://page.yahooadtech.com/here.html', - ref: 'https://ref.yahooadtech.com/there.html', - keywords:'yahoo, ad, tech', - search: 'SSP', - content: { - id: '1234', - title: 'Title', - series: 'Series', - season: 'Season', - episode: 1, - cat: ['IAB1', 'IAB1-1', 'IAB1-2', 'IAB2', 'IAB2-1'], - genre: 'Fantase', - contentrating: 'C-Rating', - language: 'EN', - prodq: 1, - context: 1, - len: 200, - data: [{ - name: "www.dataprovider1.com", - ext: { segtax: 4 }, - segment: [ - { id: "687" }, - { id: "123" } - ] - }], - ext: { - network: 'ext-network', - channel: 'ext-channel', - data: { - pageType: "article", - category: "repair" - } - } - } - } - } - }); -``` - -Notes: The first party site info is filtered and only the following specific keys are allowed in the bidRequests: - -| Field | Type | -|-----------------------------|--------| -| site.name | String | -| site.domain | String | -| site.page | String | -| site.ref | String | -| site.keywords | String | -| site.search | String | -| site.cat | Array | -| site.sectioncat | Array | -| site.pagecat | Array | -| site.ext | Object | -| site.publisher.ext | Object | -| site.content.id | String | -| site.content.title | String | -| site.content.series | String | -| site.content.season | String | -| site.content.genre | String | -| site.content.contentrating | String | -| site.content.language | String | -| site.content.episode | Number | -| site.content.prodq | Number | -| site.content.context | Number | -| site.content.livestream | Number | -| site.content.len | Number | -| site.content.cat | Array | -| site.content.ext | Object | -| site.content.data | Array | -| site.content.data[].id | String | -| site.content.data[].name | String | -| site.content.data[].segment | Array | -| site.content.data[].ext | Object | - - -### Passing First Party "user" data: -```javascript -pbjs.setConfig({ - ortb2: { - user: { - yob: 1985, - gender: 'm', - keywords: 'a,b', - data: [{ - name: "www.dataprovider1.com", - ext: { segtax: 4 }, - segment: [ - { id: "687" }, - { id: "123" } - ] - }], - ext: { - data: { - registered: true, - interests: ['cars'] - } - } - } - } - }); -``` -### Passing First Party "app.content.data" data: -```javascript -pbjs.setConfig({ - ortb2: { - app: { - content: { - data: [{ - name: "www.dataprovider1.com", - ext: { segtax: 4 }, - segment: [ - { id: "687" }, - { id: "123" } - ] - }], - } - } - } - }); -``` - - -## AdUnit First Party Data "ortb2Imp" -Most DSPs are adopting the Global Placement ID (GPID). -Please pass your placement specific GPID value to Yahoo SSP using `adUnit.ortb2Imp.ext.data.pbadslot`. -```javascript -const adUnits = [{ - code: 'placement', - mediaTypes: { - banner: { - sizes: [ - [300, 250] - ] - }, - }, - ortb2Imp: { - ext: { - data: { - pbadslot: "homepage-top-rect", - adUnitSpecificAttribute: "123" - } - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - pubdId: 'DemoPublisher' - } - }] - } - ] -``` - -# Optional: Bidder bidOverride Parameters -The yahoossp adapter allows passing override data to the outbound bid-request in that overrides First Party Data. -**Important!** We highly recommend using prebid modules to pass data instead of bidder speicifc overrides. -The use of these parameters are a last resort to force a specific feature or use case in your implementation. - -Currently the bidOverride object only accepts the following: -* imp - * video - * mimes - * w - * h - * maxbitrate - * maxduration - * minduration - * api - * delivery - * pos - * playbackmethod - * placement - * linearity - * protocols - * rewarded -* site - * page -* device - * ip - - -```javascript -const adUnits = [{ - code: 'bidOverride-adUnit', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [ - [300, 250] - ], - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', - pos: '8a96958a017a7a57ac375d50c0c700cc', - bidOverride: { - imp: { - video: { - mimes: ['video/mp4', 'javascript/application'], - w: 300, - h: 200, - maxbitrate: 4000, - maxduration: 30, - minduration: 10, - api: [1,2], - delivery: 1, - pos: 1, - playbackmethod: 0, - placement: 1, - linearity: 1, - protocols: [2,5], - startdelay: 0, - rewarded: 0 - } - }, - site: { - page: 'https://yahoossp-bid-adapter.com', - }, - device: { - ip: "1.2.3.4" - } - } - } - }] -}] -``` - -# Optional: Custom Key-Value Pairs -Custom key-value paris can be used for both inventory targeting and reporting. -You must set up key-value pairs in the Yahoo SSP before sending them via the adapter otherwise the Ad Server will not be listening and picking them up. - -Important! Key-value pairs can only contain values of types: String, Number, Array of strings OR Array of numbers - -```javascript -const adUnits = [{ - code: 'key-value-pairs', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [ - [300, 250] - ], - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', - pos: '8a96958a017a7a57ac375d50c0c700cc', - kvp: { - key1: 'value', // String - key2: 123456, // Number - key3: ['string1','string2', 'string3'], // Array of strings - key4: [1, 23, 456, 7890] // Array of Numbers - } - } - }] -}] -``` - -# Optional: Custom Cache Time To Live (ttl): -The yahoossp adapter supports passing of "Time To Live" (ttl) that indicates to prebid chache for how long to keep the chaced winning bid alive. Value is Number in seconds and you can enter any number between 1 - 3600 (seconds). -The setting can be defined globally using setConfig or within the adUnit.params. -Global level setConfig overrides adUnit.params. -If no value is being passed default is 300 seconds. -## Global TTL -```javascript -pbjs.setConfig({ - yahoossp: { - ttl: 300 - } -}); - -const adUnits = [{ - code: 'global-ttl', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [ - [300, 250] - ], - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', - pos: '8a96958a017a7a57ac375d50c0c700cc', - } - }] -}] -``` - -## Ad Unit Params TTL -```javascript -const adUnits = [{ - code: 'adUnit-ttl', - mediaTypes: { - banner: { - sizes: [[300, 250]], - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', - pos: '8a96958a017a7a57ac375d50c0c700cc', - ttl: 300, - } - }] -}] -``` -# Optional: Video Features -## Rewarded video flag -To indicate to Yahoo SSP that this adUnit is a rewarded video you can pass the following in the params.bidOverride.imp.video.rewarded: 1 - -```javascript -const adUnits = [{ - code: 'rewarded-video-adUnit', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [ - [300, 250] - ], - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - dcn: '8a969516017a7a396ec539d97f540011', - pos: '8a96958a017a7a57ac375d50c0c700cc', - bidOverride: { - imp: { - video: { - rewarded: 1 - } - } - } - } - }] -}] -``` - -## Site/App Targeting for "pubId" Inventory Mapping -To target your adUnit explicitly to a specific Site/App Object in Yahoo SSP, you can pass one of the following: -1. params.siteId = External Site ID || Video SSP RTBIS Id (in String format). -2. params.bidOverride.site.id = External Site ID || Video SSP RTBIS Id (in String format). -**Important:** Site override is a only supported when using "pubId" mode. -**Important:** If you are switching from the oneVideo adapter, please make sure to pass inventoryid as a String instead of Integer. - -```javascript -const adUnits = [{ - code: 'pubId-site-targeting-adUnit', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [ - [300, 250] - ], - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - pubId: 'DemoPublisher', - siteId: '1234567'; - } - }] -}] -``` - -## Placement Targeting for "pubId" Inventory Mapping -To target your adUnit explicitly to a specific Placement within a Site/App Object in Yahoo SSP, you can pass the following params.placementId = External Placement ID || Placement Alias - -**Important!** Placement override is a only supported when using "pubId" mode. -**Important!** It is highly recommended that you pass both `siteId` AND `placementId` together to avoid inventory miss matching. - -### Site & Placement override -**Important!** If the placement ID does not reside under the defined Site/App object, the request will not resolve and no response will be sent back from the ad-server. -```javascript -const adUnits = [{ - code: 'pubId-site-targeting-adUnit', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [ - [300, 250] - ], - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - pubId: 'DemoPublisher', - siteId: '1234567', - placementId: 'header-250x300' - } - }] -}] -``` -### Placement only override -**Important!** Using this method is not advised if you have multiple Site/Apps that are broken out of a Run Of Network (RON) Site/App. If the placement ID does not reside under a matching Site/App object, the request will not resolve and no response will be sent back from the ad-server. -```javascript -const adUnits = [{ - code: 'pubId-site-targeting-adUnit', - mediaTypes: { - video: { - context: 'outstream', - playerSize: [ - [300, 250] - ], - } - }, - bids: [{ - bidder: 'yahoossp', - params: { - pubId: 'DemoPublisher', - placementId: 'header-250x300' - } - }] -}] -``` - -# Optional: Legacy override Parameters -This adapter does not support passing legacy overrides via 'bidder.params.ext' since most of the data should be passed using prebid modules (First Party Data, Schain, Price Floors etc.). -If you do not know how to pass a custom parameter that you previously used, please contact us using the information provided above. - -Thank you, -Yahoo SSP - From 95cf40d3c3e5198e48006c8f76af5b077c09fe89 Mon Sep 17 00:00:00 2001 From: slimkrazy Date: Tue, 27 Jun 2023 13:44:22 +0100 Subject: [PATCH 3/6] Addressing PR feedback --- ...ingBidAdapter.js => yahoosspBidAdapter.js} | 6 +- ...ingBidAdapter.md => yahoosspBidAdapter.md} | 80 +++++++++---------- ...dapter_spec.js => yahoosspAdapter_spec.js} | 28 ++++--- 3 files changed, 59 insertions(+), 55 deletions(-) rename modules/{yahooAdvertisingBidAdapter.js => yahoosspBidAdapter.js} (99%) rename modules/{yahooAdvertisingBidAdapter.md => yahoosspBidAdapter.md} (89%) rename test/spec/modules/{yahooAdvertisingAdapter_spec.js => yahoosspAdapter_spec.js} (99%) diff --git a/modules/yahooAdvertisingBidAdapter.js b/modules/yahoosspBidAdapter.js similarity index 99% rename from modules/yahooAdvertisingBidAdapter.js rename to modules/yahoosspBidAdapter.js index effeb845e21..5217ba3e87d 100644 --- a/modules/yahooAdvertisingBidAdapter.js +++ b/modules/yahoosspBidAdapter.js @@ -6,9 +6,10 @@ import { Renderer } from '../src/Renderer.js'; import {hasPurpose1Consent} from '../src/utils/gpdr.js'; const INTEGRATION_METHOD = 'prebid.js'; -const BIDDER_CODE = 'yahooAdvertising'; -const BIDDER_ALIASES = ['yahoossp'] +const BIDDER_CODE = 'yahooAds'; +const BIDDER_ALIASES = ['yahoossp', 'yahooAdvertising'] const GVLID = 25; +const ADAPTER_VERSION = '1.1.0'; const PREBID_VERSION = '$prebid.version$'; const DEFAULT_BID_TTL = 300; const TEST_MODE_DCN = '8a969516017a7a396ec539d97f540011'; @@ -287,6 +288,7 @@ function generateOpenRtbObject(bidderRequest, bid) { source: { ext: { hb: 1, + adapterver: ADAPTER_VERSION, prebidver: PREBID_VERSION, integration: { name: INTEGRATION_METHOD, diff --git a/modules/yahooAdvertisingBidAdapter.md b/modules/yahoosspBidAdapter.md similarity index 89% rename from modules/yahooAdvertisingBidAdapter.md rename to modules/yahoosspBidAdapter.md index 68e8791d99e..8eb8ebadb50 100644 --- a/modules/yahooAdvertisingBidAdapter.md +++ b/modules/yahoosspBidAdapter.md @@ -21,10 +21,10 @@ The Yahoo Advertising Bid Adapter is an OpenRTB interface that consolidates all # Adapter Request mode -Since the `yahooAdvertising` bid adapter supports both Banner and Video adUnits, a controller was needed to allow you to define when the adapter should generate a bid-requests to the Yahoo bid endpoint. +Since the Yahoo Advertising bid adapter supports both Banner and Video adUnits, a controller was needed to allow you to define when the adapter should generate a bid-requests to the Yahoo bid endpoint. **Important!** By default the adapter mode is set to "banner" only. -This means that you do not need to explicitly declare the `yahooAdvertising.mode` property in the global config to initiate banner adUnit requests. +This means that you do not need to explicitly declare the `yahooAds.mode` property in the global config to initiate banner adUnit requests. ## Request modes: * **undefined** - (Default) Will generate bid-requests for "Banner" formats only. @@ -32,20 +32,20 @@ This means that you do not need to explicitly declare the `yahooAdvertising.mode * **video** - Will generate bid-requests for "Video" formats only (Explicit declaration). * **all** - Will generate bid-requests for both "Banner" & "Video" formats. -**Important!** When setting `yahooAdvertising.mode` to `'all'`, make sure your Yahoo Placement (pos id) supports both Banner & Video placements. +**Important!** When setting `yahooAds.mode` to `'all'`, make sure your Yahoo Placement (pos id) supports both Banner & Video placements. If it does not, the Yahoo bid server will respond only in the format it is set too. ### Example: explicitly setting the request mode ```javascript pbjs.setConfig({ - yahooAdvertising: { + yahooAds: { mode: 'banner' // 'all', 'video', 'banner' (default) } }); ``` # Integration Options -The `yahooAdvertising` bid adapter supports 2 types of integration: +The `yahooAds` bid adapter supports 2 types of integration: 1. **dcn & pos** DEFAULT (Site/App & Position targeting) - For Display partners/publishers. 2. **pubId** (Publisher ID) - For legacy "oneVideo" AND new partners/publishers. **Important:** pubId integration (option 2) is only possible when your seller account is setup for "Inventory Mapping". @@ -61,7 +61,7 @@ At this time, only the following partners/publishers are eligble for pubId integ # Mandatory Bidder Parameters ## dcn & pos (DEFAULT) -The minimal requirements for the `yahooAdvertising` bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: +The minimal requirements for the `yahooAds` bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: 1. At least 1 adUnit including mediaTypes: banner or video 2. **bidder.params** object must include: A. **dcn:** Yahoo Advertising Site/App inventory parameter. @@ -78,7 +78,7 @@ const adUnits = [{ }, bids: [ { - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising pos: '8a969978017a7aaabab4ab0bc01a0009' // Placement ID provided by Yahoo Advertising @@ -89,7 +89,7 @@ const adUnits = [{ ``` ## pubId -The minimal requirements for the `yahooAdvertising` bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: +The minimal requirements for the `yahooAds` bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: 1. At least 1 adUnit including mediaTypes: banner or video 2. **bidder.params** object must include: A. **pubId:** Yahoo Advertising Publisher ID (AKA oneVideo pubId/Exchange name) @@ -105,7 +105,7 @@ const adUnits = [{ }, bids: [ { - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { pubId: 'DemoPublisher', // Publisher defined external ID as configured by Yahoo Advertising. } @@ -127,7 +127,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided by Yahoo Advertising @@ -142,7 +142,7 @@ const adUnits = [{ **Note:** Make sure to set the adapter mode to allow video requests by setting it to mode: 'video' OR mode: 'all'. ```javascript pbjs.setConfig({ - yahooAdvertising: { + yahooAds: { mode: 'video' } }); @@ -160,7 +160,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided by Yahoo Advertising @@ -174,7 +174,7 @@ const adUnits = [{ **Note:** Make sure to set the adapter mode to allow video requests by setting it to mode: 'video' OR mode: 'all' ```javascript pbjs.setConfig({ - yahooAdvertising: { + yahooAds: { mode: 'video' } }); @@ -192,7 +192,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided by Yahoo Advertising @@ -202,12 +202,12 @@ const adUnits = [{ ``` ## Multi-Format -**Important!** If you intend to use the `yahooAdvertising` bidder for both Banner and Video formats please make sure: +**Important!** If you intend to use the `yahooAds` bidder for both Banner and Video formats please make sure: 1. Set the adapter as mode: 'all' - to configure the bid adapter to call the bid endpoint for both banner & video formats. 2. Make sure the Yahoo Advertising placement (pos id) supports both banner & video format requests. ```javascript pbjs.setConfig({ - yahooAdvertising: { + yahooAds: { mode: 'all' } }); @@ -228,7 +228,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided by Yahoo Advertising @@ -238,7 +238,7 @@ const adUnits = [{ ``` # Optional: Schain module support -The `yahooAdvertising` bid adapter supports the Prebid.org Schain module and will pass it through to our bid endpoint. +The `yahooAds` bid adapter supports the Prebid.org Schain module and will pass it through to our bid endpoint. For further details please see, https://docs.prebid.org/dev-docs/modules/schain.html ## Global Schain Example: ```javascript @@ -260,7 +260,7 @@ For further details please see, https://docs.prebid.org/dev-docs/modules/schain. ## Bidder Specific Schain Example: ```javascript pbjs.setBidderConfig({ - "bidders": ['yahooAdvertising'], // can list more bidders here if they share the same config + "bidders": ['yahooAds'], // can list more bidders here if they share the same config "config": { "schain": { "validation": "strict", @@ -279,7 +279,7 @@ For further details please see, https://docs.prebid.org/dev-docs/modules/schain. ``` # Optional: Price floors module & bidfloor -The `yahooAdvertising` bid adapter supports the Prebid.org Price Floors module and will use it to define the outbound bidfloor and currency, if the relevant floors have been defined in the configuration. +The `yahooAds` bid adapter supports the Prebid.org Price Floors module and will use it to define the outbound bidfloor and currency, if the relevant floors have been defined in the configuration. A cusom method for defining bid floors is also supported, this can be enabled by setting the `params.bidOverride.imp.bidfloor` bidder parameter. **Note:** All override params apply to all requests generated using this configuration regardless of format type. @@ -294,7 +294,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided by Yahoo Advertising @@ -312,11 +312,11 @@ const adUnits = [{ For further details please see, https://docs.prebid.org/dev-docs/modules/floors.html # Optional: Self-served E2E testing mode -If you want to see how the `yahooAdvertising` bid adapter works and loads you are invited to try it out using our testing mode. +If you want to see how the `yahooAds` bid adapter works and loads you are invited to try it out using our testing mode. This is useful for integration testing and response parsing when checking banner vs video capabilities. ## How to use E2E test mode: -1. Set the `yahooAdvertising` global config mode to either `'banner'` or `'video'` - depending on the adUnit you want to test. +1. Set the `yahooAds` global config mode to either `'banner'` or `'video'` - depending on the adUnit you want to test. 2. Add params.testing.e2etest: true to your adUnit bidder config - See examples below. **Note:** When using E2E Test Mode you do not need to pass mandatory bidder params dcn or pos. @@ -326,7 +326,7 @@ This is useful for integration testing and response parsing when checking banner ## Activating E2E Test for "Banner" ```javascript pbjs.setConfig({ - yahooAdvertising: { + yahooAds: { mode: 'banner' // select 'banner' or 'video' to define what response to load } }); @@ -340,7 +340,7 @@ const adUnits = [{ }, bids: [ { - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { testing: { e2etest: true // Activate E2E Test mode @@ -355,7 +355,7 @@ const adUnits = [{ **Note:** We recommend using Video Outstream as it would load the video response using our Outstream Renderer feature ```javascript pbjs.setConfig({ - yahooAdvertising: { + yahooAds: { mode: 'video' } }); @@ -373,7 +373,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { testing: { e2etest: true // Activate E2E Test mode @@ -384,7 +384,7 @@ const adUnits = [{ ``` # Optional: First Party Data -The `yahooAdvertising` bid adapter supports first party data passed via: +The `yahooAds` bid adapter supports first party data passed via: 1. Global ortb2 object using `pbjs.setConfig()` 2. adUnit ortb2Imp object declared within an adUnit. For further details please see, https://docs.prebid.org/features/firstPartyData.html @@ -543,7 +543,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { pubdId: 'DemoPublisher' } @@ -553,7 +553,7 @@ const adUnits = [{ ``` # Optional: Bidder bidOverride Parameters -The `yahooAdvertising` bid adapter allows passing override data to the outbound bid-request that overrides First Party Data. +The `yahooAds` bid adapter allows passing override data to the outbound bid-request that overrides First Party Data. **Important!** We highly recommend using prebid modules to pass data instead of bidder speicifc overrides. The use of these parameters are a last resort to force a specific feature or use case in your implementation. @@ -591,7 +591,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -645,7 +645,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -661,14 +661,14 @@ const adUnits = [{ ``` # Optional: Custom Cache Time To Live (ttl): -The `yahooAdvertising` bid adapter supports passing of "Time To Live" (ttl) to indicate to prebid how long the bid response from Yahoo Advertising should be retained by Prebid for. This configuration value must be a Number in seconds, with the valid range being 1 - 3600 inclusive. +The `yahooAds` bid adapter supports passing of "Time To Live" (ttl) to indicate to prebid how long the bid response from Yahoo Advertising should be retained by Prebid for. This configuration value must be a Number in seconds, with the valid range being 1 - 3600 inclusive. The setting can be defined globally using `setConfig` or within the adUnit.params. Global level `setConfig` overrides adUnit.params. If no value is being passed default is 300 seconds. ## Global TTL ```javascript pbjs.setConfig({ - yahooAdvertising: { + yahooAds: { ttl: 300 } }); @@ -684,7 +684,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -703,7 +703,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -729,7 +729,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -764,7 +764,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { pubId: 'DemoPublisher', siteId: '1234567'; @@ -793,7 +793,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { pubId: 'DemoPublisher', siteId: '1234567', @@ -816,7 +816,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahooAdvertising', + bidder: 'yahooAds', params: { pubId: 'DemoPublisher', placementId: 'header-250x300' diff --git a/test/spec/modules/yahooAdvertisingAdapter_spec.js b/test/spec/modules/yahoosspAdapter_spec.js similarity index 99% rename from test/spec/modules/yahooAdvertisingAdapter_spec.js rename to test/spec/modules/yahoosspAdapter_spec.js index 4ba8829cbe5..09d1bd04753 100644 --- a/test/spec/modules/yahooAdvertisingAdapter_spec.js +++ b/test/spec/modules/yahoosspAdapter_spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { config } from 'src/config.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; -import { spec } from 'modules/yahooAdvertisingBidAdapter.js'; +import { spec } from 'modules/yahoosspBidAdapter.js'; import {createEidsArray} from '../../../modules/userId/eids'; const DEFAULT_BID_ID = '84ab500420319d'; @@ -12,8 +12,9 @@ const DEFAULT_AD_UNIT_CODE = '/19968336/header-bid-tag-1'; const DEFAULT_AD_UNIT_TYPE = 'banner'; const DEFAULT_PARAMS_BID_OVERRIDE = {}; const DEFAULT_VIDEO_CONTEXT = 'instream'; -const DEFAULT_BIDDER_CODE = 'yahooAdvertising'; -const VALID_BIDDER_CODES = [DEFAULT_BIDDER_CODE, 'yahoossp']; +const ADAPTER_VERSION = '1.1.0'; +const DEFAULT_BIDDER_CODE = 'yahooAds'; +const VALID_BIDDER_CODES = [DEFAULT_BIDDER_CODE, 'yahoossp', 'yahooAdvertising']; const PREBID_VERSION = '$prebid.version$'; const INTEGRATION_METHOD = 'prebid.js'; @@ -188,11 +189,11 @@ describe('Yahoo Advertising Bid Adapter:', () => { describe('Validate basic properties', () => { it('should define the correct bidder code', () => { - expect(spec.code).to.equal('yahooAdvertising'); + expect(spec.code).to.equal('yahooAds'); }); it('should define the correct bidder aliases', () => { - expect(spec.aliases).to.deep.equal(['yahoossp']); + expect(spec.aliases).to.deep.equal(['yahoossp', 'yahooAdvertising']); }); it('should define the correct vendor ID', () => { @@ -828,7 +829,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { describe('Endpoint & Impression Request Mode:', () => { afterEach(() => { config.setConfig({ - yahooAdvertising: { + yahooAds: { singleRequestMode: undefined } }); @@ -880,7 +881,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { bidderRequest.bids = validBidRequests; config.setConfig({ - yahooAdvertising: { + yahooAds: { singleRequestMode: true } }); @@ -1011,6 +1012,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { expect(data.source).to.deep.equal({ ext: { hb: 1, + adapterver: ADAPTER_VERSION, prebidver: PREBID_VERSION, integration: { name: INTEGRATION_METHOD, @@ -1448,7 +1450,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { describe('for mediaTypes: "video"', () => { beforeEach(() => { config.setConfig({ - yahooAdvertising: { + yahooAds: { mode: VIDEO } }); @@ -1456,7 +1458,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { afterEach(() => { config.setConfig({ - yahooAdvertising: { + yahooAds: { mode: undefined } }); @@ -1482,7 +1484,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { describe('wrapped in video players for display inventory', () => { beforeEach(() => { config.setConfig({ - yahooAdvertising: { + yahooAds: { mode: undefined } }); @@ -1551,7 +1553,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { it(`should not allow unsupported global ${bidderCode}.ttl formats and default to 300`, () => { const { serverResponse, bidderRequest } = generateResponseMock('banner'); const cfg = {}; - cfg['yahooAdvertising'] = { ttl: param }; + cfg['yahooAds'] = { ttl: param }; config.setConfig(cfg); const response = spec.interpretResponse(serverResponse, {bidderRequest}); expect(response[0].ttl).to.equal(300); @@ -1570,7 +1572,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { it('should not allow invalid global config ttl values 3600 < ttl < 0 and default to 300', () => { const { serverResponse, bidderRequest } = generateResponseMock('banner'); config.setConfig({ - yahooAdvertising: { ttl: param } + yahooAds: { ttl: param } }); const response = spec.interpretResponse(serverResponse, {bidderRequest}); expect(response[0].ttl).to.equal(300); @@ -1587,7 +1589,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { it('should give presedence to Gloabl ttl over params.ttl ', () => { const { serverResponse, bidderRequest } = generateResponseMock('banner'); config.setConfig({ - yahooAdvertising: { ttl: 500 } + yahooAds: { ttl: 500 } }); bidderRequest.bids[0].params.ttl = 400; const response = spec.interpretResponse(serverResponse, {bidderRequest}); From 3ee9438664edb2f46b10c4b223cda3481d77ab96 Mon Sep 17 00:00:00 2001 From: slimkrazy Date: Tue, 27 Jun 2023 13:47:31 +0100 Subject: [PATCH 4/6] Updated docs --- modules/yahoosspBidAdapter.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/yahoosspBidAdapter.md b/modules/yahoosspBidAdapter.md index 8eb8ebadb50..c8c42930e5b 100644 --- a/modules/yahoosspBidAdapter.md +++ b/modules/yahoosspBidAdapter.md @@ -45,7 +45,7 @@ pbjs.setConfig({ ``` # Integration Options -The `yahooAds` bid adapter supports 2 types of integration: +The Yahoo Advertising bid adapter supports 2 types of integration: 1. **dcn & pos** DEFAULT (Site/App & Position targeting) - For Display partners/publishers. 2. **pubId** (Publisher ID) - For legacy "oneVideo" AND new partners/publishers. **Important:** pubId integration (option 2) is only possible when your seller account is setup for "Inventory Mapping". @@ -61,7 +61,7 @@ At this time, only the following partners/publishers are eligble for pubId integ # Mandatory Bidder Parameters ## dcn & pos (DEFAULT) -The minimal requirements for the `yahooAds` bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: +The minimal requirements for the Yahoo Advertising bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: 1. At least 1 adUnit including mediaTypes: banner or video 2. **bidder.params** object must include: A. **dcn:** Yahoo Advertising Site/App inventory parameter. @@ -89,7 +89,7 @@ const adUnits = [{ ``` ## pubId -The minimal requirements for the `yahooAds` bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: +The minimal requirements for the Yahoo Advertising bid adapter to generate an outbound bid-request to Yahoo's bid endpoint are: 1. At least 1 adUnit including mediaTypes: banner or video 2. **bidder.params** object must include: A. **pubId:** Yahoo Advertising Publisher ID (AKA oneVideo pubId/Exchange name) @@ -202,7 +202,7 @@ const adUnits = [{ ``` ## Multi-Format -**Important!** If you intend to use the `yahooAds` bidder for both Banner and Video formats please make sure: +**Important!** If you intend to use the Yahoo Advertising bidder for both Banner and Video formats please make sure: 1. Set the adapter as mode: 'all' - to configure the bid adapter to call the bid endpoint for both banner & video formats. 2. Make sure the Yahoo Advertising placement (pos id) supports both banner & video format requests. ```javascript @@ -238,7 +238,7 @@ const adUnits = [{ ``` # Optional: Schain module support -The `yahooAds` bid adapter supports the Prebid.org Schain module and will pass it through to our bid endpoint. +The Yahoo Advertising bid adapter supports the Prebid.org Schain module and will pass it through to our bid endpoint. For further details please see, https://docs.prebid.org/dev-docs/modules/schain.html ## Global Schain Example: ```javascript @@ -279,7 +279,7 @@ For further details please see, https://docs.prebid.org/dev-docs/modules/schain. ``` # Optional: Price floors module & bidfloor -The `yahooAds` bid adapter supports the Prebid.org Price Floors module and will use it to define the outbound bidfloor and currency, if the relevant floors have been defined in the configuration. +The Yahoo Advertising bid adapter supports the Prebid.org Price Floors module and will use it to define the outbound bidfloor and currency, if the relevant floors have been defined in the configuration. A cusom method for defining bid floors is also supported, this can be enabled by setting the `params.bidOverride.imp.bidfloor` bidder parameter. **Note:** All override params apply to all requests generated using this configuration regardless of format type. @@ -312,7 +312,7 @@ const adUnits = [{ For further details please see, https://docs.prebid.org/dev-docs/modules/floors.html # Optional: Self-served E2E testing mode -If you want to see how the `yahooAds` bid adapter works and loads you are invited to try it out using our testing mode. +If you want to see how the Yahoo Advertising bid adapter works and loads you are invited to try it out using our testing mode. This is useful for integration testing and response parsing when checking banner vs video capabilities. ## How to use E2E test mode: @@ -384,7 +384,7 @@ const adUnits = [{ ``` # Optional: First Party Data -The `yahooAds` bid adapter supports first party data passed via: +The Yahoo Advertising bid adapter supports first party data passed via: 1. Global ortb2 object using `pbjs.setConfig()` 2. adUnit ortb2Imp object declared within an adUnit. For further details please see, https://docs.prebid.org/features/firstPartyData.html @@ -523,7 +523,7 @@ pbjs.setConfig({ ## AdUnit First Party Data "ortb2Imp" Most DSPs are adopting the Global Placement ID (GPID). -Please pass your placement specific GPID value to Yahoo SSP using `adUnit.ortb2Imp.ext.data.pbadslot`. +Please pass your placement specific GPID value by setting `adUnit.ortb2Imp.ext.data.pbadslot`. ```javascript const adUnits = [{ code: 'placement', @@ -553,7 +553,7 @@ const adUnits = [{ ``` # Optional: Bidder bidOverride Parameters -The `yahooAds` bid adapter allows passing override data to the outbound bid-request that overrides First Party Data. +The Yahoo Advertising bid adapter allows passing override data to the outbound bid-request that overrides First Party Data. **Important!** We highly recommend using prebid modules to pass data instead of bidder speicifc overrides. The use of these parameters are a last resort to force a specific feature or use case in your implementation. @@ -661,7 +661,7 @@ const adUnits = [{ ``` # Optional: Custom Cache Time To Live (ttl): -The `yahooAds` bid adapter supports passing of "Time To Live" (ttl) to indicate to prebid how long the bid response from Yahoo Advertising should be retained by Prebid for. This configuration value must be a Number in seconds, with the valid range being 1 - 3600 inclusive. +The Yahoo Advertising bid adapter supports passing of "Time To Live" (ttl) to indicate to prebid how long the bid response from Yahoo Advertising should be retained by Prebid for. This configuration value must be a Number in seconds, with the valid range being 1 - 3600 inclusive. The setting can be defined globally using `setConfig` or within the adUnit.params. Global level `setConfig` overrides adUnit.params. If no value is being passed default is 300 seconds. From 2f4fc5b012d6dcb715b911cafcd67336fe707645 Mon Sep 17 00:00:00 2001 From: slimkrazy Date: Wed, 28 Jun 2023 12:30:38 +0100 Subject: [PATCH 5/6] Renamed spec file --- .../{yahoosspAdapter_spec.js => yahoosspBidAdapter_spec.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/spec/modules/{yahoosspAdapter_spec.js => yahoosspBidAdapter_spec.js} (100%) diff --git a/test/spec/modules/yahoosspAdapter_spec.js b/test/spec/modules/yahoosspBidAdapter_spec.js similarity index 100% rename from test/spec/modules/yahoosspAdapter_spec.js rename to test/spec/modules/yahoosspBidAdapter_spec.js From d3ec6fd82fce0c86de6163e6396998a546df3a06 Mon Sep 17 00:00:00 2001 From: slimkrazy Date: Wed, 28 Jun 2023 19:08:54 +0100 Subject: [PATCH 6/6] Ensure consent data is pulled and sent correctly --- modules/yahoosspBidAdapter.js | 15 ++-- test/spec/modules/yahoosspBidAdapter_spec.js | 77 ++++++++++++++------ 2 files changed, 61 insertions(+), 31 deletions(-) diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js index 5217ba3e87d..a66d76f8689 100644 --- a/modules/yahoosspBidAdapter.js +++ b/modules/yahoosspBidAdapter.js @@ -119,11 +119,12 @@ function extractUserSyncUrls(syncOptions, pixels) { */ function updateConsentQueryParams(url, consentData) { const parameterMap = { - 'gdpr_consent': consentData.gdpr.consentString, - 'gdpr': consentData.gdpr.gdprApplies ? '1' : '0', - 'us_privacy': consentData.uspConsent, - 'gpp': consentData.gpp.gppString, - 'gpp_sid': consentData.gpp.applicableSections ? consentData.gpp.applicableSections.join(',') : '' + 'gdpr_consent': consentData.gdpr ? consentData.gdpr.consentString : '', + 'gdpr': consentData.gdpr && consentData.gdpr.gdprApplies ? '1' : '0', + 'us_privacy': consentData.uspConsent ? consentData.uspConsent : '', + 'gpp': consentData.gpp ? consentData.gpp.gppString : '', + 'gpp_sid': consentData.gpp && Array.isArray(consentData.gpp.applicableSections) + ? consentData.gpp.applicableSections.join(',') : '' } const existingUrl = new URL(url); @@ -281,8 +282,8 @@ function generateOpenRtbObject(bidderRequest, bid) { ext: { 'us_privacy': bidderRequest.uspConsent ? bidderRequest.uspConsent : '', gdpr: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies ? 1 : 0, - gpp: bidderRequest.gppConsent.gppString, - gpp_sid: bidderRequest.gppConsent.applicableSections + gpp: bidderRequest.gppConsent ? bidderRequest.gppConsent.gppString : '', + gpp_sid: bidderRequest.gppConsent ? bidderRequest.gppConsent.applicableSections : [] } }, source: { diff --git a/test/spec/modules/yahoosspBidAdapter_spec.js b/test/spec/modules/yahoosspBidAdapter_spec.js index 09d1bd04753..c0584173082 100644 --- a/test/spec/modules/yahoosspBidAdapter_spec.js +++ b/test/spec/modules/yahoosspBidAdapter_spec.js @@ -280,33 +280,62 @@ describe('Yahoo Advertising Bid Adapter:', () => { }); describe('user consent parameters are updated', () => { - let syncOptions = { + const syncOptions = { iframeEnabled: true, pixelEnabled: true }; - let pixelObjects = spec.getUserSyncs( - syncOptions, - SERVER_RESPONSES, - bidderRequest.gdprConsent, - bidderRequest.uspConsent, - bidderRequest.gppConsent - ); - pixelObjects.forEach(pixelObject => { - let url = pixelObject.url; - let urlParams = new URL(url).searchParams; - const expectedParams = { - 'baz': 'true', - 'gdpr_consent': bidderRequest.gdprConsent.consentString, - 'gdpr': bidderRequest.gdprConsent.gdprApplies ? '1' : '0', - 'us_privacy': bidderRequest.uspConsent, - 'gpp': bidderRequest.gppConsent.gppString, - 'gpp_sid': Array.isArray(bidderRequest.gppConsent.applicableSections) ? bidderRequest.gppConsent.applicableSections.join(',') : '' - } - for (const [key, value] of Object.entries(expectedParams)) { - it(`Updates the ${key} consent param in user sync URL ${url}`, () => { - expect(urlParams.get(key)).to.equal(value); - }); - }; + describe('when all consent data is set', () => { + const pixelObjects = spec.getUserSyncs( + syncOptions, + SERVER_RESPONSES, + bidderRequest.gdprConsent, + bidderRequest.uspConsent, + bidderRequest.gppConsent + ); + pixelObjects.forEach(pixelObject => { + let url = pixelObject.url; + let urlParams = new URL(url).searchParams; + const expectedParams = { + 'baz': 'true', + 'gdpr_consent': bidderRequest.gdprConsent.consentString, + 'gdpr': bidderRequest.gdprConsent.gdprApplies ? '1' : '0', + 'us_privacy': bidderRequest.uspConsent, + 'gpp': bidderRequest.gppConsent.gppString, + 'gpp_sid': Array.isArray(bidderRequest.gppConsent.applicableSections) ? bidderRequest.gppConsent.applicableSections.join(',') : '' + } + for (const [key, value] of Object.entries(expectedParams)) { + it(`Updates the ${key} consent param in user sync URL ${url}`, () => { + expect(urlParams.get(key)).to.equal(value); + }); + }; + }); + }); + + describe('when no consent data is set', () => { + const pixelObjects = spec.getUserSyncs( + syncOptions, + SERVER_RESPONSES, + undefined, + undefined, + undefined + ); + pixelObjects.forEach(pixelObject => { + let url = pixelObject.url; + let urlParams = new URL(url).searchParams; + const expectedParams = { + 'baz': 'true', + 'gdpr_consent': '', + 'gdpr': '0', + 'us_privacy': '', + 'gpp': '', + 'gpp_sid': '' + } + for (const [key, value] of Object.entries(expectedParams)) { + it(`Updates the ${key} consent param in user sync URL ${url}`, () => { + expect(urlParams.get(key)).to.equal(value); + }); + }; + }); }); }); });