From 3fe8dc2dc067de9a54004d256a3ea1c739b63921 Mon Sep 17 00:00:00 2001 From: tarasmatokhniuk Date: Mon, 16 Dec 2024 10:52:01 +0100 Subject: [PATCH 1/3] adtrgtme bid adapter Bugfix, add params.zid as placement ident --- modules/adtrgtmeBidAdapter.js | 538 +++++++------- modules/adtrgtmeBidAdapter.md | 19 +- test/spec/modules/adtrgtmeBidAdapter_spec.js | 715 +++++++++---------- 3 files changed, 636 insertions(+), 636 deletions(-) diff --git a/modules/adtrgtmeBidAdapter.js b/modules/adtrgtmeBidAdapter.js index 18cbd1e6ae7..bd655820dac 100644 --- a/modules/adtrgtmeBidAdapter.js +++ b/modules/adtrgtmeBidAdapter.js @@ -1,332 +1,358 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; -import { deepAccess, isFn, isStr, isNumber, isArray, isEmpty, isPlainObject, generateUUID, logWarn } from '../src/utils.js'; +import { + deepAccess, + isFn, + isStr, + isNumber, + isArray, + isEmpty, + isPlainObject, + generateUUID, + logWarn, +} from '../src/utils.js'; import { config } from '../src/config.js'; import { hasPurpose1Consent } from '../src/utils/gdpr.js'; -const INTEGRATION_METHOD = 'prebid.js'; const BIDDER_CODE = 'adtrgtme'; -const ENDPOINT = 'https://z.cdn.adtarget.market/ssp?prebid&s='; -const ADAPTER_VERSION = '1.0.0'; -const PREBID_VERSION = '$prebid.version$'; -const DEFAULT_BID_TTL = 300; -const DEFAULT_CURRENCY = 'USD'; - -function transformSizes(sizes) { - const getSize = (size) => { - return { - w: parseInt(size[0]), - h: parseInt(size[1]) - } - } - 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; +const BIDDER_VERSION = '1.0.4'; +const BIDDER_URL = 'https://z.cdn.adtarget.market/ssp?prebid&s='; +const PREBIDJS_VERSION = '$prebid.version$'; +const DEFAULT_TTL = 300; +const DEFAULT_CUR = 'USD'; + +function getFormat(s) { + const parseSize = ([w, h]) => ({ w: parseInt(w, 10), h: parseInt(h, 10) }); + return Array.isArray(s) && s.length === 2 && !Array.isArray(s[0]) + ? [parseSize(s)] + : s.map(parseSize); } -function isSecure(bid) { - return deepAccess(bid, 'params.bidOverride.imp.secure') ?? deepAccess(bid, 'ortb2Imp.secure') ?? 1; -}; - -function getMediaType(bid) { +function getType(bid) { return deepAccess(bid, 'mediaTypes.banner') ? BANNER : false; } -function validateAppendObject(validationFunction, allowedKeys, inputObject, appendToObject) { - const outputObject = { - ...appendToObject +function appObj( + checker, + keys, + obj, + appObj +) { + const res = { + ...appObj, }; - if (allowedKeys.length > 0 && typeof validationFunction === 'function') { - for (const objectKey in inputObject) { - if (allowedKeys.indexOf(objectKey) !== -1 && validationFunction(inputObject[objectKey])) { - outputObject[objectKey] = inputObject[objectKey] + if (keys.length > 0 && typeof checker === 'function') { + for (const oKey in obj) { + if ( + keys.indexOf(oKey) !== -1 && + checker(obj[oKey]) + ) { + res[oKey] = obj[oKey]; } } } - return outputObject; -}; + return res; +} -function getTtl(bidderRequest) { - const ttl = config.getConfig('adtrgtme.ttl'); - const validateTTL = (ttl) => { - return (isNumber(ttl) && ttl > 0 && ttl < 3600) ? ttl : DEFAULT_BID_TTL - }; - return ttl ? validateTTL(ttl) : validateTTL(deepAccess(bidderRequest, 'params.ttl')); -}; +function getBidfloor(b) { + return isFn(b.getFloor) ? b.getFloor({size: '*', + currency: deepAccess(b, 'params.bidOverride.cur') ?? DEFAULT_CUR, + mediaType: BANNER}) : false +} -function getFloorModuleData(bid) { - const getFloorRequestObject = { - currency: deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CURRENCY, - mediaType: BANNER, - size: '*' - }; - return (isFn(bid.getFloor)) ? (bid.getFloor(getFloorRequestObject) || {}) : false; -}; +function getTtl(bR) { + const t = config.getConfig('adtrgtme.ttl'); + const validate = (t) => (isNumber(t) && t > 0 && t < 3000) ? t : DEFAULT_TTL; + return t + ? validate(t) + : validate(deepAccess(bR, 'params.ttl')); +} -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 +function createORTB(bR, bid) { + if (!bR) return; + + const { currency = deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CUR } = getBidfloor(bR); + const ip = deepAccess(bid, 'params.bidOverride.device.ip') || deepAccess(bid, 'params.ext.ip'); + const gdpr = bR.gdprConsent?.gdprApplies ? 1 : 0; + const consentString = gdpr ? bR.gdprConsent?.consentString : ''; + const usPrivacy = bR.uspConsent || ''; + + let oR = { + id: generateUUID(), + cur: [currency], + imp: [], + site: { + page: deepAccess(bR, 'refererInfo.page'), + id: String(bid.params.sid), + }, + device: { + dnt: 0, + ua: navigator.userAgent, + ip, + }, + regs: { + ext: { + us_privacy: usPrivacy, + gdpr, }, - regs: { - ext: { - 'us_privacy': bidderRequest.uspConsent ? bidderRequest.uspConsent : '', - gdpr: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies ? 1 : 0 - } + }, + source: { + ext: { + hb: 1, + bidderver: BIDDER_VERSION, + prebidjsver: PREBIDJS_VERSION, + ...(deepAccess(bid, 'schain') && { schain: bid.schain }), }, - source: { - ext: { - hb: 1, - adapterver: ADAPTER_VERSION, - prebidver: PREBID_VERSION, - integration: { - name: INTEGRATION_METHOD, - ver: PREBID_VERSION - } - }, - fd: 1 + fd: 1, + }, + user: { + ext: { + consent: consentString, }, - user: { - ext: { - consent: bidderRequest.gdprConsent && bidderRequest.gdprConsent.gdprApplies - ? bidderRequest.gdprConsent.consentString : '' - } - } - }; - - outBoundBidRequest.site.id = bid.params.sid; - - if (bidderRequest.ortb2) { - outBoundBidRequest = appendFirstPartyData(outBoundBidRequest, bid); - }; - - if (deepAccess(bid, 'schain')) { - outBoundBidRequest.source.ext.schain = bid.schain; - outBoundBidRequest.source.ext.schain.nodes[0].rid = outBoundBidRequest.id; - }; - - return outBoundBidRequest; + }, }; -}; - -function appendImpObject(bid, openRtbObject) { - const mediaTypeMode = getMediaType(bid); - if (openRtbObject && bid) { - const impObject = { - id: bid.bidId, - secure: isSecure(bid), - bidfloor: getFloorModuleData(bid).floor || deepAccess(bid, 'params.bidOverride.imp.bidfloor') || 0.000001 - }; - - if (mediaTypeMode === BANNER) { - 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 (bR.ortb2) { + oR = appendSiteData(oR, bid); + } - impObject.ext = { - dfp_ad_unit_code: bid.adUnitCode - }; + if (deepAccess(bid, 'schain')) { + oR.source.ext.schain.nodes[0].rid = oR.id; + } - if (deepAccess(bid, 'params.zid')) { - impObject.tagid = bid.params.zid; - } + return oR; +} - if (deepAccess(bid, 'ortb2Imp.ext.data') && isPlainObject(bid.ortb2Imp.ext.data)) { - impObject.ext.data = bid.ortb2Imp.ext.data; - }; +function appendImp(bid, oRtb) { + if (!oRtb || !bid) return; + + const type = getType(bid); + const { floor: bidfloor = 0, currency: bidfloorcur = '' } = getBidfloor(bid); + const overrideFloor = deepAccess(bid, 'params.bidOverride.imp.bidfloor') || bidfloor; + const overrideCurrency = deepAccess(bid, 'params.bidOverride.imp.bidfloorcur') || bidfloorcur; + + const impObject = { + id: bid.bidId, + secure: 1, + bidfloor: overrideFloor, + bidfloorcur: overrideCurrency, + ext: { + dfp_ad_unit_code: bid.adUnitCode, + ...(deepAccess(bid, 'ortb2Imp.ext.data') && isPlainObject(bid.ortb2Imp.ext.data) && { data: bid.ortb2Imp.ext.data }) + }, + ...(deepAccess(bid, 'params.zid') && { tagid: String(bid.params.zid) }), + ...(deepAccess(bid, 'ortb2Imp.instl') === 1 && { instl: 1 }), + }; - if (deepAccess(bid, 'ortb2Imp.instl') && isNumber(bid.ortb2Imp.instl) && (bid.ortb2Imp.instl === 1)) { - impObject.instl = bid.ortb2Imp.instl; + if (type === BANNER) { + impObject.banner = { + mimes: bid.mediaTypes.banner.mimes || ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + format: getFormat(bid.sizes), + ...(bid.mediaTypes.banner.pos && { pos: bid.mediaTypes.banner.pos }), }; + } - openRtbObject.imp.push(impObject); - }; + oRtb.imp.push(impObject); }; -function appendFirstPartyData(outBoundBidRequest, bid) { - const ortb2Object = bid.ortb2; - const siteObject = deepAccess(ortb2Object, 'site') || undefined; - const siteContentObject = deepAccess(siteObject, 'content') || undefined; - const userObject = deepAccess(ortb2Object, 'user') || undefined; - - if (siteObject && isPlainObject(siteObject)) { - const allowedSiteStringKeys = ['name', 'domain', 'page', 'ref', 'keywords']; - const allowedSiteArrayKeys = ['cat', 'sectioncat', 'pagecat']; - const allowedSiteObjectKeys = ['ext']; - outBoundBidRequest.site = validateAppendObject(isStr, allowedSiteStringKeys, siteObject, outBoundBidRequest.site); - outBoundBidRequest.site = validateAppendObject(isArray, allowedSiteArrayKeys, siteObject, outBoundBidRequest.site); - outBoundBidRequest.site = validateAppendObject(isPlainObject, allowedSiteObjectKeys, siteObject, outBoundBidRequest.site); - }; - - if (siteContentObject && isPlainObject(siteContentObject)) { - const allowedContentStringKeys = ['id', 'title', 'language']; - const allowedContentArrayKeys = ['cat']; - outBoundBidRequest.site.content = validateAppendObject(isStr, allowedContentStringKeys, siteContentObject, outBoundBidRequest.site.content); - outBoundBidRequest.site.content = validateAppendObject(isArray, allowedContentArrayKeys, siteContentObject, outBoundBidRequest.site.content); - }; +function appendSiteData(oR, bid) { + const site = deepAccess(bid.ortb2, 'site') || undefined; + const content = deepAccess(site, 'content') || undefined; + const user = deepAccess(bid.ortb2, 'user') || undefined; + + if (site && isPlainObject(site)) { + const keys = [ + 'id', + 'name', + 'domain', + 'page', + 'ref', + 'keywords', + ]; + oR.site = appObj( + isStr, + keys, + site, + oR.site + ); + oR.site = appObj( + isArray, + ['cat', 'sectioncat', 'pagecat'], + site, + oR.site + ); + oR.site = appObj( + isPlainObject, + ['ext'], + site, + oR.site + ); + } - if (userObject && isPlainObject(userObject)) { - const allowedUserStrings = ['id', 'buyeruid', 'gender', 'keywords', 'customdata']; - const allowedUserObjects = ['ext']; - outBoundBidRequest.user = validateAppendObject(isStr, allowedUserStrings, userObject, outBoundBidRequest.user); - outBoundBidRequest.user.ext = validateAppendObject(isPlainObject, allowedUserObjects, userObject, outBoundBidRequest.user.ext); - }; + if (content && isPlainObject(content)) { + oR.site.content = appObj( + isStr, + ['id', 'title', 'language', 'keywords'], + content, + oR.site.content + ); + oR.site.content = appObj( + isArray, + ['cat'], + content, + oR.site.content + ); + } - return outBoundBidRequest; -}; + if (user && isPlainObject(user)) { + oR.user = appObj( + isStr, + [ + 'id', + 'buyeruid', + 'gender', + 'keywords', + 'customdata', + ], + user, + oR.user + ); + oR.user.ext = appObj( + isPlainObject, + ['ext'], + user, + oR.user.ext + ); + } + return oR; +} -function generateServerRequest({payload, requestOptions, bidderRequest}) { +function createRequest({ data, options, bidderRequest }) { return { - url: (config.getConfig('adtrgtme.endpoint') || ENDPOINT) + (payload.site.id || ''), + url: `${config.getConfig('adtrgtme.endpoint') || BIDDER_URL}${data.site?.id || ''}`, method: 'POST', - data: payload, - options: requestOptions, - bidderRequest: bidderRequest - }; -}; + data, + options, + bidderRequest, + } +} export const spec = { code: BIDDER_CODE, aliases: [], supportedMediaTypes: [BANNER], - isBidRequestValid: function(bid) { + isOK: function (bid) { const params = bid.params; - if (isPlainObject(params) && isNumber(params.sid)) { + if ( + isPlainObject(params) && + isStr(params.sid) && + !isEmpty(params.sid) && + params.sid.length > 0 && + (isEmpty(params.zid) || + isNumber(params.zid) || + (isStr(params.zid) && !isNaN(parseInt(params.zid)))) + ) { return true; } else { - logWarn('Adtrgtme bidder params missing or incorrect'); + logWarn('Adtrgtme request invalid'); return false; } }, - buildRequests: function(validBidRequests, bidderRequest) { - if (isEmpty(validBidRequests) || isEmpty(bidderRequest)) { + buildRequests: function (bR, aR) { + if (isEmpty(bR) || isEmpty(aR)) { logWarn('Adtrgtme Adapter: buildRequests called with empty request'); return undefined; - }; + } - const requestOptions = { + const options = { contentType: 'application/json', - customHeaders: { - 'x-openrtb-version': '2.5' - } + withCredentials: hasPurpose1Consent( + aR.gdprConsent + ) }; - requestOptions.withCredentials = hasPurpose1Consent(bidderRequest.gdprConsent); - if (config.getConfig('adtrgtme.singleRequestMode') === true) { - const payload = generateOpenRtbObject(bidderRequest, validBidRequests[0]); - validBidRequests.forEach(bid => { - appendImpObject(bid, payload); + const data = createORTB(aR, bR[0]); + bR.forEach((bid) => { + appendImp(bid, data); }); - return generateServerRequest({payload, requestOptions, bidderRequest}); + return createRequest({ data, options, bidderRequest: aR }); } - return validBidRequests.map(bid => { - const payloadClone = generateOpenRtbObject(bidderRequest, bid); - appendImpObject(bid, payloadClone); + return bR.map((b) => { + const data = createORTB(aR, b); + appendImp(b, data); - return generateServerRequest({payload: payloadClone, requestOptions, bidderRequest: bid}); + return createRequest({ + data, + options, + bidderRequest: b, + }); }); }, - interpretResponse: function(serverResponse, { data, bidderRequest }) { - const response = []; - if (!serverResponse.body || !Array.isArray(serverResponse.body.seatbid)) { - return response; + interpretResponse: function (sR, { data, bidderRequest }) { + const res = []; + if (!sR.body || !Array.isArray(sR.body.seatbid)) { + return res; } - let seatbids = serverResponse.body.seatbid; - seatbids.forEach(seatbid => { - let bid; - + sR.body.seatbid.forEach((sb) => { try { - bid = seatbid.bid[0]; + let b = sb.bid[0]; + + res.push({ + adId: deepAccess(b, 'adId') ? b.adId : b.impid || b.crid, + ad: b.adm, + adUnitCode: bidderRequest.adUnitCode, + requestId: b.impid, + cpm: b.price, + width: b.w, + height: b.h, + mediaType: BANNER, + creativeId: b.crid || 0, + currency: b.cur || DEFAULT_CUR, + dealId: b.dealid ? b.dealid : null, + netRevenue: true, + ttl: getTtl(bidderRequest), + meta: { + advertiserDomains: b.adomain || [], + mediaType: BANNER, + }, + }); } catch (e) { - return response; + return res; } - - let cpm = bid.price; - - let bidResponse = { - adId: deepAccess(bid, 'adId') ? bid.adId : bid.impid || bid.crid, - ad: bid.adm, - 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), - mediaType: BANNER, - meta: { - advertiserDomains: bid.adomain, - mediaType: BANNER, - } - }; - - response.push(bidResponse); }); - return response; + return res; }, - - getUserSyncs: function(syncOptions, serverResponses, gdprConsent = {}, uspConsent = '') { - const bidResponse = !isEmpty(serverResponses) && serverResponses[0].body; - if (bidResponse && bidResponse.ext && bidResponse.ext.pixels) { - return extractUserSyncUrls(syncOptions, bidResponse.ext.pixels); + getUserSyncs: function (options, sR) { + const s = []; + if (!options.pixelEnabled && !options.iframeEnabled) { + return s; + } + if (Array.isArray(sR)) { + sR.forEach((response) => { + const p = response.body?.ext?.pixels; + if (Array.isArray(p)) { + p.forEach(([stype, url]) => { + const type = stype.toLowerCase(); + if ( + typeof url === 'string' && url.startsWith('http') && + (((type === 'image' || type === 'img') && options.pixelEnabled) || + (type === 'iframe' && options.iframeEnabled)) + ) { + s.push({type, url}); + } + }); + } + }); } - return []; + return s; } }; diff --git a/modules/adtrgtmeBidAdapter.md b/modules/adtrgtmeBidAdapter.md index d136b17067d..b1a01e2e7b7 100644 --- a/modules/adtrgtmeBidAdapter.md +++ b/modules/adtrgtmeBidAdapter.md @@ -31,18 +31,23 @@ const adUnits = [{ { bidder: 'adtrgtme', params: { - sid: 1220291391, // Site/App ID provided from SSP + sid: '1220291391', // Site/App ID provided from SSP } } ] }]; ``` -# Optional: Price floors module & bidfloor -The adtargerme adapter supports the Prebid.org Price Floors module and will use it to define the outbound bidfloor and currency. +# Optional +## Price floors module & bidfloor +The 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". +If a module price floor does not exist you can set a custom bid floor for your impression using "params.bidOverride.imp.bidfloor" and "params.bidOverride.imp.bidfloorcur". +## Strict placement identification +It's possible to use params.zid for strict identification for placement id provided from SSP like tagid. + +## Example: ```javascript const adUnits = [{ code: 'your-placement', @@ -56,10 +61,12 @@ const adUnits = [{ bids: [{ bidder: 'adtrgtme', params: { - sid: 1220291391, + sid: '1220291391', + zid: '1836455615', bidOverride :{ imp: { - bidfloor: 5.00 // bidOverride bidfloor + bidfloor: 5.00, // bidOverride bidfloor + bidfloorcur: 'USD' // bidOverride currency } } } diff --git a/test/spec/modules/adtrgtmeBidAdapter_spec.js b/test/spec/modules/adtrgtmeBidAdapter_spec.js index fce270b4ea7..d9ca79a246b 100644 --- a/test/spec/modules/adtrgtmeBidAdapter_spec.js +++ b/test/spec/modules/adtrgtmeBidAdapter_spec.js @@ -1,249 +1,238 @@ import { expect } from 'chai'; import { config } from 'src/config.js'; -import { BANNER } from 'src/mediaTypes.js'; import { spec } from 'modules/adtrgtmeBidAdapter.js'; -const DEFAULT_SID = 1220291391; -const DEFAULT_ZID = 1836455615; -const DEFAULT_BID_ID = '84ab500420319d'; +const DEFAULT_SID = '1220291391'; +const DEFAULT_ZID = '1836455615'; +const DEFAULT_PIXEL_URL = 'https://cdn.adtarget.me/libs/1x1.gif'; +const DEFAULT_BANNER_URL = 'https://cdn.adtarget.me/libs/banner/300x250.jpg'; +const BIDDER_VERSION = '1.0.4'; +const PREBIDJS_VERSION = '$prebid.version$'; -const DEFAULT_AD_UNIT_CODE = '/1220291391/header-banner'; -const DEFAULT_AD_UNIT_TYPE = BANNER; -const DEFAULT_PARAMS_BID_OVERRIDE = {}; - -const ADAPTER_VERSION = '1.0.0'; -const PREBID_VERSION = '$prebid.version$'; -const INTEGRATION_METHOD = 'prebid.js'; - -// Utility functions -const generateBidRequest = ({bidId, adUnitCode, bidOverrideObject, zid, ortb2}) => { - const bidRequest = { +const createBidRequest = ({bidId, adUnitCode, bidOverride, zid, ortb2}) => { + const bR = { + auctionId: 'f3c594t-3o0ch1b0rm-ayn93c3o0ch1b0rm', adUnitCode, - auctionId: 'b06c5141-fe8f-4cdf-9d7d-54415490a917', bidId, - bidderRequestsCount: 1, bidder: 'adtrgtme', - bidderRequestId: '7101db09af0db2', - bidderWinsCount: 0, mediaTypes: {}, params: { - bidOverride: bidOverrideObject + sid: DEFAULT_SID, + bidOverride }, - src: 'client', transactionId: '5b17b67d-7704-4732-8cc9-5b1723e9bcf9', ortb2 }; - const bannerObj = { + bR.mediaTypes.banner = { sizes: [[300, 250]] }; + bR.sizes = [[300, 250]]; - bidRequest.mediaTypes.banner = bannerObj; - bidRequest.sizes = [[300, 250]]; - - bidRequest.params.sid = DEFAULT_SID; - if (typeof zid == 'number') { - bidRequest.params.zid = zid; + if (typeof zid == 'string') { + bR.params.zid = zid; } - - return bidRequest; + return bR; } -let generateBidderRequest = (bidRequestArray, adUnitCode, ortb2 = {}) => { - const bidderRequest = { - adUnitCode: adUnitCode || 'default-adUnitCode', +let createBidderRequest = (arr, code = 'default-code', ortb2 = {}) => { + return { + adUnitCode: code, auctionId: 'd4c83a3b-18e4-4208-b98b-63848449c7aa', - auctionStart: new Date().getTime(), bidderCode: 'adtrgtme', - bidderRequestId: '112f1c7c5d399a', - bids: bidRequestArray, + bids: arr, refererInfo: { - page: 'https://publisher-test.com', - reachedTop: true, - isAmp: false, - numIframes: 0, - stack: ['https://publisher-test.com'], + page: 'https://partner-site.com', + stack: ['https://partner-site.com'], }, gdprConsent: { consentString: 'BOtmiBKOtmiBKABABAENAFAAAAACeAAA', vendorData: {}, gdprApplies: true }, - start: new Date().getTime(), timeout: 1000, ortb2 }; - - return bidderRequest; }; -const generateBuildRequestMock = ({bidId, adUnitCode, adUnitType, zid, bidOverrideObject, pubIdMode, ortb2}) => { - const bidRequestConfig = { - bidId: bidId || DEFAULT_BID_ID, - adUnitCode: adUnitCode || DEFAULT_AD_UNIT_CODE, - adUnitType: adUnitType || DEFAULT_AD_UNIT_TYPE, +const createRequestMock = ({bidId, adUnitCode, type, zid, bidOverride, pubIdMode, ortb2}) => { + const bR = createBidRequest({ + bidId: bidId || '84ab500420319d', + adUnitCode: adUnitCode || '/1220291391/banner', + type: type || 'banner', zid: zid || DEFAULT_ZID, - bidOverrideObject: bidOverrideObject || DEFAULT_PARAMS_BID_OVERRIDE, - + bidOverride: bidOverride || {}, pubIdMode: pubIdMode || false, ortb2: ortb2 || {} - }; - const bidRequest = generateBidRequest(bidRequestConfig); - const validBidRequests = [bidRequest]; - const bidderRequest = generateBidderRequest(validBidRequests, adUnitCode, ortb2); - - return { bidRequest, validBidRequests, bidderRequest } + }); + return { bidRequest: bR, validBR: [bR], bidderRequest: createBidderRequest([bR], adUnitCode, ortb2) } }; -const generateAdmPayload = (admPayloadType) => { - let ADM_PAYLOAD; - switch (admPayloadType) { +const createAdm = (type) => { + let ADM; + switch (type) { case 'banner': - ADM_PAYLOAD = ''; // banner + ADM = ` + `; break; - default: ''; break; + default: 'Ad is here'; break; }; - - return ADM_PAYLOAD; + return ADM; }; -const generateResponseMock = (admPayloadType) => { - const bidResponse = { - id: 'fc0c35df-21fb-4f93-9ebd-88759dbe31f9', - impid: '274395c06a24e5', - adm: generateAdmPayload(admPayloadType), - price: 1, - w: 300, - h: 250, - crid: 'ssp-placement-name', - adomain: ['advertiser-domain.com'] - }; - - const serverResponse = { +const createResponseMock = (type) => { + const sR = { body: { - id: 'fc0c35df-21fb-4f93-9ebd-88759dbe31f9', - seatbid: [{ bid: [ bidResponse ], seat: 13107 }] + id: '5qtvluj7bk6jhzmqwu4zzulv', + seatbid: [{ + bid: [{ + id: '5qtvluj7bk6jhzmqwu4zzulv', + impid: 'y7v7iu0uljj94rbjcw9', + adm: createAdm(type), + price: 1, + w: 300, + h: 250, + crid: 'creativeid', + adomain: ['some-advertiser-domain.com'] + }], + seat: 12345 + }] } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({adUnitType: admPayloadType}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const { validBR, bidderRequest } = createRequestMock({type}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; - return {serverResponse, data, bidderRequest}; + return {sR, data, bidderRequest}; } -// Unit tests -describe('adtrgtme Bid Adapter:', () => { +describe('Adtrgtme Bid Adapter:', () => { it('PLACEHOLDER TO PASS GULP', () => { - const obj = {}; - expect(obj).to.be.an('object'); + expect({}).to.be.an('object'); }); - describe('Validate basic properties', () => { - it('should define the correct bidder code', () => { + describe('check basic properties', () => { + it('should define bidder code', () => { expect(spec.code).to.equal('adtrgtme') }); }); - describe('getUserSyncs()', () => { - const IMAGE_PIXEL_URL = 'http://image-pixel.com/foo/bar?1234&baz=true'; - const IFRAME_ONE_URL = 'http://image-iframe.com/foo/bar?1234&baz=true'; - const IFRAME_TWO_URL = 'http://image-iframe-two.com/foo/bar?1234&baz=true'; + describe('getUserSyncs', () => { + const BAD_SYNC_URL = 'cdn.adtarget.me/libs/1x1.gif?image&rnd=5fr55r'; + const IMAGE_SYNC_URL = `${DEFAULT_PIXEL_URL}?image&rnd=5fr55r`; + const IFRAME_SYNC_ONE_URL = `${DEFAULT_PIXEL_URL}?iframe1&rnd=5fr55r`; + const IFRAME_SYNC_TWO_URL = `${DEFAULT_PIXEL_URL}?iframe2&rnd=5fr55r`; - let serverResponses = []; + let sRs = []; beforeEach(() => { - serverResponses[0] = { + sRs[0] = { body: { ext: { - pixels: `` + pixels: [ + ['image', BAD_SYNC_URL], + ['invalid', IMAGE_SYNC_URL], + ['image', IMAGE_SYNC_URL], + ['iframe', IFRAME_SYNC_ONE_URL], + ['iframe', IFRAME_SYNC_TWO_URL] + ] } } } }); after(() => { - serverResponses = undefined; + sRs = undefined; }); - it('for only iframe enabled syncs', () => { - let syncOptions = { + it('sync check bad url and type in pixels', () => { + let opt = { + iframeEnabled: true, + pixelEnabled: true + }; + let pixels = spec.getUserSyncs(opt, sRs); + expect(pixels.length).to.equal(3); + }); + + it('sync check for iframe only', () => { + let opt = { iframeEnabled: true, pixelEnabled: false }; - let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); - expect(pixelsObjects.length).to.equal(2); - expect(pixelsObjects).to.deep.equal( + let pixels = spec.getUserSyncs(opt, sRs); + expect(pixels.length).to.equal(2); + expect(pixels).to.deep.equal( [ - {type: 'iframe', 'url': IFRAME_ONE_URL}, - {type: 'iframe', 'url': IFRAME_TWO_URL} + {type: 'iframe', 'url': IFRAME_SYNC_ONE_URL}, + {type: 'iframe', 'url': IFRAME_SYNC_TWO_URL} ] ) }); - it('for only pixel enabled syncs', () => { - let syncOptions = { + it('sync check for image only', () => { + let opt = { iframeEnabled: false, pixelEnabled: true }; - let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); - expect(pixelsObjects.length).to.equal(1); - expect(pixelsObjects).to.deep.equal( + let pixels = spec.getUserSyncs(opt, sRs); + expect(pixels.length).to.equal(1); + expect(pixels).to.deep.equal( [ - {type: 'image', 'url': IMAGE_PIXEL_URL} + {type: 'image', 'url': IMAGE_SYNC_URL} ] ) }); - it('for both pixel and iframe enabled syncs', () => { - let syncOptions = { + it('Sync for iframe and image', () => { + let opt = { iframeEnabled: true, pixelEnabled: true }; - let pixelsObjects = spec.getUserSyncs(syncOptions, serverResponses); - expect(pixelsObjects.length).to.equal(3); - expect(pixelsObjects).to.deep.equal( + let pixels = spec.getUserSyncs(opt, sRs); + expect(pixels.length).to.equal(3); + expect(pixels).to.deep.equal( [ - {type: 'iframe', 'url': IFRAME_ONE_URL}, - {type: 'image', 'url': IMAGE_PIXEL_URL}, - {type: 'iframe', 'url': IFRAME_TWO_URL} + {type: 'image', 'url': IMAGE_SYNC_URL}, + {type: 'iframe', 'url': IFRAME_SYNC_ONE_URL}, + {type: 'iframe', 'url': IFRAME_SYNC_TWO_URL} ] ) }); }); - // Validate Bid Requests - describe('isBidRequestValid()', () => { - const INVALID_INPUT = [ + describe('Check if bid request is OK', () => { + const BAD_VALUE = [ {}, {params: {}}, - {params: {sid: '1234', zid: '4321'}}, - {params: {sid: '1220291391', zid: 4321}}, + {params: {sid: 1220291391, zid: '1836455615'}}, + {params: {sid: '1220291391', zid: 'A'}}, + {params: {sid: '', zid: '1836455615'}}, + {params: {sid: '', zid: 'A'}}, {params: {zid: ''}}, - {params: {sid: '', zid: ''}}, ]; - INVALID_INPUT.forEach(input => { - it(`should determine that the bid is INVALID for the input ${JSON.stringify(input)}`, () => { - expect(spec.isBidRequestValid(input)).to.be.false; + BAD_VALUE.forEach(value => { + it(`should determine bad bid for ${JSON.stringify(value)}`, () => { + expect(spec.isOK(value)).to.be.false; }); }); - it('should determine that the bid is VALID if sid and zid are present on the params object', () => { - const validBid = { - params: { - sid: 1220291391, - zid: 1836455615 - } - }; - expect(spec.isBidRequestValid(validBid)).to.be.true; + const OK_VALUE = [ + {params: {sid: '1220291391'}}, + {params: {sid: '1220291391', zid: 1836455615}}, + {params: {sid: '1220291391', zid: '1836455615'}}, + {params: {sid: '1220291391', zid: '1836455615A'}}, + ]; + + OK_VALUE.forEach(value => { + it(`should determine OK bid for ${JSON.stringify(value)}`, () => { + expect(spec.isOK(value)).to.be.true; + }); }); }); - describe('Price Floor module support:', () => { + describe('Bidfloor support:', () => { it('should get bidfloor from getFloor method', () => { - const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); - bidRequest.params.bidOverride = {cur: 'EUR'}; + const { bidRequest, validBR, bidderRequest } = createRequestMock({}); + bidRequest.params.bidOverride = {cur: 'AUD'}; bidRequest.getFloor = (floorObj) => { return { floor: bidRequest.floors.values[floorObj.mediaType + '|300x250'], @@ -252,203 +241,194 @@ describe('adtrgtme Bid Adapter:', () => { } }; bidRequest.floors = { - currency: 'EUR', + currency: 'AUD', values: { - 'banner|300x250': 5.55 + 'banner|300x250': 1.111 } }; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.cur).to.deep.equal(['EUR']); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.cur).to.deep.equal(['AUD']); expect(data.imp[0].bidfloor).is.a('number'); - expect(data.imp[0].bidfloor).to.equal(5.55); + expect(data.imp[0].bidfloor).to.equal(1.111); }); }); - describe('Schain module support:', () => { - it('should send Global or Bidder specific schain', function () { - const { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); + describe('Schain support:', () => { + it('should send schains', function () { + const { bidRequest, validBR, bidderRequest } = createRequestMock({}); const globalSchain = { ver: '1.0', complete: 1, nodes: [{ - asi: 'some-platform.com', - sid: '111111', + asi: 'adtarget-partner.com', + sid: '1234567890', rid: bidRequest.bidId, hp: 1 }] }; bidRequest.schain = globalSchain; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const data = spec.buildRequests(validBR, bidderRequest)[0].data; const schain = data.source.ext.schain; expect(schain.nodes.length).to.equal(1); expect(schain).to.equal(globalSchain); }); }); - describe('First party data module - "Site" support (ortb2):', () => { - // Should not allow invalid "site" data types - const INVALID_ORTB2_TYPES = [ null, [], 123, 'unsupportedKeyName', true, false, undefined ]; - INVALID_ORTB2_TYPES.forEach(param => { - it(`should not allow invalid site types to be added to bid-request: ${JSON.stringify(param)}`, () => { - const ortb2 = { site: param } - const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.site[param]).to.be.undefined; + describe('Check Site obj support (ortb2):', () => { + const BAD_ORTB2_TYPES = [ null, [], 123, 'invalidID', true, false, undefined ]; + BAD_ORTB2_TYPES.forEach(key => { + it(`should remove bad site data: ${JSON.stringify(key)}`, () => { + const ortb2 = { site: key } + const { validBR, bidderRequest } = createRequestMock({ortb2}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.site[key]).to.be.undefined; }); }); - // Should add valid "site" params - const VALID_SITE_STRINGS = ['name', 'domain', 'page', 'ref', 'keywords']; - const VALID_SITE_ARRAYS = ['cat', 'sectioncat', 'pagecat']; + const OK_SITE_STR = ['id', 'name', 'domain', 'page', 'ref', 'keywords']; + const OK_SITE_ARR = ['cat', 'sectioncat', 'pagecat']; - VALID_SITE_STRINGS.forEach(param => { - it(`should allow supported site keys to be added bid-request: ${JSON.stringify(param)}`, () => { + OK_SITE_STR.forEach(key => { + it(`should allow supported site keys to be added bid request: ${JSON.stringify(key)}`, () => { const ortb2 = { site: { - [param]: 'something' + [key]: 'some value here' } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.site[param]).to.exist; - expect(data.site[param]).to.be.a('string'); - expect(data.site[param]).to.be.equal(ortb2.site[param]); + const { validBR, bidderRequest } = createRequestMock({ortb2}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.site[key]).to.exist; + expect(data.site[key]).to.be.a('string'); + expect(data.site[key]).to.be.equal(ortb2.site[key]); }); }); - VALID_SITE_ARRAYS.forEach(param => { - it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + OK_SITE_ARR.forEach(key => { + it(`should determine valid keys of the ortb2 site and append to the bid request: ${JSON.stringify(key)}`, () => { const ortb2 = { site: { - [param]: ['something'] + [key]: ['some value here'] } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.site[param]).to.exist; - expect(data.site[param]).to.be.a('array'); - expect(data.site[param]).to.be.equal(ortb2.site[param]); + const { validBR, bidderRequest } = createRequestMock({ortb2}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.site[key]).to.exist; + expect(data.site[key]).to.be.a('array'); + expect(data.site[key]).to.be.equal(ortb2.site[key]); }); }); - // Should not allow invalid "site.content" data types - INVALID_ORTB2_TYPES.forEach(param => { - it(`should determine that the ortb2.site.content key is invalid and should not be added to bid-request: ${JSON.stringify(param)}`, () => { + BAD_ORTB2_TYPES.forEach(key => { + it(`should determine bad keys of the ortb2 site content key and should not be added to bid request: ${JSON.stringify(key)}`, () => { const ortb2 = { site: { - content: param + content: key } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const { validBR, bidderRequest } = createRequestMock({ortb2}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.site.content).to.be.undefined; }); }); - // Should not allow invalid "site.content" keys - it(`should not allow invalid ortb2.site.content object keys to be added to bid-request: {custom object}`, () => { + it(`should not allow bad ortb2.site.content keys to be added to bid request: {custom object}`, () => { const ortb2 = { site: { content: { fake: 'news', - unreal: 'param', + unreal: 'key', counterfit: 'data' } } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const { validBR, bidderRequest } = createRequestMock({ortb2}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.site.content).to.be.a('object'); }); - // Should append valid "site.content" keys - const VALID_CONTENT_STRINGS = ['id', 'title', 'language']; - VALID_CONTENT_STRINGS.forEach(param => { - it(`should determine that the ortb2.site String key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const OK_CONTENT_STR = ['id', 'title', 'language']; + OK_CONTENT_STR.forEach(key => { + it(`should determine that the ortb2.site String key is ok and append to the bid request: ${JSON.stringify(key)}`, () => { const ortb2 = { site: { content: { - [param]: 'something' + [key]: 'some value here' } } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.site.content[param]).to.exist; - expect(data.site.content[param]).to.be.a('string'); - expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); + const { validBR, bidderRequest } = createRequestMock({ortb2}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.site.content[key]).to.exist; + expect(data.site.content[key]).to.be.a('string'); + expect(data.site.content[key]).to.be.equal(ortb2.site.content[key]); }); }); - const VALID_CONTENT_ARRAYS = ['cat']; - VALID_CONTENT_ARRAYS.forEach(param => { - it(`should determine that the ortb2.site Array key is valid and append to the bid-request: ${JSON.stringify(param)}`, () => { + const OK_CONTENT_ARR = ['cat']; + OK_CONTENT_ARR.forEach(key => { + it(`should determine that the ortb2.site key is ok and append to the bid request: ${JSON.stringify(key)}`, () => { const ortb2 = { site: { content: { - [param]: ['something', 'something-else'] + [key]: ['some value here', 'something-else'] } } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.site.content[param]).to.be.a('array'); - expect(data.site.content[param]).to.be.equal(ortb2.site.content[param]); + const { validBR, bidderRequest } = createRequestMock({ortb2}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.site.content[key]).to.be.a('array'); + expect(data.site.content[key]).to.be.equal(ortb2.site.content[key]); }); }); }); - describe('First party data module - "User" support (ortb2):', () => { - // Global ortb2.user validations - // Should not allow invalid "user" data types - const INVALID_ORTB2_TYPES = [ null, [], 'unsupportedKeyName', true, false, undefined ]; - INVALID_ORTB2_TYPES.forEach(param => { - it(`should not allow invalid site types to be added to bid-request: ${JSON.stringify(param)}`, () => { - const ortb2 = { user: param } - const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.user[param]).to.be.undefined; + describe('Check ortb2 user support:', () => { + const BAD_ORTB2_TYPES = [ null, [], 'unsupportedKeyName', true, false, undefined ]; + BAD_ORTB2_TYPES.forEach(key => { + it(`should not allow bad site types to be added to bid request: ${JSON.stringify(key)}`, () => { + const ortb2 = { user: key } + const { validBR, bidderRequest } = createRequestMock({ortb2}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.user[key]).to.be.undefined; }); }); - // Should add valid "user" params - const VALID_USER_STRINGS = ['id', 'buyeruid', 'gender', 'keywords', 'customdata']; - VALID_USER_STRINGS.forEach(param => { - it(`should allow supported user string keys to be added bid-request: ${JSON.stringify(param)}`, () => { + const OK_USER_STR = ['id', 'buyeruid', 'gender', 'keywords', 'customdata']; + OK_USER_STR.forEach(key => { + it(`should allow valid keys of the user to be added to bid request: ${JSON.stringify(key)}`, () => { const ortb2 = { user: { - [param]: 'something' + [key]: 'some value here' } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.user[param]).to.exist; - expect(data.user[param]).to.be.a('string'); - expect(data.user[param]).to.be.equal(ortb2.user[param]); + const { validBR, bidderRequest } = createRequestMock({ortb2}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.user[key]).to.exist; + expect(data.user[key]).to.be.a('string'); + expect(data.user[key]).to.be.equal(ortb2.user[key]); }); }); - const VALID_USER_OBJECTS = ['ext']; - VALID_USER_OBJECTS.forEach(param => { - it(`should allow supported user extObject keys to be added to the bid-request: ${JSON.stringify(param)}`, () => { + const OK_USER_OBJECTS = ['ext']; + OK_USER_OBJECTS.forEach(key => { + it(`should allow user ext to be added to the bid request: ${JSON.stringify(key)}`, () => { const ortb2 = { user: { - [param]: {a: '123', b: '456'} + [key]: {a: '123', b: '456'} } }; - const { validBidRequests, bidderRequest } = generateBuildRequestMock({ortb2}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.user[param]).to.exist; - expect(data.user[param]).to.be.a('object'); - expect(data.user[param]).to.be.deep.include({[param]: {a: '123', b: '456'}}); + const { validBR, bidderRequest } = createRequestMock({ortb2}); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.user[key]).to.exist; + expect(data.user[key]).to.be.a('object'); + expect(data.user[key]).to.be.deep.include({[key]: {a: '123', b: '456'}}); config.setConfig({ortb2: {}}); }); }); - // adUnit.ortb2Imp.ext.data - it(`should allow adUnit.ortb2Imp.ext.data object to be added to the bid-request`, () => { - let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) - validBidRequests[0].ortb2Imp = { + it(`should allow adUnit.ortb2Imp.ext.data object to be added to the bid request`, () => { + let { validBR, bidderRequest } = createRequestMock({}) + validBR[0].ortb2Imp = { ext: { data: { pbadslot: 'homepage-top-rect', @@ -456,43 +436,42 @@ describe('adtrgtme Bid Adapter:', () => { } } }; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].ext.data).to.deep.equal(validBidRequests[0].ortb2Imp.ext.data); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.imp[0].ext.data).to.deep.equal(validBR[0].ortb2Imp.ext.data); }); - // adUnit.ortb2Imp.instl - it(`should allow adUnit.ortb2Imp.instl numeric boolean "1" to be added to the bid-request`, () => { - let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) - validBidRequests[0].ortb2Imp = { + it(`should allow adUnit.ortb2Imp.instl numeric boolean "1" to be added to the bid request`, () => { + let { validBR, bidderRequest } = createRequestMock({}) + validBR[0].ortb2Imp = { instl: 1 }; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - expect(data.imp[0].instl).to.deep.equal(validBidRequests[0].ortb2Imp.instl); + const data = spec.buildRequests(validBR, bidderRequest)[0].data; + expect(data.imp[0].instl).to.deep.equal(validBR[0].ortb2Imp.instl); }); - it(`should prevent adUnit.ortb2Imp.instl boolean "true" to be added to the bid-request`, () => { - let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) - validBidRequests[0].ortb2Imp = { + it(`should prevent adUnit.ortb2Imp.instl boolean "true" to be added to the bid request`, () => { + let { validBR, bidderRequest } = createRequestMock({}) + validBR[0].ortb2Imp = { instl: true }; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.imp[0].instl).to.not.exist; }); - it(`should prevent adUnit.ortb2Imp.instl boolean "false" to be added to the bid-request`, () => { - let { validBidRequests, bidderRequest } = generateBuildRequestMock({}) - validBidRequests[0].ortb2Imp = { + it(`should prevent adUnit.ortb2Imp.instl boolean false to be added to the bid request`, () => { + let { validBR, bidderRequest } = createRequestMock({}) + validBR[0].ortb2Imp = { instl: false }; - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.imp[0].instl).to.not.exist; }); }); - describe('GDPR & Consent:', () => { + describe('GDPR:', () => { it('should return request objects that do not send cookies if purpose 1 consent is not provided', () => { - const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + const { validBR, bidderRequest } = createRequestMock({}); bidderRequest.gdprConsent = { - consentString: 'BOtmiBKOtmiBKABABAENAFAAAAACeAAA', + consentString: 'BOtmiBKO234234tmiBKABABAEN234AFAAAAACeAAA', apiVersion: 2, vendorData: { purpose: { @@ -503,22 +482,22 @@ describe('adtrgtme Bid Adapter:', () => { }, gdprApplies: true }; - const options = spec.buildRequests(validBidRequests, bidderRequest)[0].options; - expect(options.withCredentials).to.be.false; + const opt = spec.buildRequests(validBR, bidderRequest)[0].options; + expect(opt.withCredentials).to.be.false; }); }); - describe('Endpoint & Impression Request Mode:', () => { + describe('Endpoint & Impression request mode:', () => { it('should route request to config override endpoint', () => { - const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const sid = validBidRequests[0].params.sid; - const testOverrideEndpoint = 'http://new_bidder_host.com/ssp?s='; + const { validBR, bidderRequest } = createRequestMock({}); + const sid = validBR[0].params.sid; + const testOverrideEndpoint = 'http://partner-adserv-domain.com/ssp?s='; config.setConfig({ adtrgtme: { endpoint: testOverrideEndpoint } }); - const response = spec.buildRequests(validBidRequests, bidderRequest)[0]; + const response = spec.buildRequests(validBR, bidderRequest)[0]; expect(response).to.deep.include( { method: 'POST', @@ -530,9 +509,9 @@ describe('adtrgtme Bid Adapter:', () => { config.setConfig({ adtrgtme: {} }); - const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const sid = validBidRequests[0].params.sid; - const response = spec.buildRequests(validBidRequests, bidderRequest); + const { validBR, bidderRequest } = createRequestMock({}); + const sid = validBR[0].params.sid; + const response = spec.buildRequests(validBR, bidderRequest); expect(response[0]).to.deep.include({ method: 'POST', url: 'https://z.cdn.adtarget.market/ssp?prebid&s=' + sid @@ -540,13 +519,10 @@ describe('adtrgtme Bid Adapter:', () => { }); it('should return a single request object for single request mode', () => { - let { bidRequest, validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const BID_ID_2 = '84ab50xxxxx'; - const BID_ZID_2 = 98876543210; - const AD_UNIT_CODE_2 = 'test-ad-unit-code-123'; - const { bidRequest: bidRequest2 } = generateBuildRequestMock({bidId: BID_ID_2, zid: BID_ZID_2, adUnitCode: AD_UNIT_CODE_2}); - validBidRequests = [bidRequest, bidRequest2]; - bidderRequest.bids = validBidRequests; + let { bidRequest, validBR, bidderRequest } = createRequestMock({}); + const { bidRequest: mock } = createRequestMock({bidId: '6heos7ks8z0j', zid: '98876543210', adUnitCode: 'bidder-code'}); + validBR = [bidRequest, mock]; + bidderRequest.bids = validBR; config.setConfig({ adtrgtme: { @@ -554,60 +530,57 @@ describe('adtrgtme Bid Adapter:', () => { } }); - const data = spec.buildRequests(validBidRequests, bidderRequest).data; + const data = spec.buildRequests(validBR, bidderRequest).data; expect(data.imp).to.be.an('array').with.lengthOf(2); expect(data.imp[0]).to.deep.include({ - id: DEFAULT_BID_ID, + id: '84ab500420319d', ext: { - dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE + dfp_ad_unit_code: '/1220291391/banner' } }); expect(data.imp[1]).to.deep.include({ - id: BID_ID_2, - tagid: BID_ZID_2, + id: '6heos7ks8z0j', + tagid: '98876543210', ext: { - dfp_ad_unit_code: AD_UNIT_CODE_2 + dfp_ad_unit_code: 'bidder-code' } }); }); }); - describe('Validate request filtering:', () => { - it('should not return request when no bids are present', function () { + describe('validate request filtering:', () => { + it('should return undefined when no bids', function () { let request = spec.buildRequests([]); expect(request).to.be.undefined; }); - it('buildRequests(): should return an array with the correct amount of request objects', () => { - const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const response = spec.buildRequests(validBidRequests, bidderRequest).bidderRequest; + it('buildRequests should return correct amount of objects', () => { + const { validBR, bidderRequest } = createRequestMock({}); + const response = spec.buildRequests(validBR, bidderRequest).bidderRequest; expect(response.bids).to.be.an('array').to.have.lengthOf(1); }); }); - describe('Request Headers validation:', () => { - it('should return request objects with the relevant custom headers and content type declaration', () => { - const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); + describe('Validate request headers:', () => { + it('should return request objects with the custom headers and content type', () => { + const { validBR, bidderRequest } = createRequestMock({}); bidderRequest.gdprConsent.gdprApplies = false; - const options = spec.buildRequests(validBidRequests, bidderRequest).options; - expect(options).to.deep.equal( + const opt = spec.buildRequests(validBR, bidderRequest).options; + expect(opt).to.deep.equal( { contentType: 'application/json', - customHeaders: { - 'x-openrtb-version': '2.5' - }, withCredentials: true }); }); }); - 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; + describe('Request data oRTB bid validation:', () => { + it('should create valid oRTB bid request object in the data field', () => { + const { validBR, bidderRequest } = createRequestMock({}); + const data = spec.buildRequests(validBR, bidderRequest).data; expect(data.site).to.deep.include({ id: bidderRequest.bids[0].params.sid, page: bidderRequest.refererInfo.page @@ -629,12 +602,8 @@ describe('adtrgtme Bid Adapter:', () => { expect(data.source).to.deep.equal({ ext: { hb: 1, - adapterver: ADAPTER_VERSION, - prebidver: PREBID_VERSION, - integration: { - name: INTEGRATION_METHOD, - ver: PREBID_VERSION - } + bidderver: BIDDER_VERSION, + prebidjsver: PREBIDJS_VERSION }, fd: 1 }); @@ -642,52 +611,49 @@ describe('adtrgtme Bid Adapter:', () => { expect(data.cur).to.deep.equal(['USD']); }); - 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; + it('should create valid oRTB imp.ext in the bid request', () => { + const { validBR, bidderRequest } = createRequestMock({}); + const data = spec.buildRequests(validBR, bidderRequest).data; expect(data.imp[0].ext).to.deep.equal({ - dfp_ad_unit_code: DEFAULT_AD_UNIT_CODE + dfp_ad_unit_code: '/1220291391/banner' }); }); - 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.sid = 9876543210; - const data = spec.buildRequests(validBidRequests, bidderRequest).data; - expect(data.site.id).to.equal(9876543210); + it('should use siteId value as site.id', () => { + let { validBR, bidderRequest } = createRequestMock({pubIdMode: true}); + validBR[0].params.sid = '9876543210'; + const data = spec.buildRequests(validBR, bidderRequest).data; + expect(data.site.id).to.equal('9876543210'); }); - it('should use placementId value as imp.tagid in the outbound bid-request when using "zid"', () => { - let { validBidRequests, bidderRequest } = generateBuildRequestMock({}), - TEST_ZID = 54321; - validBidRequests[0].params.zid = TEST_ZID; - const data = spec.buildRequests(validBidRequests, bidderRequest).data; + it('should use placementId value as imp.tagid when using "zid"', () => { + let { validBR, bidderRequest } = createRequestMock({}), + TEST_ZID = '54321'; + validBR[0].params.zid = TEST_ZID; + const data = spec.buildRequests(validBR, bidderRequest).data; expect(data.imp[0].tagid).to.deep.equal(TEST_ZID); }); }); - describe('Request Payload oRTB bid.imp validation:', () => { - // Validate Banner imp imp when adtrgtme.mode=undefined - it('should generate a valid "Banner" imp object', () => { + describe('Request oRTB bid.imp validation:', () => { + it('should create valid default Banner imp', () => { config.setConfig({ adtrgtme: {} }); - const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const { validBR, bidderRequest } = createRequestMock({}); + const data = spec.buildRequests(validBR, 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}] }); }); - // Validate Banner imp - it('should generate a valid "Banner" imp object', () => { + it('should create valid Banner imp', () => { config.setConfig({ adtrgtme: { mode: 'banner' } }); - const { validBidRequests, bidderRequest } = generateBuildRequestMock({}); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + const { validBR, bidderRequest } = createRequestMock({}); + const data = spec.buildRequests(validBR, 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}] @@ -697,104 +663,105 @@ describe('adtrgtme Bid Adapter:', () => { describe('interpretResponse()', () => { describe('for mediaTypes: "banner"', () => { - it('should insert banner payload into response[0].ad', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); - expect(response[0].ad).to.equal(''); + it('should insert banner object into response[0].ad', () => { + const { sR, bidderRequest } = createResponseMock('banner'); + const response = spec.interpretResponse(sR, {bidderRequest}); + expect(response[0].ad).to.equal(` + `); expect(response[0].mediaType).to.equal('banner'); }) }); - describe('Support Advertiser domains', () => { + describe('Support adomains', () => { it('should append bid-response adomain to meta.advertiserDomains', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); + const { sR, bidderRequest } = createResponseMock('banner'); + const response = spec.interpretResponse(sR, {bidderRequest}); expect(response[0].meta.advertiserDomains).to.be.a('array'); - expect(response[0].meta.advertiserDomains[0]).to.equal('advertiser-domain.com'); + expect(response[0].meta.advertiserDomains[0]).to.equal('some-advertiser-domain.com'); }) }); - describe('bid response Ad ID / Creative ID', () => { + describe('Check response Ad ID / Creative ID', () => { it('should use adId if it exists in the bid-response', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const { sR, bidderRequest } = createResponseMock('banner'); const adId = 'bid-response-adId'; - serverResponse.body.seatbid[0].bid[0].adId = adId; - const response = spec.interpretResponse(serverResponse, {bidderRequest}); + sR.body.seatbid[0].bid[0].adId = adId; + const response = spec.interpretResponse(sR, {bidderRequest}); expect(response[0].adId).to.equal(adId); }); it('should use impid if adId does not exist in the bid-response', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - const impid = '25b6c429c1f52f'; - serverResponse.body.seatbid[0].bid[0].impid = impid; - const response = spec.interpretResponse(serverResponse, {bidderRequest}); + const { sR, bidderRequest } = createResponseMock('banner'); + const impid = 'y7v7iu0uljj94rbjcw9'; + sR.body.seatbid[0].bid[0].impid = impid; + const response = spec.interpretResponse(sR, {bidderRequest}); expect(response[0].adId).to.equal(impid); }); it('should use crid if adId & impid do not exist in the bid-response', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const { sR, bidderRequest } = createResponseMock('banner'); const crid = 'passback-12579'; - serverResponse.body.seatbid[0].bid[0].impid = undefined; - serverResponse.body.seatbid[0].bid[0].crid = crid; - const response = spec.interpretResponse(serverResponse, {bidderRequest}); + sR.body.seatbid[0].bid[0].impid = undefined; + sR.body.seatbid[0].bid[0].crid = crid; + const response = spec.interpretResponse(sR, {bidderRequest}); expect(response[0].adId).to.equal(crid); }); }); describe('Time To Live (ttl)', () => { - const UNSUPPORTED_TTL_FORMATS = ['string', [1, 2, 3], true, false, null, undefined]; - UNSUPPORTED_TTL_FORMATS.forEach(param => { + const BAD_TTL_FORMATS = ['string', [1, 2, 3], true, false, null, undefined]; + BAD_TTL_FORMATS.forEach(key => { it('should not allow unsupported global adtrgtme.ttl formats and default to 300', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const { sR, bidderRequest } = createResponseMock('banner'); config.setConfig({ - adtrgtme: { ttl: param } + adtrgtme: { ttl: key } }); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); + const response = spec.interpretResponse(sR, {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}); + it('should not set unsupported ttl formats and check default to 300', () => { + const { sR, bidderRequest } = createResponseMock('banner'); + bidderRequest.bids[0].params.ttl = key; + const response = spec.interpretResponse(sR, {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 adtrgtme.ttl values 3600 < ttl < 0 and default to 300', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); + const BAD_TTL_VALUES = [-1, 12345]; + BAD_TTL_VALUES.forEach(key => { + it('should not set bad global adtrgtme.ttl and check default to 300', () => { + const { sR, bidderRequest } = createResponseMock('banner'); config.setConfig({ - adtrgtme: { ttl: param } + adtrgtme: { ttl: key } }); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); + const response = spec.interpretResponse(sR, {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}); + it('should not set bad keys.ttl values', () => { + const { sR, bidderRequest } = createResponseMock('banner'); + bidderRequest.bids[0].params.ttl = key; + const response = spec.interpretResponse(sR, {bidderRequest}); expect(response[0].ttl).to.equal(300); }); }); - it('should give presedence to Gloabl ttl over params.ttl ', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); + it('should set gloabl ttl over params.ttl if it presents', () => { + const { sR, bidderRequest } = createResponseMock('banner'); config.setConfig({ adtrgtme: { ttl: 500 } }); bidderRequest.bids[0].params.ttl = 400; - const response = spec.interpretResponse(serverResponse, {bidderRequest}); + const response = spec.interpretResponse(sR, {bidderRequest}); expect(response[0].ttl).to.equal(500); }); }); - describe('Aliasing support', () => { + describe('Alias support', () => { it('should return undefined as the bidder code value', () => { - const { serverResponse, bidderRequest } = generateResponseMock('banner'); - const response = spec.interpretResponse(serverResponse, {bidderRequest}); + const { sR, bidderRequest } = createResponseMock('banner'); + const response = spec.interpretResponse(sR, {bidderRequest}); expect(response[0].bidderCode).to.be.undefined; }); }); From bfdb1e167dce8c65097dac914ea6c0ad8e8efd43 Mon Sep 17 00:00:00 2001 From: tarasmatokhniuk Date: Fri, 3 Jan 2025 17:04:40 +0100 Subject: [PATCH 2/3] #12580 add fixes add ortb2.device.ip remove deepAccess refactor site object refactor user sync --- modules/adtrgtmeBidAdapter.js | 260 +++++++------------ test/spec/modules/adtrgtmeBidAdapter_spec.js | 33 +-- 2 files changed, 101 insertions(+), 192 deletions(-) diff --git a/modules/adtrgtmeBidAdapter.js b/modules/adtrgtmeBidAdapter.js index bd655820dac..9966279afae 100644 --- a/modules/adtrgtmeBidAdapter.js +++ b/modules/adtrgtmeBidAdapter.js @@ -1,25 +1,23 @@ -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER } from '../src/mediaTypes.js'; +import { registerBidder } from "../src/adapters/bidderFactory.js"; +import { BANNER } from "../src/mediaTypes.js"; import { - deepAccess, isFn, isStr, isNumber, - isArray, isEmpty, isPlainObject, generateUUID, logWarn, -} from '../src/utils.js'; -import { config } from '../src/config.js'; -import { hasPurpose1Consent } from '../src/utils/gdpr.js'; - -const BIDDER_CODE = 'adtrgtme'; -const BIDDER_VERSION = '1.0.4'; -const BIDDER_URL = 'https://z.cdn.adtarget.market/ssp?prebid&s='; -const PREBIDJS_VERSION = '$prebid.version$'; +} from "../src/utils.js"; +import { config } from "../src/config.js"; +import { hasPurpose1Consent } from "../src/utils/gdpr.js"; + +const BIDDER_CODE = "adtrgtme"; +const BIDDER_VERSION = "1.0.5"; +const BIDDER_URL = "https://z.cdn.adtarget.market/ssp?prebid&s="; +const PREBIDJS_VERSION = "$prebid.version$"; const DEFAULT_TTL = 300; -const DEFAULT_CUR = 'USD'; +const DEFAULT_CUR = "USD"; function getFormat(s) { const parseSize = ([w, h]) => ({ w: parseInt(w, 10), h: parseInt(h, 10) }); @@ -28,66 +26,53 @@ function getFormat(s) { : s.map(parseSize); } -function getType(bid) { - return deepAccess(bid, 'mediaTypes.banner') ? BANNER : false; -} - -function appObj( - checker, - keys, - obj, - appObj -) { - const res = { - ...appObj, - }; - if (keys.length > 0 && typeof checker === 'function') { - for (const oKey in obj) { - if ( - keys.indexOf(oKey) !== -1 && - checker(obj[oKey]) - ) { - res[oKey] = obj[oKey]; - } - } - } - return res; +function getType(b) { + return b?.mediaTypes?.banner ? BANNER : false; } function getBidfloor(b) { - return isFn(b.getFloor) ? b.getFloor({size: '*', - currency: deepAccess(b, 'params.bidOverride.cur') ?? DEFAULT_CUR, - mediaType: BANNER}) : false + return isFn(b.getFloor) + ? b.getFloor({ + size: "*", + currency: b?.params?.bidOverride?.cur ?? DEFAULT_CUR, + mediaType: BANNER, + }) + : false; } -function getTtl(bR) { - const t = config.getConfig('adtrgtme.ttl'); - const validate = (t) => (isNumber(t) && t > 0 && t < 3000) ? t : DEFAULT_TTL; - return t - ? validate(t) - : validate(deepAccess(bR, 'params.ttl')); +function getTtl(b) { + const t = config.getConfig("adtrgtme.ttl"); + const validate = (t) => (isNumber(t) && t > 0 && t < 3000 ? t : DEFAULT_TTL); + return t ? validate(t) : validate(b?.params?.ttl); } function createORTB(bR, bid) { - if (!bR) return; - - const { currency = deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CUR } = getBidfloor(bR); - const ip = deepAccess(bid, 'params.bidOverride.device.ip') || deepAccess(bid, 'params.ext.ip'); + if (!bR || !bid) return; + + const { currency = bid.params?.bidOverride?.cur || DEFAULT_CUR } = + getBidfloor(bR); + const ip = + bid.params?.bidOverride?.device?.ip || + bid.ortb2?.device?.ip || + bid.params?.ext?.ip; + const site = bid.ortb2?.site || undefined; + const user = bid.ortb2?.user || undefined; const gdpr = bR.gdprConsent?.gdprApplies ? 1 : 0; - const consentString = gdpr ? bR.gdprConsent?.consentString : ''; - const usPrivacy = bR.uspConsent || ''; + const consentString = gdpr ? bR.gdprConsent?.consentString : ""; + const usPrivacy = bR.uspConsent || ""; let oR = { id: generateUUID(), cur: [currency], imp: [], site: { - page: deepAccess(bR, 'refererInfo.page'), - id: String(bid.params.sid), + id: String(bid.params?.sid), + page: bR.refererInfo?.page || "", + ...site, }, device: { - dnt: 0, - ua: navigator.userAgent, + dnt: bid?.params?.dnt ? 1 : 0, + ua: bid?.params?.ua || navigator.userAgent, ip, }, regs: { @@ -101,22 +86,20 @@ function createORTB(bR, bid) { hb: 1, bidderver: BIDDER_VERSION, prebidjsver: PREBIDJS_VERSION, - ...(deepAccess(bid, 'schain') && { schain: bid.schain }), + ...(bid?.schain && { schain: bid.schain }), }, fd: 1, }, user: { + ...user, ext: { consent: consentString, + ...(user?.ext || {}), }, }, }; - if (bR.ortb2) { - oR = appendSiteData(oR, bid); - } - - if (deepAccess(bid, 'schain')) { + if (bid?.schain) { oR.source.ext.schain.nodes[0].rid = oR.id; } @@ -127,114 +110,50 @@ function appendImp(bid, oRtb) { if (!oRtb || !bid) return; const type = getType(bid); - const { floor: bidfloor = 0, currency: bidfloorcur = '' } = getBidfloor(bid); - const overrideFloor = deepAccess(bid, 'params.bidOverride.imp.bidfloor') || bidfloor; - const overrideCurrency = deepAccess(bid, 'params.bidOverride.imp.bidfloorcur') || bidfloorcur; + const { floor: bidfloor = 0, currency: bidfloorcur = "" } = getBidfloor(bid); const impObject = { id: bid.bidId, secure: 1, - bidfloor: overrideFloor, - bidfloorcur: overrideCurrency, + bidfloor: bid?.params?.bidOverride?.imp?.bidfloor || bidfloor, + bidfloorcur: bid?.params?.bidOverride?.imp?.bidfloorcur || bidfloorcur, ext: { dfp_ad_unit_code: bid.adUnitCode, - ...(deepAccess(bid, 'ortb2Imp.ext.data') && isPlainObject(bid.ortb2Imp.ext.data) && { data: bid.ortb2Imp.ext.data }) + ...(bid?.ortb2Imp?.ext?.data && + isPlainObject(bid.ortb2Imp.ext.data) && { + data: bid.ortb2Imp.ext.data, + }), }, - ...(deepAccess(bid, 'params.zid') && { tagid: String(bid.params.zid) }), - ...(deepAccess(bid, 'ortb2Imp.instl') === 1 && { instl: 1 }), + ...(bid?.params?.zid && { tagid: String(bid.params.zid) }), + ...(bid?.ortb2Imp?.instl === 1 && { instl: 1 }), }; if (type === BANNER) { impObject.banner = { - mimes: bid.mediaTypes.banner.mimes || ['text/html', 'text/javascript', 'application/javascript', 'image/jpg'], + mimes: bid.mediaTypes.banner.mimes || [ + "text/html", + "text/javascript", + "application/javascript", + "image/jpg", + ], format: getFormat(bid.sizes), ...(bid.mediaTypes.banner.pos && { pos: bid.mediaTypes.banner.pos }), }; } oRtb.imp.push(impObject); -}; - -function appendSiteData(oR, bid) { - const site = deepAccess(bid.ortb2, 'site') || undefined; - const content = deepAccess(site, 'content') || undefined; - const user = deepAccess(bid.ortb2, 'user') || undefined; - - if (site && isPlainObject(site)) { - const keys = [ - 'id', - 'name', - 'domain', - 'page', - 'ref', - 'keywords', - ]; - oR.site = appObj( - isStr, - keys, - site, - oR.site - ); - oR.site = appObj( - isArray, - ['cat', 'sectioncat', 'pagecat'], - site, - oR.site - ); - oR.site = appObj( - isPlainObject, - ['ext'], - site, - oR.site - ); - } - - if (content && isPlainObject(content)) { - oR.site.content = appObj( - isStr, - ['id', 'title', 'language', 'keywords'], - content, - oR.site.content - ); - oR.site.content = appObj( - isArray, - ['cat'], - content, - oR.site.content - ); - } - - if (user && isPlainObject(user)) { - oR.user = appObj( - isStr, - [ - 'id', - 'buyeruid', - 'gender', - 'keywords', - 'customdata', - ], - user, - oR.user - ); - oR.user.ext = appObj( - isPlainObject, - ['ext'], - user, - oR.user.ext - ); - } - return oR; } function createRequest({ data, options, bidderRequest }) { return { - url: `${config.getConfig('adtrgtme.endpoint') || BIDDER_URL}${data.site?.id || ''}`, - method: 'POST', + url: `${config.getConfig("adtrgtme.endpoint") || BIDDER_URL}${ + data.site?.id || "" + }`, + method: "POST", data, options, bidderRequest, - } + }; } export const spec = { @@ -255,25 +174,23 @@ export const spec = { ) { return true; } else { - logWarn('Adtrgtme request invalid'); + logWarn("Adtrgtme request invalid"); return false; } }, buildRequests: function (bR, aR) { if (isEmpty(bR) || isEmpty(aR)) { - logWarn('Adtrgtme Adapter: buildRequests called with empty request'); + logWarn("Adtrgtme Adapter: buildRequests called with empty request"); return undefined; } const options = { - contentType: 'application/json', - withCredentials: hasPurpose1Consent( - aR.gdprConsent - ) + contentType: "application/json", + withCredentials: hasPurpose1Consent(aR.gdprConsent), }; - if (config.getConfig('adtrgtme.singleRequestMode') === true) { + if (config.getConfig("adtrgtme.singleRequestMode") === true) { const data = createORTB(aR, bR[0]); bR.forEach((bid) => { appendImp(bid, data); @@ -305,7 +222,7 @@ export const spec = { let b = sb.bid[0]; res.push({ - adId: deepAccess(b, 'adId') ? b.adId : b.impid || b.crid, + adId: b?.adId ? b.adId : b.impid || b.crid, ad: b.adm, adUnitCode: bidderRequest.adUnitCode, requestId: b.impid, @@ -330,30 +247,49 @@ export const spec = { return res; }, - getUserSyncs: function (options, sR) { + getUserSyncs: function (options, res, gdprConsent, uspConsent, gppConsent) { const s = []; if (!options.pixelEnabled && !options.iframeEnabled) { return s; } - if (Array.isArray(sR)) { - sR.forEach((response) => { + if (Array.isArray(res)) { + res.forEach((response) => { const p = response.body?.ext?.pixels; if (Array.isArray(p)) { p.forEach(([stype, url]) => { const type = stype.toLowerCase(); if ( - typeof url === 'string' && url.startsWith('http') && - (((type === 'image' || type === 'img') && options.pixelEnabled) || - (type === 'iframe' && options.iframeEnabled)) + typeof url === "string" && + url.startsWith("http") && + (((type === "image" || type === "img") && options.pixelEnabled) || + (type === "iframe" && options.iframeEnabled)) ) { - s.push({type, url}); + s.push({ type, url: addConsentParams(url) }); } }); } }); } + function addConsentParams(url) { + if (gdprConsent) { + url += `&gdpr=${gdprConsent.gdprApplies ? 1 : 0}&gdpr_consent=${ + encodeURIComponent(gdprConsent.consentString) || "" + }`; + } + if (uspConsent) { + url += `&us_privacy=${encodeURIComponent(uspConsent)}`; + } + if (gppConsent?.gppString && gppConsent?.applicableSections?.length) { + url += `&gpp=${encodeURIComponent( + gppConsent.gppString + )}&gpp_sid=${encodeURIComponent( + gppConsent.applicableSections?.join(",") + )}`; + } + return url; + } return s; - } + }, }; registerBidder(spec); diff --git a/test/spec/modules/adtrgtmeBidAdapter_spec.js b/test/spec/modules/adtrgtmeBidAdapter_spec.js index d9ca79a246b..a74844857ce 100644 --- a/test/spec/modules/adtrgtmeBidAdapter_spec.js +++ b/test/spec/modules/adtrgtmeBidAdapter_spec.js @@ -6,7 +6,7 @@ const DEFAULT_SID = '1220291391'; const DEFAULT_ZID = '1836455615'; const DEFAULT_PIXEL_URL = 'https://cdn.adtarget.me/libs/1x1.gif'; const DEFAULT_BANNER_URL = 'https://cdn.adtarget.me/libs/banner/300x250.jpg'; -const BIDDER_VERSION = '1.0.4'; +const BIDDER_VERSION = '1.0.5'; const PREBIDJS_VERSION = '$prebid.version$'; const createBidRequest = ({bidId, adUnitCode, bidOverride, zid, ortb2}) => { @@ -318,34 +318,6 @@ describe('Adtrgtme Bid Adapter:', () => { }); }); - BAD_ORTB2_TYPES.forEach(key => { - it(`should determine bad keys of the ortb2 site content key and should not be added to bid request: ${JSON.stringify(key)}`, () => { - const ortb2 = { - site: { - content: key - } - }; - const { validBR, bidderRequest } = createRequestMock({ortb2}); - const data = spec.buildRequests(validBR, bidderRequest)[0].data; - expect(data.site.content).to.be.undefined; - }); - }); - - it(`should not allow bad ortb2.site.content keys to be added to bid request: {custom object}`, () => { - const ortb2 = { - site: { - content: { - fake: 'news', - unreal: 'key', - counterfit: 'data' - } - } - }; - const { validBR, bidderRequest } = createRequestMock({ortb2}); - const data = spec.buildRequests(validBR, bidderRequest)[0].data; - expect(data.site.content).to.be.a('object'); - }); - const OK_CONTENT_STR = ['id', 'title', 'language']; OK_CONTENT_STR.forEach(key => { it(`should determine that the ortb2.site String key is ok and append to the bid request: ${JSON.stringify(key)}`, () => { @@ -421,7 +393,8 @@ describe('Adtrgtme Bid Adapter:', () => { const data = spec.buildRequests(validBR, bidderRequest)[0].data; expect(data.user[key]).to.exist; expect(data.user[key]).to.be.a('object'); - expect(data.user[key]).to.be.deep.include({[key]: {a: '123', b: '456'}}); + expect(data.user[key].a).to.be.equal('123'); + expect(data.user[key].b).to.be.equal('456'); config.setConfig({ortb2: {}}); }); }); From 2e263b458fb063a2c6a23d07669f4f2bb3eee430 Mon Sep 17 00:00:00 2001 From: tarasmatokhniuk Date: Fri, 3 Jan 2025 17:12:24 +0100 Subject: [PATCH 3/3] Fix linter --- modules/adtrgtmeBidAdapter.js | 72 +++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/modules/adtrgtmeBidAdapter.js b/modules/adtrgtmeBidAdapter.js index 9966279afae..ba30b17e3d1 100644 --- a/modules/adtrgtmeBidAdapter.js +++ b/modules/adtrgtmeBidAdapter.js @@ -1,5 +1,5 @@ -import { registerBidder } from "../src/adapters/bidderFactory.js"; -import { BANNER } from "../src/mediaTypes.js"; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER } from '../src/mediaTypes.js'; import { isFn, isStr, @@ -8,16 +8,16 @@ import { isPlainObject, generateUUID, logWarn, -} from "../src/utils.js"; -import { config } from "../src/config.js"; -import { hasPurpose1Consent } from "../src/utils/gdpr.js"; +} from '../src/utils.js'; +import { config } from '../src/config.js'; +import { hasPurpose1Consent } from '../src/utils/gdpr.js'; -const BIDDER_CODE = "adtrgtme"; -const BIDDER_VERSION = "1.0.5"; -const BIDDER_URL = "https://z.cdn.adtarget.market/ssp?prebid&s="; -const PREBIDJS_VERSION = "$prebid.version$"; +const BIDDER_CODE = 'adtrgtme'; +const BIDDER_VERSION = '1.0.5'; +const BIDDER_URL = 'https://z.cdn.adtarget.market/ssp?prebid&s='; +const PREBIDJS_VERSION = '$prebid.version$'; const DEFAULT_TTL = 300; -const DEFAULT_CUR = "USD"; +const DEFAULT_CUR = 'USD'; function getFormat(s) { const parseSize = ([w, h]) => ({ w: parseInt(w, 10), h: parseInt(h, 10) }); @@ -33,15 +33,15 @@ function getType(b) { function getBidfloor(b) { return isFn(b.getFloor) ? b.getFloor({ - size: "*", - currency: b?.params?.bidOverride?.cur ?? DEFAULT_CUR, - mediaType: BANNER, - }) + size: '*', + currency: b?.params?.bidOverride?.cur ?? DEFAULT_CUR, + mediaType: BANNER, + }) : false; } function getTtl(b) { - const t = config.getConfig("adtrgtme.ttl"); + const t = config.getConfig('adtrgtme.ttl'); const validate = (t) => (isNumber(t) && t > 0 && t < 3000 ? t : DEFAULT_TTL); return t ? validate(t) : validate(b?.params?.ttl); } @@ -58,8 +58,8 @@ function createORTB(bR, bid) { const site = bid.ortb2?.site || undefined; const user = bid.ortb2?.user || undefined; const gdpr = bR.gdprConsent?.gdprApplies ? 1 : 0; - const consentString = gdpr ? bR.gdprConsent?.consentString : ""; - const usPrivacy = bR.uspConsent || ""; + const consentString = gdpr ? bR.gdprConsent?.consentString : ''; + const usPrivacy = bR.uspConsent || ''; let oR = { id: generateUUID(), @@ -67,7 +67,7 @@ function createORTB(bR, bid) { imp: [], site: { id: String(bid.params?.sid), - page: bR.refererInfo?.page || "", + page: bR.refererInfo?.page || '', ...site, }, device: { @@ -110,7 +110,7 @@ function appendImp(bid, oRtb) { if (!oRtb || !bid) return; const type = getType(bid); - const { floor: bidfloor = 0, currency: bidfloorcur = "" } = getBidfloor(bid); + const { floor: bidfloor = 0, currency: bidfloorcur = '' } = getBidfloor(bid); const impObject = { id: bid.bidId, @@ -131,10 +131,10 @@ function appendImp(bid, oRtb) { if (type === BANNER) { impObject.banner = { mimes: bid.mediaTypes.banner.mimes || [ - "text/html", - "text/javascript", - "application/javascript", - "image/jpg", + 'text/html', + 'text/javascript', + 'application/javascript', + 'image/jpg', ], format: getFormat(bid.sizes), ...(bid.mediaTypes.banner.pos && { pos: bid.mediaTypes.banner.pos }), @@ -146,10 +146,10 @@ function appendImp(bid, oRtb) { function createRequest({ data, options, bidderRequest }) { return { - url: `${config.getConfig("adtrgtme.endpoint") || BIDDER_URL}${ - data.site?.id || "" + url: `${config.getConfig('adtrgtme.endpoint') || BIDDER_URL}${ + data.site?.id || '' }`, - method: "POST", + method: 'POST', data, options, bidderRequest, @@ -174,23 +174,23 @@ export const spec = { ) { return true; } else { - logWarn("Adtrgtme request invalid"); + logWarn('Adtrgtme request invalid'); return false; } }, buildRequests: function (bR, aR) { if (isEmpty(bR) || isEmpty(aR)) { - logWarn("Adtrgtme Adapter: buildRequests called with empty request"); + logWarn('Adtrgtme Adapter: buildRequests called with empty request'); return undefined; } const options = { - contentType: "application/json", + contentType: 'application/json', withCredentials: hasPurpose1Consent(aR.gdprConsent), }; - if (config.getConfig("adtrgtme.singleRequestMode") === true) { + if (config.getConfig('adtrgtme.singleRequestMode') === true) { const data = createORTB(aR, bR[0]); bR.forEach((bid) => { appendImp(bid, data); @@ -259,10 +259,10 @@ export const spec = { p.forEach(([stype, url]) => { const type = stype.toLowerCase(); if ( - typeof url === "string" && - url.startsWith("http") && - (((type === "image" || type === "img") && options.pixelEnabled) || - (type === "iframe" && options.iframeEnabled)) + typeof url === 'string' && + url.startsWith('http') && + (((type === 'image' || type === 'img') && options.pixelEnabled) || + (type === 'iframe' && options.iframeEnabled)) ) { s.push({ type, url: addConsentParams(url) }); } @@ -273,7 +273,7 @@ export const spec = { function addConsentParams(url) { if (gdprConsent) { url += `&gdpr=${gdprConsent.gdprApplies ? 1 : 0}&gdpr_consent=${ - encodeURIComponent(gdprConsent.consentString) || "" + encodeURIComponent(gdprConsent.consentString) || '' }`; } if (uspConsent) { @@ -283,7 +283,7 @@ export const spec = { url += `&gpp=${encodeURIComponent( gppConsent.gppString )}&gpp_sid=${encodeURIComponent( - gppConsent.applicableSections?.join(",") + gppConsent.applicableSections?.join(',') )}`; } return url;