From 8864ae4301c881cb8baa17cab7f0d75827dda90e Mon Sep 17 00:00:00 2001 From: rishko00 <43280707+rishko00@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:09:13 +0300 Subject: [PATCH 01/92] add gpp support on Smartyads adapter (#10163) Co-authored-by: Vasyl Rishko --- modules/smartyadsBidAdapter.js | 9 ++++++--- test/spec/modules/smartyadsBidAdapter_spec.js | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/smartyadsBidAdapter.js b/modules/smartyadsBidAdapter.js index 89749aed4332..7dbd2f3993a0 100644 --- a/modules/smartyadsBidAdapter.js +++ b/modules/smartyadsBidAdapter.js @@ -66,6 +66,9 @@ export const spec = { if (bidderRequest.gdprConsent) { request.gdpr = bidderRequest.gdprConsent } + if (bidderRequest.gppConsent) { + request.gpp = bidderRequest.gppConsent; + } } const len = validBidRequests.length; @@ -102,19 +105,19 @@ export const spec = { return response; }, - getUserSyncs: (syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '') => { + getUserSyncs: (syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '', gppConsent = '') => { let syncs = []; let { gdprApplies, consentString = '' } = gdprConsent; if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&type=iframe&us_privacy=${uspConsent}` + url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&type=iframe&us_privacy=${uspConsent}&gpp=${gppConsent}` }); } else { syncs.push({ type: 'image', - url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&type=image&us_privacy=${uspConsent}` + url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&type=image&us_privacy=${uspConsent}&gpp=${gppConsent}` }); } diff --git a/test/spec/modules/smartyadsBidAdapter_spec.js b/test/spec/modules/smartyadsBidAdapter_spec.js index 3474753c838f..cbc4a6405e8e 100644 --- a/test/spec/modules/smartyadsBidAdapter_spec.js +++ b/test/spec/modules/smartyadsBidAdapter_spec.js @@ -243,7 +243,7 @@ describe('SmartyadsAdapter', function () { }); }); describe('getUserSyncs', function () { - const syncUrl = 'https://as.ck-ie.com/prebidjs?p=7c47322e527cf8bdeb7facc1bb03387a&gdpr=0&gdpr_consent=&type=iframe&us_privacy='; + const syncUrl = 'https://as.ck-ie.com/prebidjs?p=7c47322e527cf8bdeb7facc1bb03387a&gdpr=0&gdpr_consent=&type=iframe&us_privacy=&gpp='; const syncOptions = { iframeEnabled: true }; From cc1abfb889471b97ecc9399e14f0349dda9f22f2 Mon Sep 17 00:00:00 2001 From: SebRobert Date: Thu, 29 Jun 2023 13:48:39 +0200 Subject: [PATCH 02/92] beOpBidAdapter : Addition of bpsegs attribute to request payload from ORTB2 object (#10154) --- modules/beopBidAdapter.js | 12 ++++---- modules/beopBidAdapter.md | 36 +++++++++++++++++++++++- test/spec/modules/beopBidAdapter_spec.js | 20 +++++++++++-- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/modules/beopBidAdapter.js b/modules/beopBidAdapter.js index 8c85183e4262..c5282c28cfc7 100644 --- a/modules/beopBidAdapter.js +++ b/modules/beopBidAdapter.js @@ -48,8 +48,10 @@ export const spec = { */ buildRequests: function(validBidRequests, bidderRequest) { const slots = validBidRequests.map(beOpRequestSlotsMaker); - const firstPartyData = bidderRequest.ortb2; - const psegs = (firstPartyData && firstPartyData.user && firstPartyData.user.ext && firstPartyData.user.ext.data) ? firstPartyData.user.ext.data.permutive : undefined; + const firstPartyData = bidderRequest.ortb2 || {}; + const psegs = firstPartyData.user?.ext?.permutive || firstPartyData.user?.ext?.data?.permutive || []; + const userBpSegs = firstPartyData.user?.ext?.bpsegs || firstPartyData.user?.ext?.data?.bpsegs || []; + const siteBpSegs = firstPartyData.site?.ext?.bpsegs || firstPartyData.site?.ext?.data?.bpsegs || []; const pageUrl = getPageUrl(bidderRequest.refererInfo, window); const gdpr = bidderRequest.gdprConsent; const firstSlot = slots[0]; @@ -61,6 +63,8 @@ export const spec = { nid: firstSlot.nid, nptnid: firstSlot.nptnid, pid: firstSlot.pid, + psegs: psegs, + bpsegs: (userBpSegs.concat(siteBpSegs)).map(item => item.toString()), url: pageUrl, lang: (window.navigator.language || window.navigator.languages[0]), kwds: keywords, @@ -71,10 +75,6 @@ export const spec = { tc_string: (gdpr && gdpr.gdprApplies) ? gdpr.consentString : null, }; - if (psegs) { - Object.assign(payloadObject, {psegs: psegs}); - } - const payloadString = JSON.stringify(payloadObject); return { method: 'POST', diff --git a/modules/beopBidAdapter.md b/modules/beopBidAdapter.md index c0e88cb1cebc..53d2542b69c9 100644 --- a/modules/beopBidAdapter.md +++ b/modules/beopBidAdapter.md @@ -9,13 +9,14 @@ Module that connects to BeOp's demand sources # Test Parameters + ``` var adUnits = [ { code: 'in-article', mediaTypes: { banner: { - sizes: [[1,1]], + sizes: [[1,1]], } }, bids: [ @@ -31,3 +32,36 @@ Module that connects to BeOp's demand sources ]; ``` +# Custom Bidder data + +If you want to pass your first party data to BeOp, you can set your bidder `config.ortb2` object with + +```json +{ + "site": { + "ext": { + "bpsegs": ["Your", 1, "ST", "party", "data"], + "data": { + "bpsegs": ["Your", 1, "ST", "party", "data"] + } + } + }, + "user": { + "ext": { + "bpsegs": ["Your", 1, "ST", "party", "data"], + "data": { + "bpsegs": ["Your", 1, "ST", "party", "data"] + } + } + } +} +``` + +You can choose the location between: + +- `site.ext` +- `site.ext.data` +- `user.ext` +- `user.ext.data` + +and our BidAdapter will be able to find them diff --git a/test/spec/modules/beopBidAdapter_spec.js b/test/spec/modules/beopBidAdapter_spec.js index 22c48c2d182d..c77e304e539e 100644 --- a/test/spec/modules/beopBidAdapter_spec.js +++ b/test/spec/modules/beopBidAdapter_spec.js @@ -130,15 +130,15 @@ describe('BeOp Bid Adapter tests', () => { expect(payload.url).to.exist; // check that the protocol is added correctly expect(payload.url).to.equal('http://test.te'); - expect(payload.psegs).to.not.exist; }); - it('should call the endpoint with psegs data if any', function () { + it('should call the endpoint with psegs and bpsegs (stringified) data if any or [] if none', function () { let bidderRequest = { 'ortb2': { 'user': { 'ext': { + 'bpsegs': ['axed', 'axec', 1234], 'data': { 'permutive': [1234, 5678, 910] } @@ -154,6 +154,22 @@ describe('BeOp Bid Adapter tests', () => { expect(payload.psegs).to.include(5678); expect(payload.psegs).to.include(910); expect(payload.psegs).to.not.include(1); + expect(payload.bpsegs).to.exist; + expect(payload.bpsegs).to.include('axed'); + expect(payload.bpsegs).to.include('axec'); + expect(payload.bpsegs).to.include('1234'); + + let bidderRequest2 = + { + 'ortb2': {} + }; + + const request2 = spec.buildRequests(bidRequests, bidderRequest2); + const payload2 = JSON.parse(request2.data); + expect(payload2.psegs).to.exist; + expect(payload2.psegs).to.be.empty; + expect(payload2.bpsegs).to.exist; + expect(payload2.bpsegs).to.be.empty; }); it('should not prepend the protocol in page url if already present', function () { From 17dafeaf7bcefc489ae37436049a7cdfc8aa36fb Mon Sep 17 00:00:00 2001 From: EMX Digital <43830380+EMXDigital@users.noreply.github.com> Date: Wed, 5 Jul 2023 09:10:16 -0400 Subject: [PATCH 03/92] Cadent Aperture MX Bid Adapter: Change contact email address (#11) (#10160) Co-authored-by: Michael Denton --- modules/cadentApertureMXBidAdapter.md | 4 ++-- test/spec/modules/cadentApertureMXBidAdapter_spec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/cadentApertureMXBidAdapter.md b/modules/cadentApertureMXBidAdapter.md index 11d63134587a..d924f904be42 100644 --- a/modules/cadentApertureMXBidAdapter.md +++ b/modules/cadentApertureMXBidAdapter.md @@ -3,14 +3,14 @@ ``` Module Name: Cadent Aperture MX Adapter Module Type: Bidder Adapter -Maintainer: git@emxdigital.com +Maintainer: contactaperturemx@cadent.tv ``` # Description The Cadent Aperture MX adapter provides publishers with access to the Cadent Aperture MX SSP. The adapter is GDPR compliant. Please note that the adapter supports Banner and Video (Instream & Outstream) media types. -Note: The Cadent Aperture MX adapter requires approval and implementation guidelines from the Cadent team, including existing publishers that work with Cadent. Please reach out to your account manager or prebid@emxdigital.com for more information. +Note: The Cadent Aperture MX adapter requires approval and implementation guidelines from the Cadent team, including existing publishers that work with Cadent. Please reach out to your account manager or contactaperturemx@cadent.tv for more information. The bidder code should be ```cadent_aperture_mx``` The params used by the bidder are : diff --git a/test/spec/modules/cadentApertureMXBidAdapter_spec.js b/test/spec/modules/cadentApertureMXBidAdapter_spec.js index 091a81053541..eb127cfd9f38 100644 --- a/test/spec/modules/cadentApertureMXBidAdapter_spec.js +++ b/test/spec/modules/cadentApertureMXBidAdapter_spec.js @@ -699,7 +699,7 @@ describe('cadent_aperture_mx Adapter', function () { it('should not throw an error when decoding an improperly encoded adm', function () { const badAdmServerResponse = utils.deepClone(serverResponse); - badAdmServerResponse.seatbid[0].bid[0].adm = '\\<\\/script\\>'; + badAdmServerResponse.seatbid[0].bid[0].adm = '\\<\\/script\\>'; badAdmServerResponse.seatbid[1].bid[0].adm = '%3F%%3Dcadent%3C3prebid'; assert.doesNotThrow(() => spec.interpretResponse({ From 20f40a592e68ebc2f130fd5a5684f5247c61cbba Mon Sep 17 00:00:00 2001 From: msmeza Date: Wed, 5 Jul 2023 15:48:13 +0200 Subject: [PATCH 04/92] Livewrapped Analytics Adapter : collect the same data for timeouts and responses (#10180) * Livewrapped Analytics Adapter - Collect more data for timeout events * Removed trailing spaces --- modules/livewrappedAnalyticsAdapter.js | 66 +++++++++++++------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/modules/livewrappedAnalyticsAdapter.js b/modules/livewrappedAnalyticsAdapter.js index fe69220e123c..f3ee81cae7aa 100644 --- a/modules/livewrappedAnalyticsAdapter.js +++ b/modules/livewrappedAnalyticsAdapter.js @@ -168,7 +168,7 @@ livewrappedAnalyticsAdapter.sendEvents = function() { requests: sentRequests.sentRequests, responses: getResponses(sentRequests.gdpr, sentRequests.auctionIds), wins: getWins(sentRequests.gdpr, sentRequests.auctionIds), - timeouts: getTimeouts(sentRequests.auctionIds), + timeouts: getTimeouts(sentRequests.gdpr, sentRequests.auctionIds), bidAdUnits: getbidAdUnits(), rf: getAdRenderFailed(sentRequests.auctionIds), rcv: getAdblockerRecovered() @@ -237,27 +237,9 @@ function getResponses(gdpr, auctionIds) { if (bid.readyToSend && !(bid.sendStatus & RESPONSESENT) && !bid.timeout) { bid.sendStatus |= RESPONSESENT; - responses.push({ - timeStamp: auction.timeStamp, - adUnit: bid.adUnit, - adUnitId: bid.adUnitId, - bidder: bid.bidder, - width: bid.width, - height: bid.height, - cpm: bid.cpm, - orgCpm: bid.originalCpm, - ttr: bid.ttr, - IsBid: bid.isBid, - mediaType: bid.mediaType, - gdpr: gdprPos, - floor: bid.lwFloor ? bid.lwFloor : (bid.floorData ? bid.floorData.floorValue : undefined), - floorCur: bid.floorData ? bid.floorData.floorCurrency : undefined, - auctionId: auctionIdPos, - auc: bid.auc, - buc: bid.buc, - lw: bid.lw, - meta: bid.meta - }); + let response = getResponseObject(auction, bid, gdprPos, auctionIdPos); + + responses.push(response); } }); }); @@ -337,27 +319,45 @@ function getAuctionIdPos(auctionIds, auctionId) { return auctionIdPos; } -function getTimeouts(auctionIds) { +function getResponseObject(auction, bid, gdprPos, auctionIdPos) { + return { + timeStamp: auction.timeStamp, + adUnit: bid.adUnit, + adUnitId: bid.adUnitId, + bidder: bid.bidder, + width: bid.width, + height: bid.height, + cpm: bid.cpm, + orgCpm: bid.originalCpm, + ttr: bid.ttr, + IsBid: bid.isBid, + mediaType: bid.mediaType, + gdpr: gdprPos, + floor: bid.lwFloor ? bid.lwFloor : (bid.floorData ? bid.floorData.floorValue : undefined), + floorCur: bid.floorData ? bid.floorData.floorCurrency : undefined, + auctionId: auctionIdPos, + auc: bid.auc, + buc: bid.buc, + lw: bid.lw, + meta: bid.meta + }; +} + +function getTimeouts(gdpr, auctionIds) { var timeouts = []; Object.keys(cache.auctions).forEach(auctionId => { let auctionIdPos = getAuctionIdPos(auctionIds, auctionId); Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { let auction = cache.auctions[auctionId]; + let gdprPos = getGdprPos(gdpr, auction); let bid = auction.bids[bidId]; if (!(bid.sendStatus & TIMEOUTSENT) && bid.timeout) { bid.sendStatus |= TIMEOUTSENT; - timeouts.push({ - bidder: bid.bidder, - adUnit: bid.adUnit, - adUnitId: bid.adUnitId, - timeStamp: auction.timeStamp, - auctionId: auctionIdPos, - auc: bid.auc, - buc: bid.buc, - lw: bid.lw - }); + let timeout = getResponseObject(auction, bid, gdprPos, auctionIdPos); + + timeouts.push(timeout); } }); }); From eb18f80f0c2df5f0092fbd289851476a7b39682c Mon Sep 17 00:00:00 2001 From: Gena Date: Wed, 5 Jul 2023 17:37:10 +0300 Subject: [PATCH 05/92] Adtelligent adds GPP support (#10179) --- modules/adtelligentBidAdapter.js | 8 ++++++++ test/spec/modules/adtelligentBidAdapter_spec.js | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/modules/adtelligentBidAdapter.js b/modules/adtelligentBidAdapter.js index cbba1bb49eba..cab2b8956bc5 100644 --- a/modules/adtelligentBidAdapter.js +++ b/modules/adtelligentBidAdapter.js @@ -200,6 +200,14 @@ function bidToTag(bidRequests, adapterRequest) { tag.DMPId = window.adtDmp.getUID(); } + if (adapterRequest.gppConsent) { + tag.GPP = adapterRequest.gppConsent.gppString; + tag.GPPSid = adapterRequest.gppConsent.applicableSections?.toString(); + } else if (adapterRequest.ortb2?.regs?.gpp) { + tag.GPP = adapterRequest.ortb2.regs.gpp; + tag.GPPSid = adapterRequest.ortb2.regs.gpp_sid; + } + // end publisher env const bids = []; diff --git a/test/spec/modules/adtelligentBidAdapter_spec.js b/test/spec/modules/adtelligentBidAdapter_spec.js index 26284d539d46..e40828e6852a 100644 --- a/test/spec/modules/adtelligentBidAdapter_spec.js +++ b/test/spec/modules/adtelligentBidAdapter_spec.js @@ -156,6 +156,10 @@ const displayBidderRequestWithConsents = { gdprApplies: true, consentString: 'test' }, + gppConsent: { + gppString: 'abc12345234', + applicableSections: [7, 8] + }, uspConsent: 'iHaveIt' }; @@ -364,6 +368,10 @@ describe('adtelligentBidAdapter', () => { expect(bidRequestWithPubSettingsData.GDPR).to.be.equal(1); expect(bidRequestWithPubSettingsData.GDPRConsent).to.be.equal(displayBidderRequestWithConsents.gdprConsent.consentString); }); + it('sets GPP flags', () => { + expect(bidRequestWithPubSettingsData.GPP).to.be.equal(displayBidderRequestWithConsents.gppConsent.gppString); + expect(bidRequestWithPubSettingsData.GPPSid).to.be.equal('7,8'); + }); it('sets USP', () => { expect(bidRequestWithPubSettingsData.USP).to.be.equal(displayBidderRequestWithConsents.uspConsent); }) From 6537343d3d661bc89ee4c73330fcdec457798a4a Mon Sep 17 00:00:00 2001 From: amykwyang Date: Thu, 6 Jul 2023 08:31:32 -0400 Subject: [PATCH 06/92] IX Bid Adapter: Add 33Across to EID allowlist (#10182) Co-authored-by: Amy Yang --- modules/ixBidAdapter.js | 3 ++- test/spec/modules/ixBidAdapter_spec.js | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 3146223f23c1..bcdffa12e86b 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -76,7 +76,8 @@ const SOURCE_RTI_MAPPING = { 'audigent.com': '', // Hadron ID from Audigent, hadronId 'pubcid.org': '', // SharedID, pubcid 'utiq.com': '', // Utiq - 'intimatemerger.com': '' + 'intimatemerger.com': '', + '33across.com': '' }; const PROVIDERS = [ 'britepoolid', diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 2097354035e1..6b166b058031 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -759,7 +759,8 @@ describe('IndexexchangeAdapter', function () { uid2: { id: 'testuid2' }, // UID 2.0 // similar to uid2, but id5's getValue takes .uid id5id: { uid: 'testid5id' }, // ID5 - imuid: 'testimuid' + imuid: 'testimuid', + '33acrossId': { envelope: 'v1.5fs.1000.fjdiosmclds' } }; const DEFAULT_USERIDASEIDS_DATA = createEidsArray(DEFAULT_USERID_DATA); @@ -813,7 +814,12 @@ describe('IndexexchangeAdapter', function () { }, { source: 'intimatemerger.com', uids: [{ - id: DEFAULT_USERID_DATA.imuid, + id: DEFAULT_USERID_DATA.imuid + }] + }, { + source: '33across.com', + uids: [{ + id: DEFAULT_USERID_DATA['33acrossId'].envelope }] } ]; @@ -1219,7 +1225,7 @@ describe('IndexexchangeAdapter', function () { const payload = extractPayload(request[0]); expect(request).to.be.an('array'); expect(request).to.have.lengthOf.above(0); // should be 1 or more - expect(payload.user.eids).to.have.lengthOf(7); + expect(payload.user.eids).to.have.lengthOf(8); expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]); }); }); @@ -1407,7 +1413,7 @@ describe('IndexexchangeAdapter', function () { cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA); const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0]; const payload = extractPayload(request); - expect(payload.user.eids).to.have.lengthOf(7); + expect(payload.user.eids).to.have.lengthOf(8); expect(payload.user.eids).to.have.deep.members(DEFAULT_USERID_PAYLOAD); }); @@ -1540,7 +1546,7 @@ describe('IndexexchangeAdapter', function () { }) expect(payload.user).to.exist; - expect(payload.user.eids).to.have.lengthOf(9); + expect(payload.user.eids).to.have.lengthOf(10); expect(payload.user.eids).to.have.deep.members(validUserIdPayload); }); @@ -1582,7 +1588,7 @@ describe('IndexexchangeAdapter', function () { }); const payload = extractPayload(request); - expect(payload.user.eids).to.have.lengthOf(8); + expect(payload.user.eids).to.have.lengthOf(9); expect(payload.user.eids).to.have.deep.members(validUserIdPayload); }); }); From f3245b65c0a262f6d698f97861c6f81068d4e3ff Mon Sep 17 00:00:00 2001 From: Remi Henriot Date: Thu, 6 Jul 2023 14:39:39 +0200 Subject: [PATCH 07/92] Adagio Bid Adapter : add new boolean params (#10168) --- modules/adagioBidAdapter.js | 3 ++- test/spec/modules/adagioBidAdapter_spec.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index 57dd80aa9b1e..ca1891cac810 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -1152,7 +1152,8 @@ export const spec = { }, prebidVersion: '$prebid.version$', featuresVersion: FEATURES_VERSION, - usIfr: usIfr + usIfr: usIfr, + adgjs: storage.localStorageIsEnabled() }, options: { contentType: 'text/plain' diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 2fb40070184f..4b054824c7be 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -269,7 +269,8 @@ describe('Adagio bid adapter', () => { 'prebidVersion', 'featuresVersion', 'data', - 'usIfr' + 'usIfr', + 'adgjs', ]; it('groups requests by organizationId', function() { From bf1c860c91749e127a84ae61cb147432c1d82a38 Mon Sep 17 00:00:00 2001 From: Krzysztof Desput Date: Thu, 6 Jul 2023 14:52:36 +0200 Subject: [PATCH 08/92] Holid Bid Adapter: Add new fields to bid request, fix auctionId leak (#10153) --- modules/holidBidAdapter.js | 79 ++++++++++++++--------- test/spec/modules/holidBidAdapter_spec.js | 32 +++++++-- 2 files changed, 75 insertions(+), 36 deletions(-) diff --git a/modules/holidBidAdapter.js b/modules/holidBidAdapter.js index f41829bd123d..2073063168d8 100644 --- a/modules/holidBidAdapter.js +++ b/modules/holidBidAdapter.js @@ -1,4 +1,11 @@ -import {deepAccess, getBidIdParameter, isStr, logMessage, triggerPixel, } from '../src/utils.js'; +import { + deepAccess, + deepSetValue, + getBidIdParameter, + isStr, + logMessage, + triggerPixel, +} from '../src/utils.js'; import * as events from '../src/events.js'; import CONSTANTS from '../src/constants.json'; import {BANNER} from '../src/mediaTypes.js'; @@ -10,6 +17,7 @@ const GVLID = 1177 const ENDPOINT = 'https://helloworld.holid.io/openrtb2/auction' const COOKIE_SYNC_ENDPOINT = 'https://null.holid.io/sync.html' const TIME_TO_LIVE = 300 +const TMAX = 500 let wurlMap = {} events.on(CONSTANTS.EVENTS.BID_WON, bidWonHandler) @@ -23,12 +31,19 @@ export const spec = { return !!bid.params.adUnitID }, - buildRequests: function (validBidRequests, _bidderRequest) { + buildRequests: function (validBidRequests, bidderRequest) { return validBidRequests.map((bid) => { const requestData = { ...bid.ortb2, - id: _bidderRequest.bidderRequestId, + source: {schain: bid.schain}, + id: bidderRequest.bidderRequestId, imp: [getImp(bid)], + tmax: TMAX, + ...buildStoredRequest(bid) + } + + if (bid.userIdAsEids) { + deepSetValue(requestData, 'user.ext.eids', bid.userIdAsEids) } return { @@ -50,8 +65,6 @@ export const spec = { serverResponse.body.seatbid.map((response) => { response.bid.map((bid) => { const requestId = bidRequest.bidId - // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 - const auctionId = bidRequest.auctionId const wurl = deepAccess(bid, 'ext.prebid.events.win') const bidResponse = { requestId, @@ -65,7 +78,7 @@ export const spec = { ttl: TIME_TO_LIVE, } - addWurl({ auctionId, requestId, wurl }) + addWurl(requestId, wurl) bidResponses.push(bidResponse) }) @@ -75,11 +88,15 @@ export const spec = { }, getUserSyncs(optionsType, serverResponse, gdprConsent, uspConsent) { + const syncs = [{ + type: 'image', + url: 'https://track.adform.net/Serving/TrackPoint/?pm=2992097&lid=132720821' + }] + if (!serverResponse || serverResponse.length === 0) { - return [] + return syncs } - const syncs = [] const bidders = getBidders(serverResponse) if (optionsType.iframeEnabled && bidders) { @@ -100,24 +117,14 @@ export const spec = { type: 'iframe', url: COOKIE_SYNC_ENDPOINT + strQueryParams + '&type=iframe', }) - - return syncs } - return [] + return syncs }, } function getImp(bid) { - const imp = { - ext: { - prebid: { - storedrequest: { - id: getBidIdParameter('adUnitID', bid.params), - }, - }, - }, - } + const imp = buildStoredRequest(bid) const sizes = bid.sizes && !Array.isArray(bid.sizes[0]) ? [bid.sizes] : bid.sizes @@ -132,6 +139,18 @@ function getImp(bid) { return imp } +function buildStoredRequest(bid) { + return { + ext: { + prebid: { + storedrequest: { + id: getBidIdParameter('adUnitID', bid.params), + }, + }, + }, + } +} + function getBidders(serverResponse) { const bidders = serverResponse .map((res) => Object.keys(res.body.ext.responsetimemillis || [])) @@ -142,28 +161,28 @@ function getBidders(serverResponse) { } } -function addWurl(auctionId, adId, wurl) { - if ([auctionId, adId].every(isStr)) { - wurlMap[`${auctionId}${adId}`] = wurl +function addWurl(requestId, wurl) { + if (isStr(requestId)) { + wurlMap[requestId] = wurl } } -function removeWurl(auctionId, adId) { - delete wurlMap[`${auctionId}${adId}`] +function removeWurl(requestId) { + delete wurlMap[requestId] } -function getWurl(auctionId, adId) { - if ([auctionId, adId].every(isStr)) { - return wurlMap[`${auctionId}${adId}`] +function getWurl(requestId) { + if (isStr(requestId)) { + return wurlMap[requestId] } } function bidWonHandler(bid) { - const wurl = getWurl(bid.auctionId, bid.adId) + const wurl = getWurl(bid.requestId) if (wurl) { logMessage(`Invoking image pixel for wurl on BID_WIN: "${wurl}"`) triggerPixel(wurl) - removeWurl(bid.auctionId, bid.adId) + removeWurl(bid.requestId) } } diff --git a/test/spec/modules/holidBidAdapter_spec.js b/test/spec/modules/holidBidAdapter_spec.js index e55befd213a0..ef0283d0f2cb 100644 --- a/test/spec/modules/holidBidAdapter_spec.js +++ b/test/spec/modules/holidBidAdapter_spec.js @@ -2,11 +2,14 @@ import { expect } from 'chai' import { spec } from 'modules/holidBidAdapter.js' describe('holidBidAdapterTests', () => { + const bidderRequest = { + bidderRequestId: 'test-id' + } + const bidRequestData = { bidder: 'holid', adUnitCode: 'test-div', bidId: 'bid-id', - auctionId: 'test-id', params: { adUnitID: '12345' }, mediaTypes: { banner: {} }, sizes: [[300, 250]], @@ -48,18 +51,26 @@ describe('holidBidAdapterTests', () => { describe('buildRequests', () => { const bid = JSON.parse(JSON.stringify(bidRequestData)) - const request = spec.buildRequests([bid], bid) + const request = spec.buildRequests([bid], bidderRequest) const payload = JSON.parse(request[0].data) + it('should include id in request', () => { + expect(payload.id).to.equal('test-id') + }) + it('should include ext in imp', () => { - expect(payload.imp[0].ext).to.exist expect(payload.imp[0].ext).to.deep.equal({ prebid: { storedrequest: { id: '12345' } }, }) }) + it('should include ext in request', () => { + expect(payload.ext).to.deep.equal({ + prebid: { storedrequest: { id: '12345' } }, + }) + }) + it('should include banner format in imp', () => { - expect(payload.imp[0].banner).to.exist expect(payload.imp[0].banner).to.deep.equal({ format: [{ w: 300, h: 250 }], }) @@ -146,6 +157,10 @@ describe('holidBidAdapterTests', () => { } const uspConsent = 'mkjvbiniwot4827obfoy8sdg8203gb' const expectedUserSyncs = [ + { + type: 'image', + url: 'https://track.adform.net/Serving/TrackPoint/?pm=2992097&lid=132720821', + }, { type: 'iframe', url: 'https://null.holid.io/sync.html?bidders=%5B%22test%20seat%201%22%2C%22test%20seat%202%22%5D&gdpr=1&gdpr_consent=dkj49Sjmfjuj34as:12jaf90123hufabidfy9u23brfpoig&usp_consent=mkjvbiniwot4827obfoy8sdg8203gb&type=iframe', @@ -162,7 +177,7 @@ describe('holidBidAdapterTests', () => { expect(userSyncs).to.deep.equal(expectedUserSyncs) }) - it('should return empty user syncs when responsetimemillis is not defined', () => { + it('should return base user syncs when responsetimemillis is not defined', () => { const optionsType = { iframeEnabled: true, pixelEnabled: true, @@ -179,7 +194,12 @@ describe('holidBidAdapterTests', () => { consentString: 'dkj49Sjmfjuj34as:12jaf90123hufabidfy9u23brfpoig', } const uspConsent = 'mkjvbiniwot4827obfoy8sdg8203gb' - const expectedUserSyncs = [] + const expectedUserSyncs = [ + { + type: 'image', + url: 'https://track.adform.net/Serving/TrackPoint/?pm=2992097&lid=132720821', + } + ] const userSyncs = spec.getUserSyncs( optionsType, From b380fe2f40b042ede12d24607d96f1c4922a7491 Mon Sep 17 00:00:00 2001 From: BaronJHYu <254878848@qq.com> Date: Thu, 6 Jul 2023 20:57:04 +0800 Subject: [PATCH 09/92] Mediago / Discovery Bid Adapter : update param tagid (#10151) * Mediago Bid Adapter:new adapter * remove console * change spec file to fix CircleCI * change spec file to fix CircleCI * change spec file * Update mediagoBidAdapter.js * Update mediagoBidAdapter.js * rerun CurcleCi * update mediagoBidAdapter * update discoveryBidAdapter * Discovery Bid Adapter : parameter updates * Mediago Bid Adapter : parameter updates * Mediago Bid Adapter : code style format * rerun circleci * rerun circleci * rerun circleci * rerun circleci * Update mediagoBidAdapter & discoveryBidAdapter:report eids to server * Update mediagoBidAdapter & discoveryBidAdapter:report eids to server * update Mediago & Discovery BidAdapter:remove size filter * update Mediago & Discovery BidAdapter:code format * update Mediago & Discovery BidAdapter:code format * update Mediago & Discovery BidAdapter:add param in banner format * update mediago & discovery:first party data * update mediago & discovery:first party data * update mediago & discovery:first party data * fix(mediago & discovery): update param tagid * fix(mediago & discovery): update param tagid --------- Co-authored-by: BaronYu --- modules/discoveryBidAdapter.js | 2 +- modules/mediagoBidAdapter.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/discoveryBidAdapter.js b/modules/discoveryBidAdapter.js index 675ede273a6d..7ad75f642159 100644 --- a/modules/discoveryBidAdapter.js +++ b/modules/discoveryBidAdapter.js @@ -258,7 +258,7 @@ function getItems(validBidRequests, bidderRequest) { format: sizes, }, ext: {}, - tagid: globals['tagid'], + tagid: req.params && req.params.tagid }; } itemMaps[id] = { diff --git a/modules/mediagoBidAdapter.js b/modules/mediagoBidAdapter.js index 055fef3bb390..756e636572d9 100644 --- a/modules/mediagoBidAdapter.js +++ b/modules/mediagoBidAdapter.js @@ -242,6 +242,7 @@ function getItems(validBidRequests, bidderRequest) { ext: { // gpid: gpid, // 加入后无法返回广告 }, + tagid: req.params && req.params.tagid, }; itemMaps[id] = { req, From e7ac1b99a26b1c4194d24073ae7d7505c3d0ea06 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 6 Jul 2023 17:50:50 +0000 Subject: [PATCH 10/92] Prebid 8.3.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 741415e3f885..16ea2342665d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.3.0-pre", + "version": "8.3.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 491e5d88fb98..a8a98c995b1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.3.0-pre", + "version": "8.3.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 22b9c7019cbee26f5547bbd1d61742f8520e3dbf Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 6 Jul 2023 17:50:51 +0000 Subject: [PATCH 11/92] Increment version to 8.4.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 16ea2342665d..faec21d0499e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.3.0", + "version": "8.4.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index a8a98c995b1a..0c1cbd4850fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.3.0", + "version": "8.4.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From bdd0f9d082f0faa5f0f29e049528e368b38b9f08 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Fri, 7 Jul 2023 05:18:43 -0700 Subject: [PATCH 12/92] SharedId: use `sharedId` instead of `pubCommonId` for accessing storage (#10183) --- modules/sharedIdSystem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index ef56e10870ba..26f6f75b7a0e 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -13,7 +13,7 @@ import {VENDORLESS_GVLID} from '../src/consentHandler.js'; import {MODULE_TYPE_UID} from '../src/activities/modules.js'; import {domainOverrideToRootDomain} from '../libraries/domainOverrideToRootDomain/index.js'; -export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: 'pubCommonId'}); +export const storage = getStorageManager({moduleType: MODULE_TYPE_UID, moduleName: 'sharedId'}); const COOKIE = 'cookie'; const LOCAL_STORAGE = 'html5'; const OPTOUT_NAME = '_pubcid_optout'; From 7cf02748654193a8973f99310e3bc2297d89761f Mon Sep 17 00:00:00 2001 From: hybrid-ai <58724131+hybrid-ai@users.noreply.github.com> Date: Mon, 10 Jul 2023 08:57:00 +0300 Subject: [PATCH 13/92] Vox Bid Addapter: add schain, floors, userid support (#10109) * vox bid adapter: add schain, floors, userid support * vox bid adapter: add schain, floors, userid support * vox bid adapter: add schain, floors, userid support * vox bid adapter: add schain, floors, userid support * vox bid adapter: add schain, floors, userid support --- modules/voxBidAdapter.js | 21 ++++-- test/spec/modules/voxBidAdapter_spec.js | 93 +++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/modules/voxBidAdapter.js b/modules/voxBidAdapter.js index acf1cc9579c5..34bd46ccb98e 100644 --- a/modules/voxBidAdapter.js +++ b/modules/voxBidAdapter.js @@ -4,6 +4,9 @@ import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {find} from '../src/polyfill.js'; import {auctionManager} from '../src/auctionManager.js'; import {Renderer} from '../src/Renderer.js'; +import {config} from '../src/config.js' + +const { getConfig } = config; const BIDDER_CODE = 'vox'; const SSP_ENDPOINT = 'https://ssp.hybrid.ai/auction/prebid'; @@ -11,13 +14,21 @@ const VIDEO_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVi const TTL = 60; function buildBidRequests(validBidRequests) { - return _map(validBidRequests, function(validBidRequest) { - const params = validBidRequest.params; + return _map(validBidRequests, function(bid) { + const currency = getConfig('currency.adServerCurrency'); + const floorInfo = bid.getFloor ? bid.getFloor({ + currency: currency || 'USD' + }) : {}; + + const params = bid.params; const bidRequest = { - bidId: validBidRequest.bidId, + floorInfo, + schain: bid.schain, + userId: bid.userId, + bidId: bid.bidId, // TODO: fix transactionId leak: https://github.com/prebid/Prebid.js/issues/9781 - transactionId: validBidRequest.transactionId, - sizes: validBidRequest.sizes, + transactionId: bid.transactionId, + sizes: bid.sizes, placement: params.placement, placeId: params.placementId, imageUrl: params.imageUrl diff --git a/test/spec/modules/voxBidAdapter_spec.js b/test/spec/modules/voxBidAdapter_spec.js index 923b0465e6c0..5f4ada06c655 100644 --- a/test/spec/modules/voxBidAdapter_spec.js +++ b/test/spec/modules/voxBidAdapter_spec.js @@ -1,5 +1,6 @@ import { expect } from 'chai' import { spec } from 'modules/voxBidAdapter.js' +import {config} from 'src/config.js' function getSlotConfigs(mediaTypes, params) { return { @@ -175,6 +176,98 @@ describe('VOX Adapter', function() { expect(bid.transactionId).to.equal('31a58515-3634-4e90-9c96-f86196db1459') }) }) + it('should not set userid if not specified', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + data.bidRequests.forEach(bid => { + expect(bid.userId).to.be.undefined + }) + }) + + it('should set userid if specified', function () { + const requests = validBidRequests.map(bid => ({ + ...bid, + userId: { + tdid: 'TDID_USER_ID', + pubcid: 'PUBID_USER_ID' + } + })) + const request = spec.buildRequests(requests, bidderRequest) + const data = JSON.parse(request.data) + data.bidRequests.forEach(bid => { + expect(bid.userId.tdid).to.equal('TDID_USER_ID') + expect(bid.userId.pubcid).to.equal('PUBID_USER_ID') + }) + }) + + it('should not set schain if not specified', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + data.bidRequests.forEach(bid => { + expect(bid.schain).to.be.undefined + }) + }) + + it('should set schain if not specified', function () { + const requests = validBidRequests.map(bid => ({ + ...bid, + schain: { + validation: 'strict', + config: { + ver: '1.0' + } + } + })) + const request = spec.buildRequests(requests, bidderRequest) + const data = JSON.parse(request.data) + data.bidRequests.forEach(bid => { + expect(bid.schain.validation).to.equal('strict') + expect(bid.schain.config.ver).to.equal('1.0') + }) + }) + + describe('price floors', function () { + it('should be empty if floors module not configured', function () { + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + data.bidRequests.forEach(bid => { + expect(bid.floorInfo).to.be.empty + }) + }) + + it('should add correct floor values', function () { + const expectedFloors = [ 2, 2.7, 1.4 ] + const validBidRequests = expectedFloors.map(getBidWithFloor) + const request = spec.buildRequests(validBidRequests, bidderRequest) + const data = JSON.parse(request.data) + expectedFloors.forEach((floor, index) => { + expect(data.bidRequests[index].floorInfo.floor).to.equal(floor) + expect(data.bidRequests[index].floorInfo.currency).to.equal('USD') + }) + }) + + it('should request floor price in adserver currency', function () { + const configCurrency = 'DKK' + config.setConfig({ currency: { adServerCurrency: configCurrency } }) + const request = spec.buildRequests([ getBidWithFloor() ], bidderRequest) + const data = JSON.parse(request.data) + data.bidRequests.forEach(bid => { + expect(bid.floorInfo.currency).to.equal(configCurrency) + }) + }) + + function getBidWithFloor(floor) { + return { + ...validBidRequests[0], + getFloor: ({ currency }) => { + return { + currency: currency, + floor + } + } + } + } + }) describe('GDPR params', function() { describe('when there are not consent management platform', function() { From b735f0b646b42208ec234ba7d0da22a9df73142c Mon Sep 17 00:00:00 2001 From: Anand Venkatraman Date: Mon, 10 Jul 2023 02:18:36 -0400 Subject: [PATCH 14/92] PulsePoint Bid Adapter: deals support and refactoring to use ortbConverter (#10177) * ET-1691: Pulsepoint Analytics adapter for Prebid. (#1) * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: Adding pulsepoint analytics and tests for pulsepoint adapter * ET-1691: cleanup * ET-1691: minor * ET-1691: revert package.json change * Adding bidRequest to bidFactory.createBid method as per https://github.com/prebid/Prebid.js/issues/509 * ET-1765: Adding support for additional params in PulsePoint adapter (#2) * ET-1850: Fixing https://github.com/prebid/Prebid.js/issues/866 * Minor fix * Adding mandatory parameters to Bid * Switching to ortbConverter * updating docs --------- Co-authored-by: Eugene Rachitskiy --- modules/pulsepointBidAdapter.js | 487 ++---------- modules/pulsepointBidAdapter.md | 84 +-- .../spec/modules/pulsepointBidAdapter_spec.js | 711 ++++++------------ 3 files changed, 327 insertions(+), 955 deletions(-) diff --git a/modules/pulsepointBidAdapter.js b/modules/pulsepointBidAdapter.js index 015e80d56925..7297c9313269 100644 --- a/modules/pulsepointBidAdapter.js +++ b/modules/pulsepointBidAdapter.js @@ -1,21 +1,9 @@ -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; -/* eslint dot-notation:0, quote-props:0 */ -import {convertTypes, deepAccess, isArray, isFn, logError} from '../src/utils.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import {convertTypes, isArray} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {Renderer} from '../src/Renderer.js'; -const NATIVE_DEFAULTS = { - TITLE_LEN: 100, - DESCR_LEN: 200, - SPONSORED_BY_LEN: 50, - IMG_MIN: 150, - ICON_MIN: 50, -}; - -const DEFAULT_BID_TTL = 20; const DEFAULT_CURRENCY = 'USD'; -const DEFAULT_NET_REVENUE = true; -const KNOWN_PARAMS = ['cp', 'ct', 'cf', 'video', 'battr', 'bcat', 'badv', 'bidfloor']; +const KNOWN_PARAMS = ['cp', 'ct', 'cf', 'battr', 'deals']; const DEFAULT_TMAX = 500; /** @@ -41,33 +29,21 @@ export const spec = { ), buildRequests: (bidRequests, bidderRequest) => { - // convert Native ORTB definition to old-style prebid native definition - bidRequests = convertOrtbRequestToProprietaryNative(bidRequests); - - const request = { - id: bidRequests[0].bidderRequestId, - imp: bidRequests.map(slot => impression(slot)), - site: site(bidRequests, bidderRequest), - app: app(bidRequests), - device: device(), - bcat: deepAccess(bidderRequest.ortb2Imp, 'bcat') || bidRequests[0].params.bcat, - badv: bidRequests[0].params.badv, - user: user(bidRequests[0], bidderRequest), - regs: regs(bidderRequest), - source: source(bidRequests[0].schain), - tmax: bidderRequest.timeout || DEFAULT_TMAX, - }; + const data = converter.toORTB({bidRequests, bidderRequest}); return { method: 'POST', url: 'https://bid.contextweb.com/header/ortb?src=prebid', - data: request, + data, bidderRequest }; }, - interpretResponse: (response, request) => ( - bidResponseAvailable(request, response) - ), + interpretResponse: (response, request) => { + if (response.body) { + return converter.fromORTB({response: response.body, request: request.data}).bids; + } + return []; + }, getUserSyncs: syncOptions => { if (syncOptions.iframeEnabled) { @@ -82,7 +58,7 @@ export const spec = { }]; } }, - transformBidParams: function(params, isOpenRtb) { + transformBidParams: function(params) { return convertTypes({ 'cf': 'string', 'cp': 'number', @@ -91,124 +67,61 @@ export const spec = { } }; -/** - * Callback for bids, after the call to PulsePoint completes. - */ -function bidResponseAvailable(request, response) { - const idToImpMap = {}; - const idToBidMap = {}; - const idToSlotConfig = {}; - const bidResponse = response.body; - // extract the request bids and the response bids, keyed by impr-id - const ortbRequest = request.data; - ortbRequest.imp.forEach(imp => { - idToImpMap[imp.id] = imp; - }); - if (bidResponse) { - bidResponse.seatbid.forEach(seatBid => seatBid.bid.forEach(bid => { - idToBidMap[bid.impid] = bid; - })); - } - if (request.bidderRequest && request.bidderRequest.bids) { - request.bidderRequest.bids.forEach(bid => { - idToSlotConfig[bid.bidId] = bid; - }); - } - const bids = []; - Object.keys(idToImpMap).forEach(id => { - if (idToBidMap[id]) { - const bid = { - requestId: id, - cpm: idToBidMap[id].price, - creative_id: idToBidMap[id].crid, - creativeId: idToBidMap[id].crid, - adId: id, - ttl: idToBidMap[id].exp || DEFAULT_BID_TTL, - netRevenue: DEFAULT_NET_REVENUE, - currency: bidResponse.cur || DEFAULT_CURRENCY, - meta: { advertiserDomains: idToBidMap[id].adomain || [] } - }; - if (idToImpMap[id].video) { - // for outstream, a renderer is specified - if (idToSlotConfig[id] && deepAccess(idToSlotConfig[id], 'mediaTypes.video.context') === 'outstream') { - bid.renderer = outstreamRenderer(deepAccess(idToSlotConfig[id], 'renderer.options'), deepAccess(idToBidMap[id], 'ext.outstream')); +const converter = ortbConverter({ + context: { + netRevenue: true, + ttl: 300, + currency: 'USD' + }, + + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + // tagid + imp.tagid = bidRequest.params.ct.toString(); + // unknown params + const unknownParams = slotUnknownParams(bidRequest); + if (imp.ext || unknownParams) { + imp.ext = Object.assign({}, imp.ext, unknownParams); + } + // battr + if (bidRequest.params.battr) { + ['banner', 'video', 'audio', 'native'].forEach(k => { + if (imp[k]) { + imp[k].battr = bidRequest.params.battr; } - bid.vastXml = idToBidMap[id].adm; - bid.mediaType = 'video'; - bid.width = idToBidMap[id].w; - bid.height = idToBidMap[id].h; - } else if (idToImpMap[id].banner) { - bid.ad = idToBidMap[id].adm; - bid.width = idToBidMap[id].w || idToImpMap[id].banner.w; - bid.height = idToBidMap[id].h || idToImpMap[id].banner.h; - } else if (idToImpMap[id]['native']) { - bid['native'] = nativeResponse(idToImpMap[id], idToBidMap[id]); - bid.mediaType = 'native'; - } - bids.push(bid); + }); } - }); - return bids; -} - -/** - * Produces an OpenRTBImpression from a slot config. - */ -function impression(slot) { - var firstPartyData = slot.ortb2Imp?.ext || {}; - var ext = Object.assign({}, firstPartyData, slotUnknownParams(slot)); - return { - id: slot.bidId, - banner: banner(slot), - 'native': nativeImpression(slot), - tagid: slot.params.ct.toString(), - video: video(slot), - bidfloor: bidFloor(slot), - ext: Object.keys(ext).length > 0 ? ext : null, - }; -} - -/** - * Produces an OpenRTB Banner object for the slot given. - */ -function banner(slot) { - const sizes = parseSizes(slot); - const size = adSize(slot, sizes); - return (slot.mediaTypes && slot.mediaTypes.banner) ? { - w: size[0], - h: size[1], - battr: slot.params.battr, - format: sizes - } : null; -} + // deals + if (bidRequest.params.deals && isArray(bidRequest.params.deals)) { + imp.pmp = { + private_auction: 0, + deals: bidRequest.params.deals + }; + } + return imp; + }, -/** - * Produce openrtb format objects based on the sizes configured for the slot. - */ -function parseSizes(slot) { - const sizes = deepAccess(slot, 'mediaTypes.banner.sizes'); - if (sizes && isArray(sizes)) { - return sizes.filter(sz => isArray(sz) && sz.length === 2).map(sz => ({ - w: sz[0], - h: sz[1] - })); - } - return null; -} + request(buildRequest, imps, bidderRequest, context) { + const request = buildRequest(imps, bidderRequest, context); + // publisher id + const siteOrApp = request.site || request.app; + const pubId = context.bidRequests && context.bidRequests.length > 0 ? context.bidRequests[0].params.cp : '0'; + if (siteOrApp) { + siteOrApp.publisher = Object.assign({}, siteOrApp.publisher, { + id: pubId.toString() + }); + } + // tmax + request.tmax = request.tmax || DEFAULT_TMAX; + return request; + }, -/** - * Produces an OpenRTB Video object for the slot given - */ -function video(slot) { - if (slot.params.video) { - return Object.assign({}, - slot.params.video, // previously supported as bidder param - slot.mediaTypes && slot.mediaTypes.video ? slot.mediaTypes.video : {}, // params on mediaTypes.video - {battr: slot.params.battr} - ); - } - return null; -} + bidResponse(buildBidResponse, bid, context) { + const bidResponse = buildBidResponse(bid, context); + bidResponse.cur = bidResponse.cur || DEFAULT_CURRENCY; + return bidResponse; + }, +}); /** * Unknown params are captured and sent on ext @@ -225,278 +138,4 @@ function slotUnknownParams(slot) { return Object.keys(ext).length > 0 ? { prebid: ext } : null; } -/** - * Sets up the renderer on the bid, for outstream bid responses. - */ -function outstreamRenderer(rendererOptions, outstreamExtOptions) { - const renderer = Renderer.install({ - url: outstreamExtOptions.rendererUrl, - config: { - defaultOptions: outstreamExtOptions.config, - rendererOptions, - type: outstreamExtOptions.type - }, - loaded: false, - }); - renderer.setRender((bid) => { - bid.renderer.push(() => { - const config = bid.renderer.getConfig(); - new window.PulsePointOutstreamRenderer().render({ - adUnitCode: bid.adUnitCode, - vastXml: bid.vastXml, - type: config.type, - defaultOptions: config.defaultOptions, - rendererOptions - }); - }); - }); - return renderer; -} - -/** - * Produces an OpenRTB Native object for the slot given. - */ -function nativeImpression(slot) { - if (slot.nativeParams) { - const assets = []; - addAsset(assets, titleAsset(assets.length + 1, slot.nativeParams.title, NATIVE_DEFAULTS.TITLE_LEN)); - addAsset(assets, dataAsset(assets.length + 1, slot.nativeParams.body, 2, NATIVE_DEFAULTS.DESCR_LEN)); - addAsset(assets, dataAsset(assets.length + 1, slot.nativeParams.sponsoredBy, 1, NATIVE_DEFAULTS.SPONSORED_BY_LEN)); - addAsset(assets, imageAsset(assets.length + 1, slot.nativeParams.icon, 1, NATIVE_DEFAULTS.ICON_MIN, NATIVE_DEFAULTS.ICON_MIN)); - addAsset(assets, imageAsset(assets.length + 1, slot.nativeParams.image, 3, NATIVE_DEFAULTS.IMG_MIN, NATIVE_DEFAULTS.IMG_MIN)); - return { - request: JSON.stringify({ assets }), - ver: '1.1', - battr: slot.params.battr, - }; - } - return null; -} - -/** - * Helper method to add an asset to the assets list. - */ -function addAsset(assets, asset) { - if (asset) { - assets.push(asset); - } -} - -/** - * Produces a Native Title asset for the configuration given. - */ -function titleAsset(id, params, defaultLen) { - if (params) { - return { - id, - required: params.required ? 1 : 0, - title: { - len: params.len || defaultLen, - }, - }; - } - return null; -} - -/** - * Produces a Native Image asset for the configuration given. - */ -function imageAsset(id, params, type, defaultMinWidth, defaultMinHeight) { - return params ? { - id, - required: params.required ? 1 : 0, - img: { - type, - wmin: params.wmin || defaultMinWidth, - hmin: params.hmin || defaultMinHeight, - } - } : null; -} - -/** - * Produces a Native Data asset for the configuration given. - */ -function dataAsset(id, params, type, defaultLen) { - return params ? { - id, - required: params.required ? 1 : 0, - data: { - type, - len: params.len || defaultLen, - } - } : null; -} - -/** - * Produces an OpenRTB site object. - */ -function site(bidRequests, bidderRequest) { - const pubId = bidRequests && bidRequests.length > 0 ? bidRequests[0].params.cp : '0'; - const appParams = bidRequests[0].params.app; - if (!appParams) { - // use the first party data if available, and override only publisher/ref/page properties - var firstPartyData = bidderRequest?.ortb2?.site || {}; - return Object.assign({}, firstPartyData, { - publisher: { - id: pubId.toString(), - }, - // TODO: does the fallback make sense here? - ref: bidderRequest?.refererInfo?.ref || window.document.referrer, - page: bidderRequest?.refererInfo?.page || '' - }); - } - return null; -} - -/** - * Produces an OpenRTB App object. - */ -function app(bidderRequest) { - const pubId = bidderRequest && bidderRequest.length > 0 ? bidderRequest[0].params.cp : '0'; - const appParams = bidderRequest[0].params.app; - if (appParams) { - return { - publisher: { - id: pubId.toString(), - }, - bundle: appParams.bundle, - storeurl: appParams.storeUrl, - domain: appParams.domain, - } - } - return null; -} - -/** - * Produces an OpenRTB Device object. - */ -function device() { - return { - ua: navigator.userAgent, - language: (navigator.language || navigator.browserLanguage || navigator.userLanguage || navigator.systemLanguage), - }; -} - -/** - * Safely parses the input given. Returns null on - * parsing failure. - */ -function parse(rawResponse) { - try { - if (rawResponse) { - return JSON.parse(rawResponse); - } - } catch (ex) { - logError('pulsepointLite.safeParse', 'ERROR', ex); - } - return null; -} - -/** - * Determines the AdSize for the slot. - */ -function adSize(slot, sizes) { - if (slot.params.cf) { - const size = slot.params.cf.toUpperCase().split('X'); - const width = parseInt(slot.params.cw || size[0], 10); - const height = parseInt(slot.params.ch || size[1], 10); - return [width, height]; - } else if (sizes && sizes.length > 0) { - return [sizes[0].w, sizes[0].h]; - } - return [1, 1]; -} - -/** - * Handles the user level attributes and produces - * an openrtb User object. - */ -function user(bidRequest, bidderRequest) { - var user = bidderRequest?.ortb2?.user || { ext: {} }; - var ext = user.ext; - if (bidderRequest) { - if (bidderRequest.gdprConsent) { - ext.consent = bidderRequest.gdprConsent.consentString; - } - } - if (bidRequest) { - let eids = bidRequest.userIdAsEids; - if (eids) { - ext.eids = eids; - } - } - return user; -} - -/** - * Produces the regulations ortb object - */ -function regs(bidderRequest) { - if (bidderRequest.gdprConsent || bidderRequest.uspConsent) { - var ext = {}; - // GDPR applies attribute (actual consent value is in user object) - if (bidderRequest.gdprConsent) { - ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; - } - // CCPA - if (bidderRequest.uspConsent) { - ext.us_privacy = bidderRequest.uspConsent; - } - return { ext }; - } - return null; -} - -/** - * Creates source object with supply chain - */ -function source(schain) { - if (schain) { - return { - ext: { schain } - }; - } - return null; -} - -/** - * Parses the native response from the Bid given. - */ -function nativeResponse(imp, bid) { - if (imp['native']) { - const nativeAd = parse(bid.adm); - const keys = {}; - if (nativeAd && nativeAd['native'] && nativeAd['native'].assets) { - nativeAd['native'].assets.forEach(asset => { - keys.title = asset.title ? asset.title.text : keys.title; - keys.body = asset.data && asset.data.type === 2 ? asset.data.value : keys.body; - keys.sponsoredBy = asset.data && asset.data.type === 1 ? asset.data.value : keys.sponsoredBy; - keys.image = asset.img && asset.img.type === 3 ? asset.img.url : keys.image; - keys.icon = asset.img && asset.img.type === 1 ? asset.img.url : keys.icon; - }); - if (nativeAd['native'].link) { - keys.clickUrl = encodeURIComponent(nativeAd['native'].link.url); - } - keys.impressionTrackers = nativeAd['native'].imptrackers; - return keys; - } - } - return null; -} - -function bidFloor(slot) { - let floor = slot.params.bidfloor; - if (isFn(slot.getFloor)) { - const floorData = slot.getFloor({ - mediaType: slot.mediaTypes.banner ? 'banner' : slot.mediaTypes.video ? 'video' : 'Native', - size: '*', - currency: DEFAULT_CURRENCY, - }); - if (floorData && floorData.floor) { - floor = floorData.floor; - } - } - return floor; -} - registerBidder(spec); diff --git a/modules/pulsepointBidAdapter.md b/modules/pulsepointBidAdapter.md index 7f4b7e6b611f..899c277f92fd 100644 --- a/modules/pulsepointBidAdapter.md +++ b/modules/pulsepointBidAdapter.md @@ -2,7 +2,7 @@ **Module Name**: PulsePoint Bidder Adapter **Module Type**: Bidder Adapter -**Maintainer**: ExchangeTeam@pulsepoint.com +**Maintainer**: ExchangeTeam@pulsepoint.com # Description @@ -18,55 +18,49 @@ Please use ```pulsepoint``` as the bidder code. sizes: [[300, 250]], bids: [{ bidder: 'pulsepoint', - params: { - cf: '300X250', + params: { cp: 512379, ct: 486653 } }] },{ - code: 'native-ad-div', - sizes: [[1, 1]], - nativeParams: { - title: { required: true, len: 75 }, - image: { required: true }, - body: { len: 200 }, - sponsoredBy: { len: 20 } - }, - bids: [{ - bidder: 'pulsepoint', - params: { - cp: 512379, - ct: 505642 - } - }] - },{ - code: 'outstream-div', - mediaTypes: { - video: { - playerSize: [640, 480], - context: 'outstream', - h: 300, - w: 400, - minduration: 1, - maxduration: 210, - linearity: 1, - mimes: ["video/mp4", "video/ogg", "video/webm"], - pos: 3 - } - }, - bids: [{ - bidder: 'pulsepoint', - params: { - cp: 512379, - ct: 505642 - } - }], - renderer: { - options: { - text: "PulsePoint Outstream" - } - } + code: 'native-1-slot', + mediaTypes: { + native: { + ortb: { + assets: [{ + id: 1, + required: 1, + img: { + type: 3, + w: 150, + h: 50, + } + }, + { + id: 2, + required: 1, + title: { + len: 80 + } + }, + { + id: 3, + required: 1, + data: { + type: 1 + } + }] + } + } + }, + bids: [{ + bidder: 'pulsepoint', + params: { + cp: 512379, + ct: 694973 + } + }] },{ code: 'instream', mediaTypes: { diff --git a/test/spec/modules/pulsepointBidAdapter_spec.js b/test/spec/modules/pulsepointBidAdapter_spec.js index 60dca9e6da0c..8db7e9097719 100644 --- a/test/spec/modules/pulsepointBidAdapter_spec.js +++ b/test/spec/modules/pulsepointBidAdapter_spec.js @@ -1,7 +1,8 @@ /* eslint dot-notation:0, quote-props:0 */ import {expect} from 'chai'; import {spec} from 'modules/pulsepointBidAdapter.js'; -import {deepClone} from 'src/utils.js'; +import {syncAddFPDToBidderRequest} from '../../helpers/fpd.js'; +import {deepClone} from '../../../src/utils'; describe('PulsePoint Adapter Tests', function () { const slotConfigs = [{ @@ -31,39 +32,52 @@ describe('PulsePoint Adapter Tests', function () { cf: '728x90' } }]; + const nativeOrtbRequest = { + assets: [{ + id: 1, + required: 1, + img: { + type: 3, + w: 150, + h: 50, + } + }, + { + id: 2, + required: 1, + title: { + len: 80 + } + }, + { + id: 3, + required: 0, + data: { + type: 1 + } + }] + }; const nativeSlotConfig = [{ placementCode: '/DfpAccount1/slot3', bidId: 'bid12345', - nativeParams: { - title: { required: true, len: 200 }, - image: { wmin: 100 }, - sponsoredBy: { } + mediaTypes: { + native: { + sendTargetingKeys: false, + ortb: nativeOrtbRequest + } }, + nativeOrtbRequest, params: { cp: 'p10000', ct: 't10000' } }]; - const appSlotConfig = [{ - placementCode: '/DfpAccount1/slot3', - bidId: 'bid12345', - params: { - cp: 'p10000', - ct: 't10000', - app: { - bundle: 'com.pulsepoint.apps', - storeUrl: 'https://pulsepoint.com/apps', - domain: 'pulsepoint.com', - } - } - }]; const videoSlotConfig = [{ placementCode: '/DfpAccount1/slotVideo', bidId: 'bid12345', - params: { - cp: 'p10000', - ct: 't10000', + mediaTypes: { video: { + playerSize: [400, 300], w: 400, h: 300, minduration: 5, @@ -73,6 +87,10 @@ describe('PulsePoint Adapter Tests', function () { minbitrate: 200, protocols: [1, 2, 4] } + }, + params: { + cp: 'p10000', + ct: 't10000' } }]; const additionalParamsConfig = [{ @@ -97,68 +115,6 @@ describe('PulsePoint Adapter Tests', function () { } }]; - const ortbParamsSlotConfig = [{ - placementCode: '/DfpAccount1/slot1', - mediaTypes: { - banner: { - sizes: [[1, 1]] - } - }, - bidId: 'bid12345', - params: { - cp: 'p10000', - ct: 't10000', - cf: '1x1', - bcat: ['IAB-1', 'IAB-20'], - battr: [1, 2, 3], - bidfloor: 1.5, - badv: ['cocacola.com', 'lays.com'] - } - }, { - placementCode: '/DfpAccount1/slotVideo', - bidId: 'bid12345', - params: { - cp: 'p10000', - ct: 't10000', - video: { - w: 400, - h: 300, - minduration: 5, - maxduration: 10, - }, - battr: [2, 3, 4], - bidfloor: 2.5, - } - }]; - - const outstreamSlotConfig = [{ - placementCode: '/DfpAccount1/slot1', - mediaTypes: { - video: { - playerSize: [640, 480], - context: 'outstream' - } - }, - bidId: 'bid12345', - params: { - cp: 'p10000', - ct: 't10000', - cf: '1x1', - video: { - h: 300, - w: 400, - minduration: 1, - maxduration: 210, - linearity: 1, - } - }, - renderer: { - options: { - text: 'PulsePoint Outstream' - } - } - }]; - const schainParamsSlotConfig = [{ placementCode: '/DfpAccount1/slot1', mediaTypes: { @@ -200,7 +156,7 @@ describe('PulsePoint Adapter Tests', function () { }; it('Verify build request', function () { - const request = spec.buildRequests(slotConfigs, bidderRequest); + const request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); expect(request.method).to.equal('POST'); const ortbRequest = request.data; @@ -208,7 +164,6 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.site).to.not.equal(null); expect(ortbRequest.site.publisher).to.not.equal(null); expect(ortbRequest.site.publisher.id).to.equal('p10000'); - expect(ortbRequest.site.ref).to.equal(bidderRequest.refererInfo.ref); expect(ortbRequest.site.page).to.equal('https://publisher.com/home'); expect(ortbRequest.imp).to.have.lengthOf(2); // device object @@ -217,19 +172,15 @@ describe('PulsePoint Adapter Tests', function () { // slot 1 expect(ortbRequest.imp[0].tagid).to.equal('t10000'); expect(ortbRequest.imp[0].banner).to.not.equal(null); - expect(ortbRequest.imp[0].banner.w).to.equal(300); - expect(ortbRequest.imp[0].banner.h).to.equal(250); + expect(ortbRequest.imp[0].banner.format).to.deep.eq([{'w': 728, 'h': 90}, {'w': 160, 'h': 600}]); // slot 2 expect(ortbRequest.imp[1].tagid).to.equal('t20000'); expect(ortbRequest.imp[1].banner).to.not.equal(null); - expect(ortbRequest.imp[1].banner.w).to.equal(728); - expect(ortbRequest.imp[1].banner.h).to.equal(90); - // tmax - expect(ortbRequest.tmax).to.equal(500); + expect(ortbRequest.imp[1].banner.format).to.deep.eq([{'w': 728, 'h': 90}]); }); it('Verify parse response', function () { - const request = spec.buildRequests(slotConfigs, bidderRequest); + const request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); const ortbRequest = request.data; const ortbResponse = { seatbid: [{ @@ -237,11 +188,16 @@ describe('PulsePoint Adapter Tests', function () { impid: ortbRequest.imp[0].id, price: 1.25, adm: 'This is an Ad', - crid: 'Creative#123' + crid: 'Creative#123', + mtype: 1, + w: 300, + h: 250, + exp: 20, + adomain: ['advertiser.com'] }] }] }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); + const bids = spec.interpretResponse({body: ortbResponse}, request); expect(bids).to.have.lengthOf(1); // verify first bid const bid = bids[0]; @@ -249,140 +205,112 @@ describe('PulsePoint Adapter Tests', function () { expect(bid.ad).to.equal('This is an Ad'); expect(bid.width).to.equal(300); expect(bid.height).to.equal(250); - expect(bid.adId).to.equal('bid12345'); expect(bid.creative_id).to.equal('Creative#123'); expect(bid.creativeId).to.equal('Creative#123'); expect(bid.netRevenue).to.equal(true); expect(bid.currency).to.equal('USD'); expect(bid.ttl).to.equal(20); - }); - - it('Verify ttl/currency/adomain applied to bid', function () { - const request = spec.buildRequests(slotConfigs, bidderRequest); - const ortbRequest = request.data; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'This is an Ad#1', - crid: 'Creative#123', - exp: 50, - adomain: ['advertiser.com'] - }, { - impid: ortbRequest.imp[1].id, - price: 1.25, - adm: 'This is an Ad#2', - crid: 'Creative#123' - }] - }], - cur: 'GBP' - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - expect(bids).to.have.lengthOf(2); - // verify first bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.ad).to.equal('This is an Ad#1'); - expect(bid.ttl).to.equal(50); - expect(bid.currency).to.equal('GBP'); expect(bid.meta).to.not.be.null; expect(bid.meta.advertiserDomains).to.eql(['advertiser.com']); - const secondBid = bids[1]; - expect(secondBid.cpm).to.equal(1.25); - expect(secondBid.ad).to.equal('This is an Ad#2'); - expect(secondBid.ttl).to.equal(20); - expect(secondBid.currency).to.equal('GBP'); - expect(secondBid.meta).to.not.be.null; - expect(secondBid.meta.advertiserDomains).to.eql([]); }); it('Verify full passback', function () { const request = spec.buildRequests(slotConfigs, bidderRequest); - const bids = spec.interpretResponse({ body: null }, request) + const bids = spec.interpretResponse({body: null}, request) expect(bids).to.have.lengthOf(0); }); - it('Verify Native request', function () { - const request = spec.buildRequests(nativeSlotConfig, bidderRequest); - expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - // native impression - expect(ortbRequest.imp[0].tagid).to.equal('t10000'); - expect(ortbRequest.imp[0].banner).to.equal(null); - const nativePart = ortbRequest.imp[0]['native']; - expect(nativePart).to.not.equal(null); - expect(nativePart.ver).to.equal('1.1'); - expect(nativePart.request).to.not.equal(null); - // native request assets - const nativeRequest = JSON.parse(ortbRequest.imp[0]['native'].request); - expect(nativeRequest).to.not.equal(null); - expect(nativeRequest.assets).to.have.lengthOf(3); - // title asset - expect(nativeRequest.assets[0].id).to.equal(1); - expect(nativeRequest.assets[0].required).to.equal(1); - expect(nativeRequest.assets[0].title).to.not.equal(null); - expect(nativeRequest.assets[0].title.len).to.equal(200); - // data asset - expect(nativeRequest.assets[1].id).to.equal(2); - expect(nativeRequest.assets[1].required).to.equal(0); - expect(nativeRequest.assets[1].title).to.be.undefined; - expect(nativeRequest.assets[1].data).to.not.equal(null); - expect(nativeRequest.assets[1].data.type).to.equal(1); - expect(nativeRequest.assets[1].data.len).to.equal(50); - // image asset - expect(nativeRequest.assets[2].id).to.equal(3); - expect(nativeRequest.assets[2].required).to.equal(0); - expect(nativeRequest.assets[2].title).to.be.undefined; - expect(nativeRequest.assets[2].img).to.not.equal(null); - expect(nativeRequest.assets[2].img.wmin).to.equal(100); - expect(nativeRequest.assets[2].img.hmin).to.equal(150); - expect(nativeRequest.assets[2].img.type).to.equal(3); - }); + if (FEATURES.NATIVE) { + it('Verify Native request', function () { + const request = spec.buildRequests(nativeSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + // native impression + expect(ortbRequest.imp[0].tagid).to.equal('t10000'); + expect(ortbRequest.imp[0].banner).to.be.undefined; + const nativePart = ortbRequest.imp[0]['native']; + expect(nativePart).to.not.equal(null); + expect(nativePart.request).to.not.equal(null); + // native request assets + const nativeRequest = JSON.parse(ortbRequest.imp[0]['native'].request); + expect(nativeRequest).to.not.equal(null); + expect(nativeRequest.assets).to.have.lengthOf(3); + // image asset + expect(nativeRequest.assets[0].id).to.equal(1); + expect(nativeRequest.assets[0].required).to.equal(1); + expect(nativeRequest.assets[0].title).to.be.undefined; + expect(nativeRequest.assets[0].img).to.not.equal(null); + expect(nativeRequest.assets[0].img.w).to.equal(150); + expect(nativeRequest.assets[0].img.h).to.equal(50); + expect(nativeRequest.assets[0].img.type).to.equal(3); + // title asset + expect(nativeRequest.assets[1].id).to.equal(2); + expect(nativeRequest.assets[1].required).to.equal(1); + expect(nativeRequest.assets[1].title).to.not.equal(null); + expect(nativeRequest.assets[1].title.len).to.equal(80); + // data asset + expect(nativeRequest.assets[2].id).to.equal(3); + expect(nativeRequest.assets[2].required).to.equal(0); + expect(nativeRequest.assets[2].title).to.be.undefined; + expect(nativeRequest.assets[2].data).to.not.equal(null); + expect(nativeRequest.assets[2].data.type).to.equal(1); + }); - it('Verify Native response', function () { - const request = spec.buildRequests(nativeSlotConfig, bidderRequest); - expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - const nativeResponse = { - 'native': { + it('Verify Native response', function () { + const request = spec.buildRequests(nativeSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + const nativeResponse = { assets: [ - { title: { text: 'Ad Title' } }, - { data: { type: 1, value: 'Sponsored By: Brand' } }, - { img: { type: 3, url: 'https://images.cdn.brand.com/123' } } + {id: 1, img: {type: 3, url: 'https://images.cdn.brand.com/123'}}, + {id: 2, title: {text: 'Ad Title'}}, + {id: 3, data: {type: 1, value: 'Sponsored By: Brand'}} ], - link: { url: 'https://brand.clickme.com/' }, + link: {url: 'https://brand.clickme.com/'}, imptrackers: ['https://imp1.trackme.com/', 'https://imp1.contextweb.com/'] - } - }; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: JSON.stringify(nativeResponse) + + }; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: JSON.stringify(nativeResponse), + mtype: 4 + }] }] - }] - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - // verify bid - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.adId).to.equal('bid12345'); - expect(bid.ad).to.be.undefined; - expect(bid.mediaType).to.equal('native'); - const nativeBid = bid['native']; - expect(nativeBid).to.not.equal(null); - expect(nativeBid.title).to.equal('Ad Title'); - expect(nativeBid.sponsoredBy).to.equal('Sponsored By: Brand'); - expect(nativeBid.image).to.equal('https://images.cdn.brand.com/123'); - expect(nativeBid.clickUrl).to.equal(encodeURIComponent('https://brand.clickme.com/')); - expect(nativeBid.impressionTrackers).to.have.lengthOf(2); - expect(nativeBid.impressionTrackers[0]).to.equal('https://imp1.trackme.com/'); - expect(nativeBid.impressionTrackers[1]).to.equal('https://imp1.contextweb.com/'); - }); + }; + const bids = spec.interpretResponse({body: ortbResponse}, request); + // verify bid + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.requestId).to.equal('bid12345'); + expect(bid.ad).to.be.undefined; + expect(bid.mediaType).to.equal('native'); + expect(bid['native']).to.not.be.null; + expect(bid['native'].ortb).to.not.be.null; + const nativeBid = bid['native'].ortb; + expect(nativeBid.assets).to.have.lengthOf(3); + expect(nativeBid.assets[0].id).to.equal(1); + expect(nativeBid.assets[0].img).to.not.be.null; + expect(nativeBid.assets[0].img.type).to.equal(3); + expect(nativeBid.assets[0].img.url).to.equal('https://images.cdn.brand.com/123'); + expect(nativeBid.assets[1].id).to.equal(2); + expect(nativeBid.assets[1].title).to.not.be.null; + expect(nativeBid.assets[1].title.text).to.equal('Ad Title'); + expect(nativeBid.assets[2].id).to.equal(3); + expect(nativeBid.assets[2].data).to.not.be.null; + expect(nativeBid.assets[2].data.type).to.equal(1); + expect(nativeBid.assets[2].data.value).to.equal('Sponsored By: Brand'); + expect(nativeBid.link).to.not.be.null; + expect(nativeBid.link.url).to.equal('https://brand.clickme.com/'); + expect(nativeBid.imptrackers).to.have.lengthOf(2); + expect(nativeBid.imptrackers[0]).to.equal('https://imp1.trackme.com/'); + expect(nativeBid.imptrackers[1]).to.equal('https://imp1.contextweb.com/'); + }); + } it('Verifies bidder code', function () { expect(spec.code).to.equal('pulsepoint'); @@ -430,19 +358,6 @@ describe('PulsePoint Adapter Tests', function () { expect(options[0].url).to.equal('https://bh.contextweb.com/visitormatch/prebid'); }); - it('Verify app requests', function () { - const request = spec.buildRequests(appSlotConfig, bidderRequest); - const ortbRequest = request.data; - // site object - expect(ortbRequest.site).to.equal(null); - expect(ortbRequest.app).to.not.be.null; - expect(ortbRequest.app.publisher).to.not.equal(null); - expect(ortbRequest.app.publisher.id).to.equal('p10000'); - expect(ortbRequest.app.bundle).to.equal('com.pulsepoint.apps'); - expect(ortbRequest.app.storeurl).to.equal('https://pulsepoint.com/apps'); - expect(ortbRequest.app.domain).to.equal('pulsepoint.com'); - }); - it('Verify GDPR', function () { const bidderRequestGdpr = { gdprConsent: { @@ -450,7 +365,7 @@ describe('PulsePoint Adapter Tests', function () { consentString: 'serialized_gpdr_data' } }; - const request = spec.buildRequests(slotConfigs, Object.assign({}, bidderRequest, bidderRequestGdpr)); + const request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(Object.assign({}, bidderRequest, bidderRequestGdpr))); expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); expect(request.method).to.equal('POST'); const ortbRequest = request.data; @@ -468,7 +383,8 @@ describe('PulsePoint Adapter Tests', function () { const bidderRequestUSPrivacy = { uspConsent: '1YYY' }; - const request = spec.buildRequests(slotConfigs, Object.assign({}, bidderRequest, bidderRequestUSPrivacy)); + const request = spec.buildRequests(slotConfigs, + syncAddFPDToBidderRequest(Object.assign({}, bidderRequest, bidderRequestUSPrivacy))); expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); expect(request.method).to.equal('POST'); const ortbRequest = request.data; @@ -478,52 +394,54 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.regs.ext.us_privacy).to.equal('1YYY'); }); - it('Verify Video request', function () { - const request = spec.buildRequests(videoSlotConfig, bidderRequest); - expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.imp).to.have.lengthOf(1); - expect(ortbRequest.imp[0].video).to.not.be.null; - expect(ortbRequest.imp[0].native).to.be.null; - expect(ortbRequest.imp[0].banner).to.be.null; - expect(ortbRequest.imp[0].video.w).to.equal(400); - expect(ortbRequest.imp[0].video.h).to.equal(300); - expect(ortbRequest.imp[0].video.minduration).to.equal(5); - expect(ortbRequest.imp[0].video.maxduration).to.equal(10); - expect(ortbRequest.imp[0].video.startdelay).to.equal(0); - expect(ortbRequest.imp[0].video.skip).to.equal(1); - expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); - expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); - }); + if (FEATURES.VIDEO) { + it('Verify Video request', function () { + const request = spec.buildRequests(videoSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + expect(ortbRequest).to.not.equal(null); + expect(ortbRequest.imp).to.have.lengthOf(1); + expect(ortbRequest.imp[0].video).to.not.be.null; + expect(ortbRequest.imp[0].native).to.be.undefined; + expect(ortbRequest.imp[0].banner).to.be.undefined; + expect(ortbRequest.imp[0].video.w).to.equal(400); + expect(ortbRequest.imp[0].video.h).to.equal(300); + expect(ortbRequest.imp[0].video.minduration).to.equal(5); + expect(ortbRequest.imp[0].video.maxduration).to.equal(10); + expect(ortbRequest.imp[0].video.startdelay).to.equal(0); + expect(ortbRequest.imp[0].video.skip).to.equal(1); + expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); + expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); + }); - it('Verify Video response', function () { - const request = spec.buildRequests(videoSlotConfig, bidderRequest); - expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); - expect(request.method).to.equal('POST'); - const ortbRequest = request.data; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'https//pulsepoint.video.mp4' + it('Verify Video response', function () { + const request = spec.buildRequests(videoSlotConfig, bidderRequest); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + const ortbResponse = { + seatbid: [{ + bid: [{ + impid: ortbRequest.imp[0].id, + price: 1.25, + adm: 'https//pulsepoint.video.mp4', + mtype: 2 + }] }] - }] - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.adId).to.equal('bid12345'); - expect(bid.ad).to.be.undefined; - expect(bid['native']).to.be.undefined; - expect(bid.mediaType).to.equal('video'); - expect(bid.vastXml).to.equal(ortbResponse.seatbid[0].bid[0].adm); - }); + }; + const bids = spec.interpretResponse({ body: ortbResponse }, request); + const bid = bids[0]; + expect(bid.cpm).to.equal(1.25); + expect(bid.ad).to.be.undefined; + expect(bid['native']).to.be.undefined; + expect(bid.mediaType).to.equal('video'); + expect(bid.vastXml).to.equal(ortbResponse.seatbid[0].bid[0].adm); + }); + } it('Verify extra parameters', function () { - let request = spec.buildRequests(additionalParamsConfig, bidderRequest); + let request = spec.buildRequests(additionalParamsConfig, syncAddFPDToBidderRequest(bidderRequest)); let ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.imp).to.have.lengthOf(1); @@ -538,31 +456,15 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.imp[0].ext.prebid.extra_key4).to.eql([1, 2, 3]); expect(Object.keys(ortbRequest.imp[0].ext.prebid)).to.eql(['extra_key1', 'extra_key2', 'extra_key3', 'extra_key4']); // attempting with a configuration with no unknown params. - request = spec.buildRequests(outstreamSlotConfig, bidderRequest); + request = spec.buildRequests(videoSlotConfig, bidderRequest); ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.imp).to.have.lengthOf(1); - expect(ortbRequest.imp[0].ext).to.equal(null); - }); - - it('Verify ortb parameters', function () { - const request = spec.buildRequests(ortbParamsSlotConfig, bidderRequest); - const ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.bcat).to.eql(['IAB-1', 'IAB-20']); - expect(ortbRequest.badv).to.eql(['cocacola.com', 'lays.com']); - expect(ortbRequest.imp).to.have.lengthOf(2); - expect(ortbRequest.imp[0].bidfloor).to.equal(1.5); - expect(ortbRequest.imp[0].banner.battr).to.eql([1, 2, 3]); - expect(ortbRequest.imp[0].ext).to.be.null; - // slot 2 - expect(ortbRequest.imp[1].bidfloor).to.equal(2.5); - expect(ortbRequest.imp[1].video.battr).to.eql([2, 3, 4]); - expect(ortbRequest.imp[1].ext).to.be.null; + expect(ortbRequest.imp[0].ext).to.be.undefined; }); it('Verify schain parameters', function () { - const request = spec.buildRequests(schainParamsSlotConfig, bidderRequest); + const request = spec.buildRequests(schainParamsSlotConfig, syncAddFPDToBidderRequest(bidderRequest)); const ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.source).to.not.equal(null); @@ -580,42 +482,6 @@ describe('PulsePoint Adapter Tests', function () { expect(ortbRequest.source.ext.schain.nodes[0].domain).to.equal('publisher.com'); }); - it('Verify outstream renderer', function () { - const bidderRequestOutstream = Object.assign({}, bidderRequest, {bids: [outstreamSlotConfig[0]]}); - const request = spec.buildRequests(outstreamSlotConfig, bidderRequestOutstream); - const ortbRequest = request.data; - expect(ortbRequest).to.not.be.null; - expect(ortbRequest.imp[0]).to.not.be.null; - expect(ortbRequest.imp[0].video).to.not.be.null; - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'https//pulsepoint.video.mp4', - ext: { - outstream: { - type: 'Inline', - config: { - text: 'ADVERTISEMENT', - skipaftersec: 5 - }, - rendererUrl: 'https://tag.contextweb.com/hb-outstr-renderer.js' - } - } - }] - }] - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - const bid = bids[0]; - expect(bid.cpm).to.equal(1.25); - expect(bid.renderer).to.not.be.null; - expect(bid.renderer.url).to.equal('https://tag.contextweb.com/hb-outstr-renderer.js'); - expect(bid.renderer.getConfig()).to.not.be.null; - expect(bid.renderer.getConfig().defaultOptions).to.eql(ortbResponse.seatbid[0].bid[0].ext.outstream.config); - expect(bid.renderer.getConfig().rendererOptions).to.eql(outstreamSlotConfig[0].renderer.options); - expect(bid.renderer.getConfig().type).to.equal('Inline'); - }); it('Verify common id parameters', function () { const bidRequests = deepClone(slotConfigs); bidRequests[0].userIdAsEids = [{ @@ -633,169 +499,17 @@ describe('PulsePoint Adapter Tests', function () { }] } ]; - const request = spec.buildRequests(bidRequests, bidderRequest); + const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); expect(request).to.be.not.null; - const ortbRequest = request.data; expect(request.data).to.be.not.null; + const ortbRequest = request.data; // user object expect(ortbRequest.user).to.not.be.undefined; expect(ortbRequest.user.ext).to.not.be.undefined; expect(ortbRequest.user.ext.eids).to.not.be.undefined; expect(ortbRequest.user.ext.eids).to.deep.equal(bidRequests[0].userIdAsEids); }); - it('Verify multiple adsizes', function () { - const bidRequests = deepClone(slotConfigs); - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request).to.be.not.null; - expect(request.data).to.be.not.null; - const ortbRequest = request.data; - expect(ortbRequest.imp).to.have.lengthOf(2); - // first impression has multi sizes - expect(ortbRequest.imp[0].banner).to.not.be.null; - expect(ortbRequest.imp[0].banner.w).to.equal(300); - expect(ortbRequest.imp[0].banner.h).to.equal(250); - expect(ortbRequest.imp[0].banner.format).to.not.be.null; - expect(ortbRequest.imp[0].banner.format).to.have.lengthOf(2); - expect(ortbRequest.imp[0].banner.format[0].w).to.equal(728); - expect(ortbRequest.imp[0].banner.format[0].h).to.equal(90); - expect(ortbRequest.imp[0].banner.format[1].w).to.equal(160); - expect(ortbRequest.imp[0].banner.format[1].h).to.equal(600); - // slot 2 - expect(ortbRequest.imp[1].banner).to.not.be.null; - expect(ortbRequest.imp[1].banner.w).to.equal(728); - expect(ortbRequest.imp[1].banner.h).to.equal(90); - expect(ortbRequest.imp[1].banner.format).to.not.be.null; - expect(ortbRequest.imp[1].banner.format).to.have.lengthOf(1); - expect(ortbRequest.imp[1].banner.format[0].w).to.equal(728); - expect(ortbRequest.imp[1].banner.format[0].h).to.equal(90); - // adsize on response - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'This is an Ad', - crid: 'Creative#123', - w: 728, - h: 90 - }] - }] - }; - const bids = spec.interpretResponse({ body: ortbResponse }, request); - expect(bids).to.have.lengthOf(1); - const bid = bids[0]; - expect(bid.width).to.equal(728); - expect(bid.height).to.equal(90); - }); - it('Verify multi-format response', function () { - const bidRequests = deepClone(slotConfigs); - bidRequests[0].mediaTypes['native'] = { - title: { - required: true - }, - image: { - required: true - }, - sponsoredBy: { - required: true - } - }; - bidRequests[1].params.video = { - w: 400, - h: 300, - minduration: 5, - maxduration: 10, - }; - const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request).to.be.not.null; - expect(request.data).to.be.not.null; - const ortbRequest = request.data; - expect(ortbRequest.imp).to.have.lengthOf(2); - // adsize on response - const ortbResponse = { - seatbid: [{ - bid: [{ - impid: ortbRequest.imp[0].id, - price: 1.25, - adm: 'This is an Ad', - crid: 'Creative#123', - w: 728, - h: 90 - }, { - impid: ortbRequest.imp[1].id, - price: 2.5, - adm: '', - crid: 'Creative#234', - w: 728, - h: 90 - }] - }] - }; - // request has both types - banner and native, response is parsed as banner. - // for impression#2, response is parsed as video - const bids = spec.interpretResponse({ body: ortbResponse }, request); - expect(bids).to.have.lengthOf(2); - const bid = bids[0]; - expect(bid.width).to.equal(728); - expect(bid.height).to.equal(90); - const secondBid = bids[1]; - expect(secondBid.vastXml).to.equal(''); - }); - it('Verify bid floor', function () { - const bidRequests = deepClone(slotConfigs); - bidRequests[0].params.bidfloor = 1.05; - let request = spec.buildRequests(bidRequests, bidderRequest); - let ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.imp[0].bidfloor).to.equal(1.05); - expect(ortbRequest.imp[1].bidfloor).to.be.undefined; - let floorArg = null; - // publisher uses the floor module - bidRequests[0].getFloor = (arg) => { - floorArg = arg; - return { floor: 1.25 }; - }; - bidRequests[1].getFloor = () => { - return { floor: 2.05 }; - }; - request = spec.buildRequests(bidRequests, bidderRequest); - ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.imp[0].bidfloor).to.equal(1.25); - expect(ortbRequest.imp[1].bidfloor).to.equal(2.05); - expect(floorArg).to.not.be.null; - expect(floorArg.mediaType).to.equal('banner'); - expect(floorArg.currency).to.equal('USD'); - expect(floorArg.size).to.equal('*'); - }); - it('Verify Video params on mediaTypes.video', function () { - const bidRequests = deepClone(videoSlotConfig); - bidRequests[0].mediaTypes = { - video: { - w: 600, - h: 400, - minduration: 15, - maxduration: 20, - startdelay: 10, - skip: 0, - } - }; - const request = spec.buildRequests(bidRequests, bidderRequest); - const ortbRequest = request.data; - expect(ortbRequest).to.not.equal(null); - expect(ortbRequest.imp).to.have.lengthOf(1); - expect(ortbRequest.imp[0].video).to.not.be.null; - expect(ortbRequest.imp[0].native).to.be.null; - expect(ortbRequest.imp[0].banner).to.be.null; - expect(ortbRequest.imp[0].video.w).to.equal(600); - expect(ortbRequest.imp[0].video.h).to.equal(400); - expect(ortbRequest.imp[0].video.minduration).to.equal(15); - expect(ortbRequest.imp[0].video.maxduration).to.equal(20); - expect(ortbRequest.imp[0].video.startdelay).to.equal(10); - expect(ortbRequest.imp[0].video.skip).to.equal(0); - expect(ortbRequest.imp[0].video.minbitrate).to.equal(200); - expect(ortbRequest.imp[0].video.protocols).to.eql([1, 2, 4]); - }); + it('Verify user level first party data', function () { const bidderRequest = { refererInfo: { @@ -819,7 +533,7 @@ describe('PulsePoint Adapter Tests', function () { } } }; - let request = spec.buildRequests(slotConfigs, bidderRequest); + let request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); let ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.user).to.not.equal(null); @@ -835,12 +549,9 @@ describe('PulsePoint Adapter Tests', function () { } }); }); + it('Verify site level first party data', function () { const bidderRequest = { - refererInfo: { - page: 'https://publisher.com/home', - ref: 'https://referrer' - }, ortb2: { site: { content: { @@ -853,11 +564,14 @@ describe('PulsePoint Adapter Tests', function () { }] }, page: 'http://pub.com/news', - ref: 'http://google.com' + ref: 'http://google.com', + publisher: { + domain: 'pub.com' + } } } }; - let request = spec.buildRequests(slotConfigs, bidderRequest); + let request = spec.buildRequests(slotConfigs, syncAddFPDToBidderRequest(bidderRequest)); let ortbRequest = request.data; expect(ortbRequest).to.not.equal(null); expect(ortbRequest.site).to.not.equal(null); @@ -871,13 +585,15 @@ describe('PulsePoint Adapter Tests', function () { } }] }, - page: 'https://publisher.com/home', - ref: 'https://referrer', + page: 'http://pub.com/news', + ref: 'http://google.com', publisher: { - id: 'p10000' + id: 'p10000', + domain: 'pub.com' } }); }); + it('Verify impression/slot level first party data', function () { const bidderRequests = [{ placementCode: '/DfpAccount1/slot1', @@ -928,4 +644,27 @@ describe('PulsePoint Adapter Tests', function () { // assert bidderRequest value is used when available expect(mkRequest(Object.assign({}, { timeout: 6000 }, bidderRequest)).tmax).to.equal(6000) }); + + it('Verify deals', function () { + const bidRequests = deepClone(slotConfigs); + const deals = [{ + id: 'DEAL_ONE', + bidfloor: 1.1 + }, { + id: 'DEAL_TWO', + bidfloor: 2.2 + }]; + bidRequests[0].params.deals = deals; + const request = spec.buildRequests(bidRequests, syncAddFPDToBidderRequest(bidderRequest)); + expect(request.url).to.equal('https://bid.contextweb.com/header/ortb?src=prebid'); + expect(request.method).to.equal('POST'); + const ortbRequest = request.data; + // slot 1 + expect(ortbRequest.imp[0].tagid).to.equal('t10000'); + expect(ortbRequest.imp[0].pmp).to.not.be.undefined; + expect(ortbRequest.imp[0].pmp).to.deep.equal({ + private_auction: 0, + deals + }); + }) }); From e43ed0e760f2ebd5f147bf2b7140b4023f6abc16 Mon Sep 17 00:00:00 2001 From: Richard Lee <14349+dlackty@users.noreply.github.com> Date: Mon, 10 Jul 2023 22:16:06 +0800 Subject: [PATCH 15/92] userId Module: Update GAM APIs for eids (#10172) --- modules/userId/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index 6f330274bfbc..6d4ddf9aa9ec 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -743,14 +743,14 @@ function registerSignalSources() { if (!isGptPubadsDefined()) { return; } - window.googletag.encryptedSignalProviders = window.googletag.encryptedSignalProviders || []; + window.googletag.secureSignalProviders = window.googletag.secureSignalProviders || []; const encryptedSignalSources = config.getConfig('userSync.encryptedSignalSources'); if (encryptedSignalSources) { const registerDelay = encryptedSignalSources.registerDelay || 0; setTimeout(() => { encryptedSignalSources['sources'] && encryptedSignalSources['sources'].forEach(({ source, encrypt, customFunc }) => { source.forEach((src) => { - window.googletag.encryptedSignalProviders.push({ + window.googletag.secureSignalProviders.push({ id: src, collectorFunction: () => getEncryptedEidsForSource(src, encrypt, customFunc) }); From dcbebefb56dcec86ebea31512985a2f7a5a16175 Mon Sep 17 00:00:00 2001 From: Dmitry Borisenko <36228400+dmitrybor@users.noreply.github.com> Date: Mon, 10 Jul 2023 21:46:19 +0400 Subject: [PATCH 16/92] Sovrn Bid adapter: add GPP support for user sync (#10198) * EX-4755 Add: forwarding of auction id and transaction id as transaction ids for source and impression objects in the request * EX-4755 Change: source of impression level tid for forwarding * EX-4755 Change: source of auction level tid for forwarding * EX-4755 Refactor: unit test description * EX-4659 Add: passing gpp for user sync --------- Co-authored-by: Brody Harris <107206837+bharris-sovrn@users.noreply.github.com> --- modules/sovrnBidAdapter.js | 6 ++++- test/spec/modules/sovrnBidAdapter_spec.js | 28 +++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/modules/sovrnBidAdapter.js b/modules/sovrnBidAdapter.js index 0633c3e0cb84..0d077ad2ae33 100644 --- a/modules/sovrnBidAdapter.js +++ b/modules/sovrnBidAdapter.js @@ -248,7 +248,7 @@ export const spec = { } }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { try { const tracks = [] if (serverResponses && serverResponses.length !== 0) { @@ -262,6 +262,10 @@ export const spec = { if (uspConsent) { params.push(['us_privacy', uspConsent]); } + if (gppConsent) { + params.push(['gpp', gppConsent.gppString]); + params.push(['gpp_sid', gppConsent.applicableSections]) + } if (iidArr[0]) { params.push(['informer', iidArr[0]]); diff --git a/test/spec/modules/sovrnBidAdapter_spec.js b/test/spec/modules/sovrnBidAdapter_spec.js index d7d20f89c3b7..90913c6f1304 100644 --- a/test/spec/modules/sovrnBidAdapter_spec.js +++ b/test/spec/modules/sovrnBidAdapter_spec.js @@ -851,7 +851,7 @@ describe('sovrnBidAdapter', function() { url: `https://ap.lijit.com/beacon?gdpr_consent=${gdprConsent.consentString}&informer=13487408`, } - const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, '') + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, '', null) expect(returnStatement[0]).to.deep.equal(expectedReturnStatement) }) @@ -863,7 +863,22 @@ describe('sovrnBidAdapter', function() { url: `https://ap.lijit.com/beacon?us_privacy=${uspString}&informer=13487408`, } - const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, null, uspString) + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, null, uspString, null) + + expect(returnStatement[0]).to.deep.equal(expectedReturnStatement) + }) + + it('should include gpp consent string if present', function() { + const gppConsent = { + applicableSections: [1, 2], + gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN' + } + const expectedReturnStatement = { + type: 'iframe', + url: `https://ap.lijit.com/beacon?gpp=${gppConsent.gppString}&gpp_sid=${gppConsent.applicableSections}&informer=13487408`, + } + + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, null, '', gppConsent) expect(returnStatement[0]).to.deep.equal(expectedReturnStatement) }) @@ -874,12 +889,17 @@ describe('sovrnBidAdapter', function() { consentString: 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A==' } const uspString = '1NYN' + const gppConsent = { + applicableSections: [1, 2], + gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN' + } + const expectedReturnStatement = { type: 'iframe', - url: `https://ap.lijit.com/beacon?gdpr_consent=${gdprConsent.consentString}&us_privacy=${uspString}&informer=13487408`, + url: `https://ap.lijit.com/beacon?gdpr_consent=${gdprConsent.consentString}&us_privacy=${uspString}&gpp=${gppConsent.gppString}&gpp_sid=${gppConsent.applicableSections}&informer=13487408`, } - const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, uspString) + const returnStatement = spec.getUserSyncs(syncOptions, serverResponse, gdprConsent, uspString, gppConsent) expect(returnStatement[0]).to.deep.equal(expectedReturnStatement) }) From 38aacce1e58112936d1d2f0812c3c364c6c2e4e1 Mon Sep 17 00:00:00 2001 From: Samuel Adu Date: Mon, 10 Jul 2023 18:52:12 +0100 Subject: [PATCH 17/92] Yahoo Bid Adapter: Rebrand from to Yahoo Advertising (#10125) * Yahoo bid adapter rebrand * Add deleted modules * Addressing PR feedback * Updated docs * Renamed spec file * Ensure consent data is pulled and sent correctly --------- Co-authored-by: slimkrazy --- modules/yahoosspBidAdapter.js | 71 +- modules/yahoosspBidAdapter.md | 191 ++--- test/spec/modules/yahoosspBidAdapter_spec.js | 716 +++++++++++-------- 3 files changed, 536 insertions(+), 442 deletions(-) diff --git a/modules/yahoosspBidAdapter.js b/modules/yahoosspBidAdapter.js index 7a414c0d2ee3..a66d76f86897 100644 --- a/modules/yahoosspBidAdapter.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 = 'yahoossp'; +const BIDDER_CODE = 'yahooAds'; +const BIDDER_ALIASES = ['yahoossp', 'yahooAdvertising'] const GVLID = 25; -const ADAPTER_VERSION = '1.0.2'; +const ADAPTER_VERSION = '1.1.0'; const PREBID_VERSION = '$prebid.version$'; const DEFAULT_BID_TTL = 300; const TEST_MODE_DCN = '8a969516017a7a396ec539d97f540011'; @@ -49,13 +50,17 @@ const SUPPORTED_USER_ID_SOURCES = [ 'quantcast.com', 'tapad.com', 'uidapi.com', - 'verizonmedia.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]), @@ -114,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); @@ -155,8 +161,8 @@ function getPubIdMode(bid) { return pubIdMode; }; -function getAdapterMode() { - let adapterMode = config.getConfig('yahoossp.mode'); +function getAdapterMode(bid) { + let adapterMode = getConfigValue(bid, 'mode'); adapterMode = adapterMode ? adapterMode.toLowerCase() : undefined; if (typeof adapterMode === 'undefined' || adapterMode === BANNER) { return BANNER; @@ -177,7 +183,7 @@ function getResponseFormat(bid) { }; function getFloorModuleData(bid) { - const adapterMode = getAdapterMode(); + const adapterMode = getAdapterMode(bid); const getFloorRequestObject = { currency: deepAccess(bid, 'params.bidOverride.cur') || DEFAULT_CURRENCY, mediaType: adapterMode, @@ -187,7 +193,7 @@ function getFloorModuleData(bid) { }; function filterBidRequestByMode(validBidRequests) { - const mediaTypesMode = getAdapterMode(); + const mediaTypesMode = getAdapterMode(validBidRequests[0]); let result = []; if (mediaTypesMode === BANNER) { result = validBidRequests.filter(bid => { @@ -244,7 +250,7 @@ function validateAppendObject(validationType, allowedKeys, inputObject, appendTo }; function getTtl(bidderRequest) { - const globalTTL = config.getConfig('yahoossp.ttl'); + const globalTTL = getConfigValue(bidderRequest, 'ttl'); return globalTTL ? validateTTL(globalTTL) : validateTTL(deepAccess(bidderRequest, 'params.ttl')); }; @@ -276,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: { @@ -332,7 +338,7 @@ function generateOpenRtbObject(bidderRequest, bid) { }; function appendImpObject(bid, openRtbObject) { - const mediaTypeMode = getAdapterMode(); + const mediaTypeMode = getAdapterMode(bid); if (openRtbObject && bid) { const impObject = { @@ -494,20 +500,21 @@ function appendFirstPartyData(outBoundBidRequest, bid) { function generateServerRequest({payload, requestOptions, bidderRequest}) { const pubIdMode = getPubIdMode(bidderRequest); - let sspEndpoint = config.getConfig('yahoossp.endpoint') || SSP_ENDPOINT_DCN_POS; + const overrideEndpoint = getConfigValue(bidderRequest, 'endpoint'); + let sspEndpoint = overrideEndpoint || SSP_ENDPOINT_DCN_POS; if (pubIdMode === true) { - sspEndpoint = config.getConfig('yahoossp.endpoint') || SSP_ENDPOINT_PUBID; + sspEndpoint = overrideEndpoint || SSP_ENDPOINT_PUBID; }; if (deepAccess(bidderRequest, 'params.testing.e2etest') === true) { - logInfo('yahoossp adapter e2etest mode is active'); + logInfo('Adapter e2etest mode is active'); requestOptions.withCredentials = false; if (pubIdMode === true) { payload.site.id = TEST_MODE_PUBID_DCN; } else { - const mediaTypeMode = getAdapterMode(); + const mediaTypeMode = getAdapterMode(bidderRequest); payload.site.id = TEST_MODE_DCN; payload.imp.forEach(impObject => { impObject.ext.e2eTestMode = true; @@ -516,8 +523,9 @@ function generateServerRequest({payload, requestOptions, bidderRequest}) { } 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'); + 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`); } }); } @@ -528,7 +536,7 @@ function generateServerRequest({payload, requestOptions, bidderRequest}) { method: 'POST', data: payload, options: requestOptions, - bidderRequest: bidderRequest + bidderRequest // Additional data for use in interpretResponse() }; }; @@ -547,7 +555,7 @@ function createRenderer(bidderRequest, bidResponse) { }, deepAccess(bidderRequest, 'params.testing.renderer.setTimeout') || DEFAULT_RENDERER_TIMEOUT); }); } catch (error) { - logWarn('yahoossp renderer error: setRender() failed', error); + logWarn('Renderer error: setRender() failed', error); } return renderer; } @@ -557,7 +565,7 @@ function createRenderer(bidderRequest, bidResponse) { export const spec = { code: BIDDER_CODE, gvlid: GVLID, - aliases: [], + aliases: BIDDER_ALIASES, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function(bid) { @@ -570,14 +578,14 @@ export const spec = { ) { return true; } else { - logWarn('yahoossp bidder params missing or incorrect, please pass object with either: dcn & pos OR pubId'); + 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('yahoossp Adapter: buildRequests called with either empty "validBidRequests" or "bidderRequest"'); + logWarn('buildRequests called with either empty "validBidRequests" or "bidderRequest"'); return undefined; }; @@ -592,13 +600,12 @@ export const spec = { const filteredBidRequests = filterBidRequestByMode(validBidRequests); - if (config.getConfig('yahoossp.singleRequestMode') === true) { + if (getConfigValue(bidderRequest, 'singleRequestMode') === true) { const payload = generateOpenRtbObject(bidderRequest, filteredBidRequests[0]); filteredBidRequests.forEach(bid => { appendImpObject(bid, payload); }); - - return generateServerRequest({payload, requestOptions, bidderRequest}); + return [generateServerRequest({payload, requestOptions, bidderRequest})]; } return filteredBidRequests.map(bid => { @@ -608,12 +615,11 @@ export const spec = { }); }, - interpretResponse: function(serverResponse, { data, bidderRequest }) { + 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; @@ -628,7 +634,6 @@ export const spec = { let bidResponse = { adId: deepAccess(bid, 'adId') ? bid.adId : bid.impid || bid.crid, - adUnitCode: bidderRequest.adUnitCode, requestId: bid.impid, cpm: cpm, width: bid.w, diff --git a/modules/yahoosspBidAdapter.md b/modules/yahoosspBidAdapter.md index 35a640b92b12..c8c42930e5bc 100644 --- a/modules/yahoosspBidAdapter.md +++ b/modules/yahoosspBidAdapter.md @@ -1,10 +1,10 @@ # Overview -**Module Name:** yahoossp Bid Adapter +**Module Name:** Yahoo Advertising 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. +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 @@ -21,49 +21,51 @@ The Yahoo SSP Bid Adapter is an OpenRTB interface that consolidates all previous # 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. +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 yahoossp.mode 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. * **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 +* **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. +**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({ - yahoossp: { + yahooAds: { mode: 'banner' // 'all', 'video', 'banner' (default) } }); ``` + # Integration Options -The `yahoossp` 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". +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). +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 any existing accounts on Yahoo SSP (aka: aol, oneMobile, oneDisplay). + 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 'yahoossp' bid adapter to generate an outbound bid-request to our Yahoo SSP 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 SSP Site/App inventory parameter. - B. **pos:** Yahoo SSP position inventory parameter. + A. **dcn:** Yahoo Advertising Site/App inventory parameter. + B. **pos:** Yahoo Advertising position inventory parameter. ### Example: dcn & pos Mandatory Parameters (Single banner adUnit) ```javascript @@ -76,10 +78,10 @@ const adUnits = [{ }, bids: [ { - bidder: 'yahoossp', + bidder: 'yahooAds', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from SSP - pos: '8a969978017a7aaabab4ab0bc01a0009' // Placement ID provided from SSP + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising + pos: '8a969978017a7aaabab4ab0bc01a0009' // Placement ID provided by Yahoo Advertising } } ] @@ -87,10 +89,10 @@ const adUnits = [{ ``` ## pubId -The minimal requirements for the 'yahoossp' bid adapter to generate an outbound bid-request to our Yahoo SSP 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 SSP Publisher ID (AKA oneVideo pubId/Exchange name) + A. **pubId:** Yahoo Advertising Publisher ID (AKA oneVideo pubId/Exchange name) ### Example: pubId Mandatory Parameters (Single banner adUnit) ```javascript @@ -103,14 +105,15 @@ const adUnits = [{ }, bids: [ { - bidder: 'yahoossp', + bidder: 'yahooAds', params: { - pubId: 'DemoPublisher', // Publisher External ID provided from Yahoo SSP. + pubId: 'DemoPublisher', // Publisher defined external ID as configured by Yahoo Advertising. } } ] }]; ``` + # Advanced adUnit Examples: ## Banner ```javascript @@ -124,21 +127,22 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP - pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided from Yahoo SSP + 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 SSP Placement type (in-stream) matches the adUnit video inventory type. +**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({ - yahoossp: { + yahooAds: { mode: 'video' } }); @@ -156,20 +160,21 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP - pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP + 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 SSP Placement type (in-feed/ in-article) matches the adUnit video inventory type. +**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({ - yahoossp: { + yahooAds: { mode: 'video' } }); @@ -187,22 +192,22 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP - pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP + 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 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. - +**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 pbjs.setConfig({ - yahoossp: { + yahooAds: { mode: 'all' } }); @@ -223,19 +228,18 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP - pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided from Yahoo SSP + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising + pos: '8a96958a017a7a57ac375d50c0c700cc', // Placement ID provided by Yahoo Advertising } }] }]; ``` # Optional: Schain module support -The yahoossp adapter supports the Prebid.org Schain module and will pass it through to our Yahoo SSP +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 pbjs.setConfig({ @@ -256,7 +260,7 @@ For further details please see, https://docs.prebid.org/dev-docs/modules/schain. ## Bidder Specific Schain Example: ```javascript pbjs.setBidderConfig({ - "bidders": ['yahoossp'], // 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", @@ -275,12 +279,10 @@ For further details please see, https://docs.prebid.org/dev-docs/modules/schain. ``` # 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". +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. - ```javascript const adUnits = [{ code: 'override-pricefloor', @@ -292,10 +294,10 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { - dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided from Yahoo SSP - pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided from Yahoo SSP + dcn: '8a969516017a7a396ec539d97f540011', // Site/App ID provided by Yahoo Advertising + pos: '8a969978017a7aaabab4ab0bc01a0009', // Placement ID provided by Yahoo Advertising bidOverride :{ imp: { bidfloor: 5.00 // bidOverride priceFloor @@ -310,21 +312,21 @@ 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 yahoossp 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: -1. Set the yahoossp 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. -**Important!** E2E Testing Mode only works when the Bidder Request Mode is set explicitly to either 'banner' or 'video'. +**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: { + yahooAds: { mode: 'banner' // select 'banner' or 'video' to define what response to load } }); @@ -338,7 +340,7 @@ const adUnits = [{ }, bids: [ { - bidder: 'yahoossp', + bidder: 'yahooAds', params: { testing: { e2etest: true // Activate E2E Test mode @@ -353,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({ - yahoossp: { + yahooAds: { mode: 'video' } }); @@ -371,7 +373,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { testing: { e2etest: true // Activate E2E Test mode @@ -382,8 +384,8 @@ const adUnits = [{ ``` # Optional: First Party Data -The yahoossp adapter now supports first party data passed via: -1. Global ortb2 object using pbjs.setConfig() +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 ## Global First Party Data "ortb2" @@ -392,15 +394,15 @@ For further details please see, https://docs.prebid.org/features/firstPartyData. pbjs.setConfig({ ortb2: { site: { - name: 'yahooAdTech', - domain: 'yahooadtech.com', + name: 'Yahoo Advertising', + domain: 'yahooadvertising.com', cat: ['IAB2'], sectioncat: ['IAB2-2'], pagecat: ['IAB2-2'], - page: 'https://page.yahooadtech.com/here.html', - ref: 'https://ref.yahooadtech.com/there.html', + page: 'https://page.yahooadvertising.com.com/here.html', + ref: 'https://ref.yahooadvertising.com.com/there.html', keywords:'yahoo, ad, tech', - search: 'SSP', + search: 'header bidding', content: { id: '1234', title: 'Title', @@ -521,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', @@ -541,7 +543,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { pubdId: 'DemoPublisher' } @@ -551,7 +553,7 @@ const adUnits = [{ ``` # Optional: Bidder bidOverride Parameters -The yahoossp adapter allows passing override data to the outbound bid-request in 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. @@ -577,7 +579,6 @@ Currently the bidOverride object only accepts the following: * device * ip - ```javascript const adUnits = [{ code: 'bidOverride-adUnit', @@ -590,7 +591,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -615,7 +616,7 @@ const adUnits = [{ } }, site: { - page: 'https://yahoossp-bid-adapter.com', + page: 'https://yahooAdvertising-bid-adapter.com', }, device: { ip: "1.2.3.4" @@ -630,7 +631,7 @@ const adUnits = [{ 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 +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 = [{ @@ -644,7 +645,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -660,14 +661,14 @@ const adUnits = [{ ``` # 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. +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. ## Global TTL ```javascript pbjs.setConfig({ - yahoossp: { + yahooAds: { ttl: 300 } }); @@ -683,7 +684,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -702,7 +703,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -710,10 +711,11 @@ const adUnits = [{ } }] }] + ``` # 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 +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 = [{ @@ -727,7 +729,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { dcn: '8a969516017a7a396ec539d97f540011', pos: '8a96958a017a7a57ac375d50c0c700cc', @@ -744,7 +746,7 @@ const adUnits = [{ ``` ## 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: +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. @@ -762,7 +764,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { pubId: 'DemoPublisher', siteId: '1234567'; @@ -772,13 +774,13 @@ const adUnits = [{ ``` ## 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 +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 miss matching. +**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 ad-server. +**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', @@ -791,7 +793,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { pubId: 'DemoPublisher', siteId: '1234567', @@ -801,7 +803,7 @@ const adUnits = [{ }] ``` ### 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. +**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', @@ -814,7 +816,7 @@ const adUnits = [{ } }, bids: [{ - bidder: 'yahoossp', + bidder: 'yahooAds', params: { pubId: 'DemoPublisher', placementId: 'header-250x300' @@ -824,9 +826,8 @@ const adUnits = [{ ``` # 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.). +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 - +Yahoo Advertsing diff --git a/test/spec/modules/yahoosspBidAdapter_spec.js b/test/spec/modules/yahoosspBidAdapter_spec.js index b0030c893711..c05841730829 100644 --- a/test/spec/modules/yahoosspBidAdapter_spec.js +++ b/test/spec/modules/yahoosspBidAdapter_spec.js @@ -3,7 +3,6 @@ import { config } from 'src/config.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; import { spec } from 'modules/yahoosspBidAdapter.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,20 @@ 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 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'; // 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 +80,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 +108,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 +176,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('yahooAds'); + }); + + it('should define the correct bidder aliases', () => { + expect(spec.aliases).to.deep.equal(['yahoossp', 'yahooAdvertising']); }); it('should define the correct vendor ID', () => { - expect(spec.gvlid).to.equal(25) + expect(spec.gvlid).to.equal(25); }); }); @@ -271,33 +280,62 @@ describe('YahooSSP 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); + }); + }; + }); }); }); }); @@ -647,9 +685,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 +707,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 +728,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 +745,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 +856,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 + yahooAds: { + 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 +910,15 @@ describe('YahooSSP Bid Adapter:', () => { bidderRequest.bids = validBidRequests; config.setConfig({ - yahoossp: { + yahooAds: { 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 +926,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 +944,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 +954,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 +970,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 +983,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 +1006,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 +1015,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 @@ -1013,7 +1064,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 +1074,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 +1090,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 +1111,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 +1124,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 +1140,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 +1234,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 +1246,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 +1259,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 +1273,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 +1285,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 +1317,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 +1333,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 +1370,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 +1477,22 @@ describe('YahooSSP Bid Adapter:', () => { }); describe('for mediaTypes: "video"', () => { + beforeEach(() => { + config.setConfig({ + yahooAds: { + mode: VIDEO + } + }); + }); + + afterEach(() => { + config.setConfig({ + yahooAds: { + mode: undefined + } + }); + }); + it('should insert video VPAID payload into vastXml', () => { const { serverResponse, bidderRequest } = generateResponseMock('video'); const response = spec.interpretResponse(serverResponse, {bidderRequest}); @@ -1434,28 +1510,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({ + yahooAds: { + 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 +1576,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['yahooAds'] = { 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({ + yahooAds: { 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({ + yahooAds: { 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 b38612fc96c81642e922cea0283b5299d3b3f00b Mon Sep 17 00:00:00 2001 From: pm-nitin-shirsat <107102698+pm-nitin-shirsat@users.noreply.github.com> Date: Mon, 10 Jul 2023 23:26:44 +0530 Subject: [PATCH 18/92] PubMatic Bid Adapter : add GPP support (#10165) * Implement functionality for deal priority * Update test cases * kick off test manually * Added support of GPP to PubMatic adapter * gpp_sid in user syncs supposed to encode as a string, not an array * Remove extra space * Remove trailing spaces --------- Co-authored-by: Chris Huie --- modules/pubmaticBidAdapter.js | 17 ++- test/spec/modules/pubmaticBidAdapter_spec.js | 119 +++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/modules/pubmaticBidAdapter.js b/modules/pubmaticBidAdapter.js index eb2ad8ccb03f..ce0da73f751c 100644 --- a/modules/pubmaticBidAdapter.js +++ b/modules/pubmaticBidAdapter.js @@ -1194,6 +1194,15 @@ export const spec = { deepSetValue(payload, 'regs.ext.us_privacy', bidderRequest.uspConsent); } + // Attaching GPP Consent Params + if (bidderRequest?.gppConsent?.gppString) { + deepSetValue(payload, 'regs.gpp', bidderRequest.gppConsent.gppString); + deepSetValue(payload, 'regs.gpp_sid', bidderRequest.gppConsent.applicableSections); + } else if (bidderRequest?.ortb2?.regs?.gpp) { + deepSetValue(payload, 'regs.gpp', bidderRequest.ortb2.regs.gpp); + deepSetValue(payload, 'regs.gpp_sid', bidderRequest.ortb2.regs.gpp_sid); + } + // coppa compliance if (config.getConfig('coppa') === true) { deepSetValue(payload, 'regs.coppa', 1); @@ -1352,7 +1361,7 @@ export const spec = { /** * Register User Sync. */ - getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent, gppConsent) => { let syncurl = '' + publisherId; // Attaching GDPR Consent Params in UserSync url @@ -1366,6 +1375,12 @@ export const spec = { syncurl += '&us_privacy=' + encodeURIComponent(uspConsent); } + // GPP Consent + if (gppConsent?.gppString && gppConsent?.applicableSections?.length) { + syncurl += '&gpp=' + encodeURIComponent(gppConsent.gppString); + syncurl += '&gpp_sid=' + encodeURIComponent(gppConsent?.applicableSections?.join(',')); + } + // coppa compliance if (config.getConfig('coppa') === true) { syncurl += '&coppa=1'; diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 5d8d762e7936..53f5e12aa6d7 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -3057,6 +3057,76 @@ describe('PubMatic adapter', function () { }); } + describe('GPP', function() { + it('Request params check with GPP Consent', function () { + let bidRequest = { + gppConsent: { + 'gppString': 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', + 'fullGppData': { + 'sectionId': 3, + 'gppVersion': 1, + 'sectionList': [ + 5, + 7 + ], + 'applicableSections': [ + 5 + ], + 'gppString': 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', + 'pingData': { + 'cmpStatus': 'loaded', + 'gppVersion': '1.0', + 'cmpDisplayStatus': 'visible', + 'supportedAPIs': [ + 'tcfca', + 'usnat', + 'usca', + 'usva', + 'usco', + 'usut', + 'usct' + ], + 'cmpId': 31 + }, + 'eventName': 'sectionChange' + }, + 'applicableSections': [ + 5 + ], + 'apiVersion': 1 + } + }; + let request = spec.buildRequests(bidRequests, bidRequest); + let data = JSON.parse(request.data); + expect(data.regs.gpp).to.equal('DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN'); + expect(data.regs.gpp_sid[0]).to.equal(5); + }); + + it('Request params check without GPP Consent', function () { + let bidRequest = {}; + let request = spec.buildRequests(bidRequests, bidRequest); + let data = JSON.parse(request.data); + expect(data.regs).to.equal(undefined); + }); + + it('Request params check with GPP Consent read from ortb2', function () { + let bidRequest = { + ortb2: { + regs: { + 'gpp': 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', + 'gpp_sid': [ + 5 + ] + } + } + }; + let request = spec.buildRequests(bidRequests, bidRequest); + let data = JSON.parse(request.data); + expect(data.regs.gpp).to.equal('DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN'); + expect(data.regs.gpp_sid[0]).to.equal(5); + }); + }); + describe('Fledge', function() { it('should not send imp.ext.ae when FLEDGE is disabled, ', function () { let bidRequest = Object.assign([], bidRequests); @@ -3991,6 +4061,55 @@ describe('PubMatic adapter', function () { type: 'image', url: `${syncurl_image}&gdpr=1&gdpr_consent=foo&us_privacy=1NYN&coppa=1` }]); }); + + describe('GPP', function() { + it('should return userSync url without Gpp consent if gppConsent is undefined', () => { + const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, undefined); + expect(result).to.deep.equal([{ + type: 'iframe', url: `${syncurl_iframe}` + }]); + }); + + it('should return userSync url without Gpp consent if gppConsent.gppString is undefined', () => { + const gppConsent = { applicableSections: ['5'] }; + const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, gppConsent); + expect(result).to.deep.equal([{ + type: 'iframe', url: `${syncurl_iframe}` + }]); + }); + + it('should return userSync url without Gpp consent if gppConsent.applicableSections is undefined', () => { + const gppConsent = { gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN' }; + const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, gppConsent); + expect(result).to.deep.equal([{ + type: 'iframe', url: `${syncurl_iframe}` + }]); + }); + + it('should return userSync url without Gpp consent if gppConsent.applicableSections is an empty array', () => { + const gppConsent = { gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', applicableSections: [] }; + const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, gppConsent); + expect(result).to.deep.equal([{ + type: 'iframe', url: `${syncurl_iframe}` + }]); + }); + + it('should concatenate gppString and applicableSections values in the returned userSync iframe url', () => { + const gppConsent = { gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', applicableSections: [5] }; + const result = spec.getUserSyncs({iframeEnabled: true}, undefined, undefined, undefined, gppConsent); + expect(result).to.deep.equal([{ + type: 'iframe', url: `${syncurl_iframe}&gpp=${encodeURIComponent(gppConsent.gppString)}&gpp_sid=${encodeURIComponent(gppConsent.applicableSections)}` + }]); + }); + + it('should concatenate gppString and applicableSections values in the returned userSync image url', () => { + const gppConsent = { gppString: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', applicableSections: [5] }; + const result = spec.getUserSyncs({iframeEnabled: false}, undefined, undefined, undefined, gppConsent); + expect(result).to.deep.equal([{ + type: 'image', url: `${syncurl_image}&gpp=${encodeURIComponent(gppConsent.gppString)}&gpp_sid=${encodeURIComponent(gppConsent.applicableSections)}` + }]); + }); + }); }); if (FEATURES.VIDEO) { From 908872d58fb3063cd204f55b1ad21c05d06adcfa Mon Sep 17 00:00:00 2001 From: YakirLavi <73641910+YakirLavi@users.noreply.github.com> Date: Mon, 10 Jul 2023 20:57:46 +0300 Subject: [PATCH 19/92] MinuteMedia Bid Adapter: support missing params and tests (#10195) * add Rise adapter * fixes * change param isOrg to org * Rise adapter * change email for rise * fix circle failed * bump * bump * bump * remove space * Upgrade Rise adapter to 5.0 * added isWrapper param * addes is_wrapper parameter to documentation * added is_wrapper to test * removed isWrapper * Rise Bid Adapter: support Coppa param (#24) * MinuteMedia Bid Adapter: support Coppa param (#25) * Revert "MinuteMedia Bid Adapter: support Coppa param (#25)" (#26) This reverts commit 66c4e7b46121afc5331c8bca6e2fc972fc55f090. * bump * update coppa fetch * setting coppa param update * update Coppa tests * update test naming * Rise Bid Adapter: support plcmt and sua (#27) * update minuteMediaBidAdapter - support missing params (#29) --------- Co-authored-by: Noam Tzuberi Co-authored-by: noamtzu Co-authored-by: Noam Tzuberi Co-authored-by: Laslo Chechur Co-authored-by: OronW <41260031+OronW@users.noreply.github.com> Co-authored-by: lasloche <62240785+lasloche@users.noreply.github.com> Co-authored-by: inna Co-authored-by: YakirLavi --- modules/minutemediaBidAdapter.js | 19 ++++++++- .../modules/minutemediaBidAdapter_spec.js | 41 ++++++++++++++++--- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/modules/minutemediaBidAdapter.js b/modules/minutemediaBidAdapter.js index 85c1386957d4..bb0bb76bdbc3 100644 --- a/modules/minutemediaBidAdapter.js +++ b/modules/minutemediaBidAdapter.js @@ -307,6 +307,15 @@ function generateBidParameters(bid, bidderRequest) { bidObject.placementId = placementId; } + const mimes = deepAccess(bid, `mediaTypes.${mediaType}.mimes`); + if (mimes) { + bidObject.mimes = mimes; + } + const api = deepAccess(bid, `mediaTypes.${mediaType}.api`); + if (api) { + bidObject.api = api; + } + const sua = deepAccess(bid, `ortb2.device.sua`); if (sua) { bidObject.sua = sua; @@ -358,6 +367,11 @@ function generateBidParameters(bid, bidderRequest) { bidObject.linearity = linearity; } + const protocols = deepAccess(bid, `mediaTypes.video.protocols`); + if (protocols) { + bidObject.protocols = protocols; + } + const plcmt = deepAccess(bid, `mediaTypes.video.plcmt`); if (plcmt) { bidObject.plcmt = plcmt; @@ -398,7 +412,8 @@ function generateGeneralParams(generalObject, bidderRequest) { dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0, device_type: getDeviceType(navigator.userAgent), ua: navigator.userAgent, - session_id: getBidIdParameter('bidderRequestId', generalObject), + is_wrapper: !!generalBidParams.isWrapper, + session_id: generalBidParams.sessionId || getBidIdParameter('bidderRequestId', generalObject), tmax: timeout } @@ -441,7 +456,7 @@ function generateGeneralParams(generalObject, bidderRequest) { if (bidderRequest && bidderRequest.refererInfo) { generalParams.referrer = deepAccess(bidderRequest, 'refererInfo.ref'); - generalParams.page_url = deepAccess(bidderRequest, 'refererInfo.page') || window.location.href + generalParams.page_url = deepAccess(bidderRequest, 'refererInfo.page') || deepAccess(window, 'location.href'); } return generalParams diff --git a/test/spec/modules/minutemediaBidAdapter_spec.js b/test/spec/modules/minutemediaBidAdapter_spec.js index 0d12e422cd7b..48f694bc79d6 100644 --- a/test/spec/modules/minutemediaBidAdapter_spec.js +++ b/test/spec/modules/minutemediaBidAdapter_spec.js @@ -104,6 +104,9 @@ describe('minutemediaAdapter', function () { bidderCode: 'minutemedia', } const placementId = '12345678'; + const api = [1, 2]; + const mimes = ['application/javascript', 'video/mp4', 'video/quicktime']; + const protocols = [2, 3, 5, 6]; it('sends the placementId to ENDPOINT via POST', function () { bidRequests[0].params.placementId = placementId; @@ -111,15 +114,22 @@ describe('minutemediaAdapter', function () { expect(request.data.bids[0].placementId).to.equal(placementId); }); - it('sends bid request to ENDPOINT via POST', function () { + it('sends the plcmt to ENDPOINT via POST', function () { const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.url).to.equal(ENDPOINT); - expect(request.method).to.equal('POST'); + expect(request.data.bids[0].plcmt).to.equal(1); }); - it('sends the plcmt to ENDPOINT via POST', function () { + it('sends the is_wrapper parameter to ENDPOINT via POST', function() { const request = spec.buildRequests(bidRequests, bidderRequest); - expect(request.data.bids[0].plcmt).to.equal(1); + expect(request.data.params).to.be.an('object'); + expect(request.data.params).to.have.property('is_wrapper'); + expect(request.data.params.is_wrapper).to.equal(false); + }); + + it('sends bid request to ENDPOINT via POST', function () { + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); }); it('sends bid request to TEST ENDPOINT via POST', function () { @@ -133,6 +143,27 @@ describe('minutemediaAdapter', function () { expect(request.data.bids[0].bidId).to.equal('299ffc8cca0b87'); }); + it('should send the correct supported api array', function () { + bidRequests[0].mediaTypes.video.api = api; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[0].api).to.be.an('array'); + expect(request.data.bids[0].api).to.eql([1, 2]); + }); + + it('should send the correct mimes array', function () { + bidRequests[1].mediaTypes.banner.mimes = mimes; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[1].mimes).to.be.an('array'); + expect(request.data.bids[1].mimes).to.eql(['application/javascript', 'video/mp4', 'video/quicktime']); + }); + + it('should send the correct protocols array', function () { + bidRequests[0].mediaTypes.video.protocols = protocols; + const request = spec.buildRequests(bidRequests, bidderRequest); + expect(request.data.bids[0].protocols).to.be.an('array'); + expect(request.data.bids[0].protocols).to.eql([2, 3, 5, 6]); + }); + it('should send the correct sizes array', function () { const request = spec.buildRequests(bidRequests, bidderRequest); expect(request.data.bids[0].sizes).to.be.an('array'); From a8d844b523648923ba9f375bd172fba02606f6f0 Mon Sep 17 00:00:00 2001 From: TheMediaGrid <44166371+TheMediaGrid@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:58:17 +0200 Subject: [PATCH 20/92] TheMediaGrid: support on data deletion request (#10095) * TheMediaGrid: added onDataDeletionRequest handler * TheMediaGrid: fix endpoint for USP delete data handler * TheMediaGrid: added tests --- modules/gridBidAdapter.js | 19 +++++++++++ test/spec/modules/gridBidAdapter_spec.js | 42 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/modules/gridBidAdapter.js b/modules/gridBidAdapter.js index 8285b3308b7b..ee8712b1de36 100644 --- a/modules/gridBidAdapter.js +++ b/modules/gridBidAdapter.js @@ -9,6 +9,7 @@ import { isNumber, isStr } from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { Renderer } from '../src/Renderer.js'; import { VIDEO, BANNER } from '../src/mediaTypes.js'; @@ -17,6 +18,7 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'grid'; const ENDPOINT_URL = 'https://grid.bidswitch.net/hbjson'; +const USP_DELETE_DATA_HANDLER = 'https://media.grid.bidswitch.net/uspapi_delete' const SYNC_URL = 'https://x.bidswitch.net/sync?ssp=themediagrid'; const TIME_TO_LIVE = 360; @@ -462,6 +464,23 @@ export const spec = { url: syncUrl + params }; } + }, + + ajaxCall: function(url, cb, data, options) { + return ajax(url, cb, data, options); + }, + + onDataDeletionRequest: function(data) { + const uids = []; + const aliases = [spec.code, ...spec.aliases.map((alias) => alias.code || alias)]; + data.forEach(({ bids }) => bids && bids.forEach(({ bidder, params }) => { + if (aliases.includes(bidder) && params && params.uid) { + uids.push(params.uid); + } + })); + if (uids.length) { + spec.ajaxCall(USP_DELETE_DATA_HANDLER, () => {}, JSON.stringify({ uids }), {contentType: 'application/json', method: 'POST'}); + } } }; diff --git a/test/spec/modules/gridBidAdapter_spec.js b/test/spec/modules/gridBidAdapter_spec.js index c1edff46dc09..2f6e3990d825 100644 --- a/test/spec/modules/gridBidAdapter_spec.js +++ b/test/spec/modules/gridBidAdapter_spec.js @@ -2,6 +2,7 @@ import { expect } from 'chai'; import { spec, resetUserSync, getSyncUrl, storage } from 'modules/gridBidAdapter.js'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { config } from 'src/config.js'; +import {ENDPOINT_DOMAIN, ENDPOINT_PROTOCOL} from '../../../modules/adpartnerBidAdapter'; describe('TheMediaGrid Adapter', function () { const adapter = newBidder(spec); @@ -1465,6 +1466,47 @@ describe('TheMediaGrid Adapter', function () { }); }); + describe('onDataDeletionRequest', function() { + let ajaxStub; + beforeEach(function() { + ajaxStub = sinon.stub(spec, 'ajaxCall'); + }); + + it('should send right request on onDataDeletionRequest call', function() { + spec.onDataDeletionRequest([{ + bids: [ + { + bidder: 'grid', + params: { + uid: 1 + } + }, + { + bidder: 'grid', + params: { + uid: 2 + } + }, + { + bidder: 'another', + params: { + uid: 3 + } + }, + { + bidder: 'gridNM', + params: { + uid: 4 + } + } + ], + }]); + expect(ajaxStub.calledOnce).to.equal(true); + expect(ajaxStub.firstCall.args[0]).to.equal('https://media.grid.bidswitch.net/uspapi_delete'); + expect(ajaxStub.firstCall.args[2]).to.equal('{"uids":[1,2,4]}'); + }); + }); + describe('user sync', function () { const syncUrl = getSyncUrl(); From 822c09e5e10077528431054e90073bc4d58099f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Iwa=C5=84czak?= <36727380+piwanczak@users.noreply.github.com> Date: Mon, 10 Jul 2023 19:59:44 +0200 Subject: [PATCH 21/92] Topics Fpd Module: Add RtbHouse topics network (#10107) * Update topicsFpdModule.md Addition of RTB House's Topics API iframe support * chore: make linter happy - fixing typo * m: post-review fixes * chore: changed domains * chore: changed domains * make tests happy --------- Co-authored-by: Przemyslaw Iwanczak --- modules/topicsFpdModule.js | 5 ++++- modules/topicsFpdModule.md | 4 ++++ test/spec/modules/topicsFpdModule_spec.js | 14 ++++++++++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/modules/topicsFpdModule.js b/modules/topicsFpdModule.js index 4cb11af48fca..97d86d5aff25 100644 --- a/modules/topicsFpdModule.js +++ b/modules/topicsFpdModule.js @@ -15,10 +15,13 @@ let LOAD_TOPICS_INITIALISE = false; const HAS_DEVICE_ACCESS = hasDeviceAccess(); const bidderIframeList = { - maxTopicCaller: 1, + maxTopicCaller: 2, bidders: [{ bidder: 'pubmatic', iframeURL: 'https://ads.pubmatic.com/AdServer/js/topics/topics_frame.html' + }, { + bidder: 'rtbhouse', + iframeURL: 'https://topics.authorizedvault.com/topicsapi.html' }] } export const coreStorage = getCoreStorageManager(MODULE_NAME); diff --git a/modules/topicsFpdModule.md b/modules/topicsFpdModule.md index b1bc3cb0d5ba..6ef0bf241dde 100644 --- a/modules/topicsFpdModule.md +++ b/modules/topicsFpdModule.md @@ -36,6 +36,10 @@ pbjs.setConfig({ bidder: 'pubmatic', iframeURL: 'https://ads.pubmatic.com/AdServer/js/topics/topics_frame.html', expiry: 7 // Configurable expiry days + },{ + bidder: 'rtbhouse', + iframeURL: 'https://topics.authorizedvault.com/topicsapi.html', + expiry: 7 // Configurable expiry days },{ bidder: 'rubicon', iframeURL: 'https://rubicon.com:8080/topics/fpd/topic.html', // dummy URL diff --git a/test/spec/modules/topicsFpdModule_spec.js b/test/spec/modules/topicsFpdModule_spec.js index 958489f2728a..22d7a98d45d7 100644 --- a/test/spec/modules/topicsFpdModule_spec.js +++ b/test/spec/modules/topicsFpdModule_spec.js @@ -372,11 +372,16 @@ describe('getCachedTopics()', () => { } ]; - const evt = { + const evt_pm = { data: '{"segment":{"domain":"ads.pubmatic.com","topics":[{"configVersion":"chrome.1","modelVersion":"2206021246","taxonomyVersion":"1","topic":165,"version":"chrome.1:1:2206021246"}],"bidder":"pubmatic"},"date":1669743901858}', origin: 'https://ads.pubmatic.com' }; + const evt_rh = { + data: '{"segment":{"domain":"topics.authorizedvault.com","topics":[{"configVersion":"chrome.1","modelVersion":"2206021246","taxonomyVersion":"1","topic":165,"version":"chrome.1:1:2206021246"}],"bidder":"rtbhouse"},"date":1669743901858}', + origin: 'https://topics.authorizedvault.com' + }; + let gdprDataHdlrStub; beforeEach(() => { gdprDataHdlrStub = sinon.stub(gdprDataHandler, 'getConsentData'); @@ -413,9 +418,10 @@ describe('getCachedTopics()', () => { it('should stored segments if receiveMessage event is triggerred with segment data', () => { return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) .then(({global}) => { - receiveMessage(evt); + receiveMessage(evt_pm); + receiveMessage(evt_rh); let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); - expect(segments.has('pubmatic')).to.equal(true); + expect(segments.has('pubmatic') || segments.has('rtbhouse')).to.equal(true); }); }); @@ -424,7 +430,7 @@ describe('getCachedTopics()', () => { storage.setDataInLocalStorage(topicStorageName, storedSegments); return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) .then(({global}) => { - receiveMessage(evt); + receiveMessage(evt_pm); let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); expect(segments.get('pubmatic')[2206021246].segment.length).to.equal(1); }); From ecc0a37b730a3c539e701b9f9a02754644cef4bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Jul 2023 11:41:02 -0400 Subject: [PATCH 22/92] Bump semver from 5.7.1 to 5.7.2 (#10205) Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2. - [Release notes](https://github.com/npm/node-semver/releases) - [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md) - [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2) --- updated-dependencies: - dependency-name: semver dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 98 +++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/package-lock.json b/package-lock.json index faec21d0499e..33bef1f1cb54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "7.52.0-pre", + "version": "8.4.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -6847,9 +6847,9 @@ } }, "node_modules/cac/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -9653,9 +9653,9 @@ } }, "node_modules/eslint/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -9903,9 +9903,9 @@ } }, "node_modules/execa/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -12090,9 +12090,9 @@ } }, "node_modules/gulp-cli/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -12432,9 +12432,9 @@ } }, "node_modules/gulp-eslint/node_modules/cross-spawn/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -15796,9 +15796,9 @@ } }, "node_modules/karma-coverage-istanbul-reporter/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -18739,9 +18739,9 @@ } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -21034,9 +21034,9 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -30515,9 +30515,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "strip-ansi": { @@ -32530,9 +32530,9 @@ "dev": true }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -32900,9 +32900,9 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "shebang-command": { @@ -34681,9 +34681,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "string-width": { @@ -34969,9 +34969,9 @@ }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true } } @@ -37585,9 +37585,9 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "source-map": { @@ -39761,9 +39761,9 @@ }, "dependencies": { "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -41495,9 +41495,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" }, "semver-greatest-satisfied-range": { "version": "1.1.0", From adfe8a72742051616a3ee23ab9713cabb7a5b25c Mon Sep 17 00:00:00 2001 From: Olivier Date: Tue, 11 Jul 2023 18:28:07 +0200 Subject: [PATCH 23/92] AdagioBidAdapter: remove useless `id` in adRequest (#10207) --- modules/adagioBidAdapter.js | 3 +-- test/spec/modules/adagioBidAdapter_spec.js | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index ca1891cac810..c28692de846b 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -38,7 +38,7 @@ import { userSync } from '../src/userSync.js'; const BIDDER_CODE = 'adagio'; const LOG_PREFIX = 'Adagio:'; const FEATURES_VERSION = '1'; -export const ENDPOINT = 'https://mp.4dex.io/prebid'; +export const ENDPOINT = 'http://127.0.0.1:8080/prebid'; const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; @@ -1133,7 +1133,6 @@ export const spec = { method: 'POST', url: ENDPOINT, data: { - id: generateUUID(), organizationId: organizationId, secure: secure, device: device, diff --git a/test/spec/modules/adagioBidAdapter_spec.js b/test/spec/modules/adagioBidAdapter_spec.js index 4b054824c7be..f9bf62206f5f 100644 --- a/test/spec/modules/adagioBidAdapter_spec.js +++ b/test/spec/modules/adagioBidAdapter_spec.js @@ -256,7 +256,6 @@ describe('Adagio bid adapter', () => { describe('buildRequests()', function() { const expectedDataKeys = [ - 'id', 'organizationId', 'secure', 'device', From 7e11273310814e7f0c390340ebb4cc438f5ef304 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 11 Jul 2023 09:39:33 -0700 Subject: [PATCH 24/92] gpdrEnforcement : add purpose 4 check for `transmitUfpd` (#10199) * gdprEnforcement: add purpose 4 enforcement * Guard additional user properties behind transmitUfpd --- modules/gdprEnforcement.js | 44 +++++++++++----------- src/activities/redactor.js | 2 +- test/spec/modules/gdprEnforcement_spec.js | 45 ++++++++++++++++++++++- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index 4bac4b0cf748..fa7418a90ab0 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -27,15 +27,16 @@ import { ACTIVITY_ENRICH_EIDS, ACTIVITY_FETCH_BIDS, ACTIVITY_REPORT_ANALYTICS, - ACTIVITY_SYNC_USER + ACTIVITY_SYNC_USER, ACTIVITY_TRANSMIT_UFPD } from '../src/activities/activities.js'; export const STRICT_STORAGE_ENFORCEMENT = 'strictStorageEnforcement'; const TCF2 = { - 'purpose1': {id: 1, name: 'storage'}, - 'purpose2': {id: 2, name: 'basicAds'}, - 'purpose7': {id: 7, name: 'measurement'} + purpose1: {id: 1, name: 'storage'}, + purpose2: {id: 2, name: 'basicAds'}, + purpose4: {id: 4, name: 'personalizedAds'}, + purpose7: {id: 7, name: 'measurement'}, }; /* @@ -55,6 +56,7 @@ const DEFAULT_RULES = [{ export let purpose1Rule; export let purpose2Rule; +export let purpose4Rule; export let purpose7Rule; export let enforcementRules; @@ -62,6 +64,7 @@ export let enforcementRules; const storageBlocked = new Set(); const biddersBlocked = new Set(); const analyticsBlocked = new Set(); +const ufpdBlocked = new Set(); let hooksAdded = false; let strictStorageEnforcement = false; @@ -232,6 +235,8 @@ export const fetchBidsRule = ((rule) => { export const reportAnalyticsRule = gdprRule(7, () => purpose7Rule, analyticsBlocked, (params) => getGvlidFromAnalyticsAdapter(params[ACTIVITY_PARAM_COMPONENT_NAME], params[ACTIVITY_PARAM_ANL_CONFIG])); +export const transmitUfpdRule = gdprRule(4, () => purpose4Rule, ufpdBlocked); + /** * Compiles the TCF2.0 enforcement results into an object, which is emitted as an event payload to "tcf2Enforcement" event. */ @@ -243,27 +248,20 @@ function emitTCF2FinalResults() { const tcf2FinalResults = { storageBlocked: formatSet(storageBlocked), biddersBlocked: formatSet(biddersBlocked), - analyticsBlocked: formatSet(analyticsBlocked) + analyticsBlocked: formatSet(analyticsBlocked), + ufpdBlocked: formatSet(ufpdBlocked), }; events.emit(CONSTANTS.EVENTS.TCF2_ENFORCEMENT, tcf2FinalResults); - [storageBlocked, biddersBlocked, analyticsBlocked].forEach(el => el.clear()); + [storageBlocked, biddersBlocked, analyticsBlocked, ufpdBlocked].forEach(el => el.clear()); } events.on(CONSTANTS.EVENTS.AUCTION_END, emitTCF2FinalResults); -/* - Set of callback functions used to detect presence of a TCF rule, passed as the second argument to find(). -*/ -const hasPurpose1 = (rule) => { - return rule.purpose === TCF2.purpose1.name; -}; -const hasPurpose2 = (rule) => { - return rule.purpose === TCF2.purpose2.name; -}; -const hasPurpose7 = (rule) => { - return rule.purpose === TCF2.purpose7.name; -}; +function hasPurpose(purposeNo) { + const pname = TCF2[`purpose${purposeNo}`].name; + return (rule) => rule.purpose === pname; +} /** * A configuration function that initializes some module variables, as well as adds hooks @@ -279,9 +277,10 @@ export function setEnforcementConfig(config) { } strictStorageEnforcement = !!deepAccess(config, STRICT_STORAGE_ENFORCEMENT); - purpose1Rule = find(enforcementRules, hasPurpose1); - purpose2Rule = find(enforcementRules, hasPurpose2); - purpose7Rule = find(enforcementRules, hasPurpose7); + purpose1Rule = find(enforcementRules, hasPurpose(1)); + purpose2Rule = find(enforcementRules, hasPurpose(2)); + purpose4Rule = find(enforcementRules, hasPurpose(4)) + purpose7Rule = find(enforcementRules, hasPurpose(7)); if (!purpose1Rule) { purpose1Rule = DEFAULT_RULES[0]; @@ -301,6 +300,9 @@ export function setEnforcementConfig(config) { if (purpose2Rule) { RULE_HANDLES.push(registerActivityControl(ACTIVITY_FETCH_BIDS, RULE_NAME, fetchBidsRule)); } + if (purpose4Rule) { + RULE_HANDLES.push(registerActivityControl(ACTIVITY_TRANSMIT_UFPD, RULE_NAME, transmitUfpdRule)); + } if (purpose7Rule) { RULE_HANDLES.push(registerActivityControl(ACTIVITY_REPORT_ANALYTICS, RULE_NAME, reportAnalyticsRule)); } diff --git a/src/activities/redactor.js b/src/activities/redactor.js index d50df72648c7..5942ee171520 100644 --- a/src/activities/redactor.js +++ b/src/activities/redactor.js @@ -8,7 +8,7 @@ import { ACTIVITY_TRANSMIT_UFPD } from './activities.js'; -export const ORTB_UFPD_PATHS = ['user.data', 'user.ext.data']; +export const ORTB_UFPD_PATHS = ['user.data', 'user.ext.data', 'user.yob', 'user.gender', 'user.keywords', 'user.kwarray']; export const ORTB_EIDS_PATHS = ['user.eids', 'user.ext.eids']; export const ORTB_GEO_PATHS = ['user.geo.lat', 'user.geo.lon', 'device.geo.lat', 'device.geo.lon']; diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js index 1585b8346baa..f571abcccea7 100644 --- a/test/spec/modules/gdprEnforcement_spec.js +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -11,7 +11,7 @@ import { reportAnalyticsRule, setEnforcementConfig, STRICT_STORAGE_ENFORCEMENT, - syncUserRule, + syncUserRule, transmitUfpdRule, validateRules } from 'modules/gdprEnforcement.js'; import {config} from 'src/config.js'; @@ -462,6 +462,46 @@ describe('gdpr enforcement', function () { }); }); + describe('transmitUfpdRule', () => { + it('should allow when purpose 3 consent is given', () => { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'personalizedAds', + enforcePurpose: true, + enforceVendor: true, + }] + } + }); + Object.assign(gvlids, { + mockBidder: 123 + }); + const consent = setupConsentData(); + consent.vendorData.purpose.consents[4] = true; + consent.vendorData.vendor.consents[123] = true; + expectAllow(true, transmitUfpdRule(activityParams(MODULE_TYPE_BIDDER, 'mockBidder'))); + }); + + it('should return deny when purpose 4 consent is withheld', () => { + setEnforcementConfig({ + gdpr: { + rules: [{ + purpose: 'personalizedAds', + enforcePurpose: true, + enforceVendor: true, + }] + } + }); + Object.assign(gvlids, { + mockBidder: 123 + }); + const consent = setupConsentData(); + consent.vendorData.purpose.consents[4] = true; + consent.vendorData.vendor.consents[123] = false; + expectAllow(false, transmitUfpdRule(activityParams(MODULE_TYPE_BIDDER, 'mockBidder'))) + }); + }); + describe('validateRules', function () { const createGdprRule = (purposeName = 'storage', enforcePurpose = true, enforceVendor = true, vendorExceptions = [], softVendorExceptions = []) => ({ purpose: purposeName, @@ -599,7 +639,8 @@ describe('gdpr enforcement', function () { Object.entries({ 'storage': 1, 'basicAds': 2, - 'measurement': 7 + 'measurement': 7, + 'personalizedAds': 4, }).forEach(([purpose, purposeNo]) => { describe(`for purpose ${purpose}`, () => { const rule = createGdprRule(purpose); From a3c7884ccba80d7f93fe4f64bd85e88b28c91b5f Mon Sep 17 00:00:00 2001 From: Philip Watson Date: Wed, 12 Jul 2023 04:45:25 +1200 Subject: [PATCH 25/92] StroeerCore Bid Adapter: use a random UUID for the request id (#10206) --- modules/stroeerCoreBidAdapter.js | 5 ++--- test/spec/modules/stroeerCoreBidAdapter_spec.js | 13 ++++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/stroeerCoreBidAdapter.js b/modules/stroeerCoreBidAdapter.js index 0737bec5a122..307a50c7f78a 100644 --- a/modules/stroeerCoreBidAdapter.js +++ b/modules/stroeerCoreBidAdapter.js @@ -1,4 +1,4 @@ -import {buildUrl, deepAccess, getWindowSelf, getWindowTop, isEmpty, isStr, logWarn} from '../src/utils.js'; +import { buildUrl, deepAccess, generateUUID, getWindowSelf, getWindowTop, isEmpty, isStr, logWarn } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {find} from '../src/polyfill.js'; @@ -50,8 +50,7 @@ export const spec = { const refererInfo = bidderRequest.refererInfo; const basePayload = { - // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 - id: bidderRequest.auctionId, + id: generateUUID(), ref: refererInfo.ref, ssl: isSecureWindow(), mpa: isMainPageAccessible(), diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js index 55d79804a382..2ed5f80f152f 100644 --- a/test/spec/modules/stroeerCoreBidAdapter_spec.js +++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js @@ -3,6 +3,7 @@ import {spec} from 'modules/stroeerCoreBidAdapter.js'; import * as utils from 'src/utils.js'; import {BANNER, VIDEO} from '../../../src/mediaTypes.js'; import {find} from 'src/polyfill.js'; +import sinon from 'sinon'; describe('stroeerCore bid adapter', function () { let sandbox; @@ -51,8 +52,6 @@ describe('stroeerCore bid adapter', function () { assert.notProperty(bidObject, 'ad'); } - const AUCTION_ID = utils.getUniqueIdentifierStr(); - // Vendor user ids and associated data const userIds = Object.freeze({ criteoId: 'criteo-user-id', @@ -72,7 +71,6 @@ describe('stroeerCore bid adapter', function () { }); const buildBidderRequest = () => ({ - auctionId: AUCTION_ID, bidderRequestId: 'bidder-request-id-123', bidderCode: 'stroeerCore', timeout: 5000, @@ -394,6 +392,10 @@ describe('stroeerCore bid adapter', function () { clock.tick(13500); const bidReq = buildBidderRequest(); + const UUID = 'fb6a39e3-083f-424c-9046-f1095e15f3d5'; + + const generateUUIDStub = sinon.stub(utils, 'generateUUID').returns(UUID); + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); const expectedTimeout = bidderRequest.timeout - (13500 - bidderRequest.auctionStart); @@ -401,7 +403,7 @@ describe('stroeerCore bid adapter', function () { assert.equal(expectedTimeout, 1500); const expectedJsonPayload = { - 'id': AUCTION_ID, + 'id': UUID, 'timeout': expectedTimeout, 'ref': 'https://www.example.com/?search=monkey', 'mpa': true, @@ -429,8 +431,9 @@ describe('stroeerCore bid adapter', function () { // trim away fields with undefined const actualJsonPayload = JSON.parse(JSON.stringify(serverRequestInfo.data)); - assert.deepEqual(actualJsonPayload, expectedJsonPayload); + + generateUUIDStub.restore(); }); describe('video bids', () => { From 83b35f525b9b176735eac32bd3cf21a0db76125c Mon Sep 17 00:00:00 2001 From: ccorbo Date: Tue, 11 Jul 2023 13:26:14 -0400 Subject: [PATCH 26/92] feat: signal imp.displaymanager [PB-551] (#10131) Co-authored-by: Chris Corbo --- modules/ixBidAdapter.js | 39 +++++++++++++- test/spec/modules/ixBidAdapter_spec.js | 72 ++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index bcdffa12e86b..c495d7b69d42 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -180,6 +180,41 @@ function bidToBannerImp(bid) { return imp; } +/** + * Sets imp.displaymanager + * + * @param {object} imp + * @param {object} bid + */ +function setDisplayManager(imp, bid) { + if (deepAccess(bid, 'mediaTypes.video.context') === OUTSTREAM) { + let renderer = deepAccess(bid, 'mediaTypes.video.renderer'); + if (!renderer) { + renderer = deepAccess(bid, 'renderer'); + } + + if (deepAccess(bid, 'schain', false)) { + imp.displaymanager = 'pbjs_wrapper'; + } else if (renderer && typeof (renderer) === 'object') { + if (renderer.url !== undefined) { + let domain = ''; + try { + domain = new URL(renderer.url).hostname + } catch { + return; + } + if (domain.includes('js-sec.indexww')) { + imp.displaymanager = 'ix'; + } else { + imp.displaymanager = renderer.url; + } + } + } else { + imp.displaymanager = 'ix'; + } + } +} + /** * Transform valid bid request config object to video impression object that will be sent to ad server. * @@ -199,8 +234,10 @@ export function bidToVideoImp(bid) { // populate imp level transactionId imp.ext.tid = deepAccess(bid, 'ortb2Imp.ext.tid'); + setDisplayManager(imp, bid); + // AdUnit-Specific First Party Data - addAdUnitFPD(imp, bid) + addAdUnitFPD(imp, bid); // copy all video properties to imp object for (const adUnitProperty in videoAdUnitRef) { diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 6b166b058031..36610db6aa62 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -2357,6 +2357,78 @@ describe('IndexexchangeAdapter', function () { }); }); + describe('video request should set displaymanager', () => { + it('ix renderer preferrered', () => { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + bid[0].mediaTypes.video.context = 'outstream'; + bid[0].mediaTypes.video.w = [[300, 143]]; + bid[0].schain = undefined; + const request = spec.buildRequests(bid); + const videoImpression = extractPayload(request[0]).imp[0]; + expect(videoImpression.displaymanager).to.equal('ix'); + }); + it('ix renderer not preferrered', () => { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + bid[0].mediaTypes.video.context = 'outstream'; + bid[0].mediaTypes.video.w = [[300, 143]]; + bid[0].mediaTypes.video.renderer = { + url: 'http://publisherplayer.js', + render: () => { } + }; + bid[0].schain = undefined; + const request = spec.buildRequests(bid); + const videoImpression = extractPayload(request[0]).imp[0]; + expect(videoImpression.displaymanager).to.equal('http://publisherplayer.js'); + }); + it('ix renderer not preferrered - bad url', () => { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + bid[0].mediaTypes.video.context = 'outstream'; + bid[0].mediaTypes.video.w = [[300, 143]]; + bid[0].mediaTypes.video.renderer = { + url: 'publisherplayer.js', + render: () => { } + }; + bid[0].schain = undefined; + const request = spec.buildRequests(bid); + const videoImpression = extractPayload(request[0]).imp[0]; + expect(videoImpression.displaymanager).to.be.undefined; + }); + it('renderer url provided and is ix renderer', () => { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + bid[0].mediaTypes.video.context = 'outstream'; + bid[0].mediaTypes.video.w = [[300, 143]]; + bid[0].mediaTypes.video.renderer = { + url: 'http://js-sec.indexww.rendererplayer.com', + render: () => { } + }; + bid[0].schain = undefined; + const request = spec.buildRequests(bid); + const videoImpression = extractPayload(request[0]).imp[0]; + expect(videoImpression.displaymanager).to.equal('ix'); + }); + it('renderer url undefined and is ix renderer', () => { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + bid[0].mediaTypes.video.context = 'outstream'; + bid[0].mediaTypes.video.w = [[300, 143]]; + bid[0].mediaTypes.video.renderer = { + render: () => { } + }; + bid[0].schain = undefined; + const request = spec.buildRequests(bid); + const videoImpression = extractPayload(request[0]).imp[0]; + expect(videoImpression.displaymanager).to.be.undefined; + }); + it('schain', () => { + const bid = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + bid[0].mediaTypes.video.context = 'outstream'; + bid[0].mediaTypes.video.w = [[300, 143]]; + bid[0].schain = SAMPLE_SCHAIN; + const request = spec.buildRequests(bid); + const videoImpression = extractPayload(request[0]).imp[0]; + expect(videoImpression.displaymanager).to.equal('pbjs_wrapper'); + }); + }); + describe('request should contain both banner and native requests', function () { let request; before(() => { From b849605adb0ef3d34cc9c4e53b6a060744e84ad1 Mon Sep 17 00:00:00 2001 From: johanbrandmetrics <91625093+johanbrandmetrics@users.noreply.github.com> Date: Tue, 11 Jul 2023 19:28:11 +0200 Subject: [PATCH 27/92] Support billable events (#10148) --- modules/brandmetricsRtdProvider.js | 38 +++++++++- modules/brandmetricsRtdProvider.md | 19 ++++- .../modules/brandmetricsRtdProvider_spec.js | 76 +++++++++++++++++-- 3 files changed, 122 insertions(+), 11 deletions(-) diff --git a/modules/brandmetricsRtdProvider.js b/modules/brandmetricsRtdProvider.js index 53868eccc4ca..30844c9c483c 100644 --- a/modules/brandmetricsRtdProvider.js +++ b/modules/brandmetricsRtdProvider.js @@ -6,8 +6,10 @@ * @requires module:modules/realTimeData */ import {submodule} from '../src/hook.js'; -import {deepAccess, deepSetValue, logError, mergeDeep} from '../src/utils.js'; +import {deepAccess, deepSetValue, logError, mergeDeep, generateUUID} from '../src/utils.js'; import {loadExternalScript} from '../src/adloader.js'; +import * as events from '../src/events.js'; +import CONSTANTS from '../src/constants.json'; const MODULE_NAME = 'brandmetrics' const MODULE_CODE = MODULE_NAME @@ -15,12 +17,15 @@ const RECEIVED_EVENTS = [] const GVL_ID = 422 const TCF_PURPOSES = [1, 7] +let billableEventsInitialized = false + function init (config, userConsent) { const hasConsent = checkConsent(userConsent) if (hasConsent) { const moduleConfig = getMergedConfig(config) initializeBrandmetrics(moduleConfig.params.scriptId) + initializeBillableEvents() } return hasConsent } @@ -82,7 +87,6 @@ function processBrandmetricsEvents (reqBidsConfigObj, moduleConfig, callback) { if (RECEIVED_EVENTS.length > 0) { callBidTargeting(RECEIVED_EVENTS[RECEIVED_EVENTS.length - 1]) } else { - window._brandmetrics = window._brandmetrics || [] window._brandmetrics.push({ cmd: '_addeventlistener', val: { @@ -120,6 +124,8 @@ function setBidderTargeting (reqBidsConfigObj, moduleConfig, key, val) { * @param {string} scriptId - The script- id provided by brandmetrics or brandmetrics partner */ function initializeBrandmetrics(scriptId) { + window._brandmetrics = window._brandmetrics || [] + if (scriptId) { const path = 'https://cdn.brandmetrics.com/survey/script/' const file = scriptId + '.js' @@ -129,6 +135,34 @@ function initializeBrandmetrics(scriptId) { } } +/** +* Hook in to brandmetrics creative_in_view- event and emit billable- event for creatives measured by brandmetrics. +*/ +function initializeBillableEvents() { + if (!billableEventsInitialized) { + window._brandmetrics.push({ + cmd: '_addeventlistener', + val: { + event: 'creative_in_view', + handler: (ev) => { + if (ev.source && ev.source.type === 'pbj') { + const bid = ev.source.data; + events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, { + vendor: 'brandmetrics', + type: 'creative_in_view', + measurementId: ev.mid, + billingId: generateUUID(), + auctionId: bid.auctionId, + transactionId: bid.transactionId, + }); + } + }, + } + }) + billableEventsInitialized = true + } +} + /** * Merges a provided config with default values * @param {Object} customConfig diff --git a/modules/brandmetricsRtdProvider.md b/modules/brandmetricsRtdProvider.md index 89ee6bb75cff..d6304f9ae12f 100644 --- a/modules/brandmetricsRtdProvider.md +++ b/modules/brandmetricsRtdProvider.md @@ -16,10 +16,10 @@ Enable the Brandmetrics RTD in your Prebid configuration, using the below format pbjs.setConfig({ ..., realTimeData: { - auctionDelay: 500, // auction delay + auctionDelay: 500, dataProviders: [{ name: 'brandmetrics', - waitForIt: true // should be true if there's an `auctionDelay`, + waitForIt: true, params: { scriptId: '00000000-0000-0000-0000-000000000000', bidders: ['ozone'] @@ -29,6 +29,7 @@ pbjs.setConfig({ ... }) ``` +The scriptId- parameter is provided by brandmetrics or a brandmetrics partner. ## Parameters | Name | Type | Description | Default | @@ -38,3 +39,17 @@ pbjs.setConfig({ | params | Object | | - | | params.bidders | String[] | An array of bidders which should receive targeting keys. | `[]` | | params.scriptId | String | A script- id GUID if the brandmetrics- script should be initialized. | `undefined` | + +## Billable events +The module emits a billable event for creatives that are measured by brandmetrics and are considered in- view. + +```javascript +{ + vendor: 'brandmetrics', + type: 'creative_in_view', + measurementId: string, // UUID, brandmetrics measurement id + billingId: string, // UUID, unique billing id + auctionId: string, // Prebid auction id + transactionId: string, //Prebid transaction id +} +``` \ No newline at end of file diff --git a/test/spec/modules/brandmetricsRtdProvider_spec.js b/test/spec/modules/brandmetricsRtdProvider_spec.js index 879ec7e1c7a1..907c672208f5 100644 --- a/test/spec/modules/brandmetricsRtdProvider_spec.js +++ b/test/spec/modules/brandmetricsRtdProvider_spec.js @@ -1,5 +1,7 @@ import * as brandmetricsRTD from '../../../modules/brandmetricsRtdProvider.js'; import {config} from 'src/config.js'; +import * as events from '../../../src/events'; +import * as sinon from 'sinon'; const VALID_CONFIG = { name: 'brandmetrics', @@ -77,14 +79,16 @@ function mockSurveyLoaded(surveyConf) { }); } -function scriptTagExists(url) { - const tags = document.getElementsByTagName('script'); - for (let i = 0; i < tags.length; i++) { - if (tags[i].src === url) { - return true; +function mockCreativeInView(creativeInViewConf) { + const commands = window._brandmetrics || []; + commands.forEach(command => { + if (command.cmd === '_addeventlistener') { + const conf = command.val; + if (conf.event === 'creative_in_view') { + conf.handler(creativeInViewConf); + } } - } - return false; + }) } describe('BrandmetricsRTD module', () => { @@ -188,4 +192,62 @@ describe('getBidRequestData', () => { expect(bidderOrtb2[exp].user.ext.data.brandmetrics_survey).to.equal('mockMeasurementId') }) }); + + describe('billable events', () => { + let sandbox; + let eventsEmitSpy; + + before(() => { + sandbox = sinon.sandbox.create(); + eventsEmitSpy = sandbox.spy(events, ['emit']); + }); + + beforeEach(() => { + eventsEmitSpy.resetHistory(); + }) + + afterEach(() => { + sandbox.restore(); + }); + + it('should emit billable event from prebid events', () => { + const expectedEvent = { + vendor: 'brandmetrics', + type: 'creative_in_view', + measurementId: 'mockMeasurementId', + auctionId: 'mockAuctionId', + transactionId: 'mockTransactionId' + }; + + mockCreativeInView({ + mid: expectedEvent.measurementId, + source: { + type: 'pbj', + data: { + auctionId: expectedEvent.auctionId, + transactionId: expectedEvent.transactionId + }, + } + }); + + expect(eventsEmitSpy.callCount).to.equal(1); + + const event = eventsEmitSpy.getCalls()[0].args[1]; + delete event['billingId']; + + expect(event).to.deep.equal(expectedEvent); + }); + + it('should not emit billable event from non prebid- sources', () => { + mockCreativeInView({ + mid: 'mockMeasurementId', + source: { + type: 'gpt', + data: {}, + } + }); + + expect(eventsEmitSpy.callCount).to.equal(0); + }); + }); }); From f77b6fd91dc5472d9a27d03df740e24506407c36 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Tue, 11 Jul 2023 13:41:05 -0400 Subject: [PATCH 28/92] Delete inskin_example.html (#10218) --- integrationExamples/gpt/inskin_example.html | 100 -------------------- 1 file changed, 100 deletions(-) delete mode 100644 integrationExamples/gpt/inskin_example.html diff --git a/integrationExamples/gpt/inskin_example.html b/integrationExamples/gpt/inskin_example.html deleted file mode 100644 index 45fd448cf487..000000000000 --- a/integrationExamples/gpt/inskin_example.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
-
- -
- - From ab56b2511e2b9ee69043e66080c61420ca74777c Mon Sep 17 00:00:00 2001 From: SmartyAdman <59048845+SmartyAdman@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:58:42 +0300 Subject: [PATCH 29/92] Adman Bid Adapter : additional content object handle (#10204) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Adman bid adapter * Add supportedMediaTypes property * Update ADman Media bidder adapter * Remove console.log * Fix typo * revert package-json.lock * Delete package-lock.json * back to original package-lock.json * catch pbjs error * catch pbjs error * catch pbjs error * log * remove eu url * remove eu url * remove eu url * remove eu url * remove eu url * Update admanBidAdapter.js add consnet to sync url * Update admanBidAdapter.js fix import * Update admanBidAdapter.js lint fix * Update admanBidAdapter.js lint fix * Update admanBidAdapter.js check consent object data availability * сompatible with prebid v5 * add Lotame Panorama ID * update getUserSyncs * fix * fix tests * remove package-lock.json * update sync url * update test * add idx (UserID Module) * update tests * remove traffic param * handle transactionID param * send transactionID param in imp.ext * rename transactionID to transactionId * update tests * additional content handle * rollback content * content handle via hb integration --------- Co-authored-by: minoru katogi Co-authored-by: minoru katogi Co-authored-by: ADman Media Co-authored-by: SmartyAdman Co-authored-by: SmartyAdman <> --- modules/admanBidAdapter.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js index b49bf6390f5d..2ee6ecfcb56b 100644 --- a/modules/admanBidAdapter.js +++ b/modules/admanBidAdapter.js @@ -66,6 +66,7 @@ export const spec = { buildRequests: (validBidRequests = [], bidderRequest) => { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + const content = deepAccess(bidderRequest, 'ortb2.site.content', config.getAnyConfig('ortb2.site.content')); let winTop = window; let location; @@ -95,6 +96,9 @@ export const spec = { if (bidderRequest.gdprConsent) { request.gdpr = bidderRequest.gdprConsent } + if (content) { + request.content = content; + } } const len = validBidRequests.length; From 8615eeb5d5226479fcdfa7810d6a7a96306a0ae0 Mon Sep 17 00:00:00 2001 From: Olivier Date: Wed, 12 Jul 2023 13:28:55 +0200 Subject: [PATCH 30/92] AdagioBidAdapter: bug: fix endpoint (#10220) --- modules/adagioBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/adagioBidAdapter.js b/modules/adagioBidAdapter.js index c28692de846b..a3369ec33573 100644 --- a/modules/adagioBidAdapter.js +++ b/modules/adagioBidAdapter.js @@ -38,7 +38,7 @@ import { userSync } from '../src/userSync.js'; const BIDDER_CODE = 'adagio'; const LOG_PREFIX = 'Adagio:'; const FEATURES_VERSION = '1'; -export const ENDPOINT = 'http://127.0.0.1:8080/prebid'; +export const ENDPOINT = 'https://mp.4dex.io/prebid'; const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO]; const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js'; const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript'; From b973b2d134e486c74667dcfd38e432c17440c39f Mon Sep 17 00:00:00 2001 From: Sajid Mahmood Date: Wed, 12 Jul 2023 07:29:45 -0400 Subject: [PATCH 31/92] IX Bid Adapter: add LiveIntent to EID allowlist (#10217) * IX Bid Adapter: Add LiveIntent to EID allowlist Co-authored-by: Sajid Mahmood * fix --------- Co-authored-by: Sajid Mahmood --- modules/ixBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index c495d7b69d42..5e39c43367f1 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -77,7 +77,8 @@ const SOURCE_RTI_MAPPING = { 'pubcid.org': '', // SharedID, pubcid 'utiq.com': '', // Utiq 'intimatemerger.com': '', - '33across.com': '' + '33across.com': '', + 'liveintent.indexexchange.com': '', }; const PROVIDERS = [ 'britepoolid', From 361cf62acd7196bbf3a2b0bf524a6ee88e2a7206 Mon Sep 17 00:00:00 2001 From: Julian Gan Date: Wed, 12 Jul 2023 07:32:09 -0400 Subject: [PATCH 32/92] Kargo Bid Adapter: Forward `schain` in the request (#10202) * KargoBidAdapter: GPP Support * kargo adapter to forward schain object (#21) * wrap in if statement (#22) * KRKPD-572: Add spec for schain (#23) * wrap in if statement * update test for schain, file formatting --------- Co-authored-by: Jeremy Sadwith --- modules/kargoBidAdapter.js | 4 + test/spec/modules/kargoBidAdapter_spec.js | 227 ++++++++++++---------- 2 files changed, 125 insertions(+), 106 deletions(-) diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 5b315ef57919..1dde44532225 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -97,6 +97,10 @@ function buildRequests(validBidRequests, bidderRequest) { user: getUserIds(tdidAdapter, bidderRequest.uspConsent, bidderRequest.gdprConsent, firstBidRequest.userIdAsEids, bidderRequest.gppConsent), }); + if (firstBidRequest.schain && firstBidRequest.schain.nodes) { + krakenParams.schain = firstBidRequest.schain + } + const reqCount = getRequestCount() if (reqCount != null) { krakenParams.requestCount = reqCount; diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index dae2640d2242..9f7a4854063c 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -1,10 +1,21 @@ -import {expect, assert} from 'chai'; -import {spec} from 'modules/kargoBidAdapter.js'; -import {config} from 'src/config.js'; +import { expect, assert } from 'chai'; +import { spec } from 'modules/kargoBidAdapter.js'; +import { config } from 'src/config.js'; const utils = require('src/utils'); describe('kargo adapter tests', function () { var sandbox, clock, frozenNow = new Date(); + const testSchain = { + complete: 1, + nodes: [ + { + 'asi': 'test-page.com', + 'hp': 1, + 'rid': '57bdd953-6e57-4d5b-9351-ed67ca238890', + 'sid': '8190248274' + } + ] + } beforeEach(function () { sandbox = sinon.sandbox.create(); @@ -16,25 +27,25 @@ describe('kargo adapter tests', function () { clock.restore(); }); - describe('bid request validity', function() { - it('passes when the bid includes a placement ID', function() { - assert(spec.isBidRequestValid({params: {placementId: 'foo'}}) === true); + describe('bid request validity', function () { + it('passes when the bid includes a placement ID', function () { + assert(spec.isBidRequestValid({ params: { placementId: 'foo' } }) === true); }); - it('fails when the bid does not include a placement ID', function() { - assert(spec.isBidRequestValid({params: {}}) === false); + it('fails when the bid does not include a placement ID', function () { + assert(spec.isBidRequestValid({ params: {} }) === false); }); - it('fails when bid is falsey', function() { + it('fails when bid is falsey', function () { assert(spec.isBidRequestValid() === false); }); - it('fails when the bid has no params at all', function() { + it('fails when the bid has no params at all', function () { assert(spec.isBidRequestValid({}) === false); }); }); - describe('build request', function() { + describe('build request', function () { var bids, undefinedCurrency, noAdServerCurrency, nonUSDAdServerCurrency, cookies = [], localStorageItems = [], sessionIds = [], requestCount = 0; beforeEach(function () { @@ -46,7 +57,7 @@ describe('kargo adapter tests', function () { undefinedCurrency = false; noAdServerCurrency = false; nonUSDAdServerCurrency = false; - sandbox.stub(config, 'getConfig').callsFake(function(key) { + sandbox.stub(config, 'getConfig').callsFake(function (key) { if (key === 'currency') { if (undefinedCurrency) { return undefined; @@ -55,9 +66,9 @@ describe('kargo adapter tests', function () { return {}; } if (nonUSDAdServerCurrency) { - return {adServerCurrency: 'EUR'}; + return { adServerCurrency: 'EUR' }; } - return {adServerCurrency: 'USD'}; + return { adServerCurrency: 'USD' }; } if (key === 'debug') return true; if (key === 'deviceAccess') return true; @@ -85,6 +96,7 @@ describe('kargo adapter tests', function () { bidRequestsCount: 1, bidderRequestsCount: 2, bidderWinsCount: 3, + schain: testSchain, userId: { tdid: 'ed1562d5-e52b-406f-8e65-e5ab3ed5583c' }, @@ -110,20 +122,20 @@ describe('kargo adapter tests', function () { sua: { platform: { brand: 'macOS', - version: [ '12', '6', '0' ] + version: ['12', '6', '0'] }, browsers: [ { brand: 'Chromium', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Google Chrome', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Not;A=Brand', - version: [ '99', '0', '0', '0' ] + version: ['99', '0', '0', '0'] } ], mobile: 1, @@ -397,6 +409,7 @@ describe('kargo adapter tests', function () { url: 'https://www.prebid.org', timeout: 200, ts: frozenNow.getTime(), + schain: testSchain, device: { size: [ screen.width, @@ -405,20 +418,20 @@ describe('kargo adapter tests', function () { sua: { platform: { brand: 'macOS', - version: [ '12', '6', '0' ] + version: ['12', '6', '0'] }, browsers: [ { brand: 'Chromium', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Google Chrome', - version: [ '106', '0', '5249', '119' ] + version: ['106', '0', '5249', '119'] }, { brand: 'Not;A=Brand', - version: [ '99', '0', '0', '0' ] + version: ['99', '0', '0', '0'] } ], mobile: 1, @@ -571,84 +584,84 @@ describe('kargo adapter tests', function () { } } - it('works when all params and localstorage and cookies are correctly set', function() { + it('works when all params and localstorage and cookies are correctly set', function () { initializeKrgCrb(); initializePageView(); testBuildRequests(getExpectedKrakenParams(generateRawCRB(getKrgCrbOldStyle(), getKrgCrb()), generatePageView())); }); - it('works when all params and cookies are correctly set but no localstorage', function() { + it('works when all params and cookies are correctly set but no localstorage', function () { initializeKrgCrb(true); testBuildRequests(getExpectedKrakenParams(generateRawCRB(getKrgCrbOldStyle()))); }); - it('gracefully handles nothing being set', function() { + it('gracefully handles nothing being set', function () { testBuildRequests(getExpectedKrakenParams(undefined, undefined, true)); }); - it('gracefully handles browsers without localStorage', function() { + it('gracefully handles browsers without localStorage', function () { simulateNoLocalStorage(); testBuildRequests(getExpectedKrakenParams(undefined, undefined, true)); }); - it('handles empty yet valid Kargo CRB', function() { + it('handles empty yet valid Kargo CRB', function () { initializeEmptyKrgCrb(); initializeEmptyKrgCrbCookie(); initializePageView(); testBuildRequests(getExpectedKrakenParams(generateRawCRB(getEmptyKrgCrbOldStyle(), getEmptyKrgCrb()), generatePageView(), true)); }); - it('handles broken Kargo CRBs where base64 encoding is invalid', function() { + it('handles broken Kargo CRBs where base64 encoding is invalid', function () { initializeInvalidKrgCrbType1(); initializePageView(); testBuildRequests(getExpectedKrakenParams(generateRawCRB(undefined, getInvalidKrgCrbType1()), generatePageView(), true)); }); - it('handles broken Kargo CRBs where top level JSON is invalid on cookie', function() { + it('handles broken Kargo CRBs where top level JSON is invalid on cookie', function () { initializeInvalidKrgCrbType1Cookie(); initializePageView(); testBuildRequests(getExpectedKrakenParams(generateRawCRB(getInvalidKrgCrbType1()), generatePageView(), true)); }); - it('handles broken Kargo CRBs where decoded JSON is invalid', function() { + it('handles broken Kargo CRBs where decoded JSON is invalid', function () { initializeInvalidKrgCrbType2(); initializePageView(); testBuildRequests(getExpectedKrakenParams(generateRawCRB(undefined, getInvalidKrgCrbType2()), generatePageView(), true)); }); - it('handles broken Kargo CRBs where inner base 64 is invalid on cookie', function() { + it('handles broken Kargo CRBs where inner base 64 is invalid on cookie', function () { initializeInvalidKrgCrbType2Cookie(); initializePageView(); testBuildRequests(getExpectedKrakenParams(generateRawCRB(getInvalidKrgCrbType2OldStyle()), generatePageView(), true)); }); - it('handles broken Kargo CRBs where inner JSON is invalid on cookie', function() { + it('handles broken Kargo CRBs where inner JSON is invalid on cookie', function () { initializeInvalidKrgCrbType3Cookie(); initializePageView(); testBuildRequests(getExpectedKrakenParams(generateRawCRB(getInvalidKrgCrbType3OldStyle()), generatePageView(), true)); }); - it('handles broken Kargo CRBs where inner JSON is falsey', function() { + it('handles broken Kargo CRBs where inner JSON is falsey', function () { initializeInvalidKrgCrbType4Cookie(); initializePageView(); testBuildRequests(getExpectedKrakenParams(generateRawCRB(getInvalidKrgCrbType4OldStyle()), generatePageView(), true)); }); - it('handles a non-existant currency object on the config', function() { + it('handles a non-existant currency object on the config', function () { simulateNoCurrencyObject(); initializeKrgCrb(); initializePageView(); testBuildRequests(getExpectedKrakenParams(generateRawCRB(getKrgCrbOldStyle(), getKrgCrb()), generatePageView())); }); - it('handles no ad server currency being set on the currency object in the config', function() { + it('handles no ad server currency being set on the currency object in the config', function () { simulateNoAdServerCurrency(); initializeKrgCrb(); initializePageView(); testBuildRequests(getExpectedKrakenParams(generateRawCRB(getKrgCrbOldStyle(), getKrgCrb()), generatePageView())); }); - it('handles non-USD ad server currency being set on the currency object in the config', function() { + it('handles non-USD ad server currency being set on the currency object in the config', function () { simulateNonUSDAdServerCurrency(); initializeKrgCrb(); initializePageView(); @@ -663,67 +676,69 @@ describe('kargo adapter tests', function () { }); }); - describe('response handler', function() { - it('handles bid responses', function() { - var resp = spec.interpretResponse({body: { - 1: { - id: 'foo', - cpm: 3, - adm: '
', - width: 320, - height: 50, - metadata: {} - }, - 2: { - id: 'bar', - cpm: 2.5, - adm: '
', - width: 300, - height: 250, - targetingCustom: 'dmpmptest1234', - metadata: { - landingPageDomain: ['https://foobar.com'] + describe('response handler', function () { + it('handles bid responses', function () { + var resp = spec.interpretResponse({ + body: { + 1: { + id: 'foo', + cpm: 3, + adm: '
', + width: 320, + height: 50, + metadata: {} + }, + 2: { + id: 'bar', + cpm: 2.5, + adm: '
', + width: 300, + height: 250, + targetingCustom: 'dmpmptest1234', + metadata: { + landingPageDomain: ['https://foobar.com'] + } + }, + 3: { + id: 'bar', + cpm: 2.5, + adm: '
', + width: 300, + height: 250 + }, + 4: { + id: 'bar', + cpm: 2.5, + adm: '
', + width: 300, + height: 250, + mediaType: 'banner', + metadata: {}, + currency: 'EUR' + }, + 5: { + id: 'bar', + cpm: 2.5, + adm: '', + width: 300, + height: 250, + mediaType: 'video', + metadata: {}, + currency: 'EUR' + }, + 6: { + id: 'bar', + cpm: 2.5, + adm: '', + admUrl: 'https://foobar.com/vast_adm', + width: 300, + height: 250, + mediaType: 'video', + metadata: {}, + currency: 'EUR' } - }, - 3: { - id: 'bar', - cpm: 2.5, - adm: '
', - width: 300, - height: 250 - }, - 4: { - id: 'bar', - cpm: 2.5, - adm: '
', - width: 300, - height: 250, - mediaType: 'banner', - metadata: {}, - currency: 'EUR' - }, - 5: { - id: 'bar', - cpm: 2.5, - adm: '', - width: 300, - height: 250, - mediaType: 'video', - metadata: {}, - currency: 'EUR' - }, - 6: { - id: 'bar', - cpm: 2.5, - adm: '', - admUrl: 'https://foobar.com/vast_adm', - width: 300, - height: 250, - mediaType: 'video', - metadata: {}, - currency: 'EUR' } - }}, { + }, { currency: 'USD', bids: [{ bidId: 1, @@ -854,7 +869,7 @@ describe('kargo adapter tests', function () { }); }); - describe('user sync handler', function() { + describe('user sync handler', function () { const clientId = '74c81cbb-7d07-46d9-be9b-68ccb291c949'; var shouldSimulateOutdatedBrowser, crb, isActuallyOutdatedBrowser; @@ -875,7 +890,7 @@ describe('kargo adapter tests', function () { if (!window.crypto) { isActuallyOutdatedBrowser = true; } else { - sandbox.stub(crypto, 'getRandomValues').callsFake(function(buf) { + sandbox.stub(crypto, 'getRandomValues').callsFake(function (buf) { if (shouldSimulateOutdatedBrowser) { throw new Error('Could not generate random values'); } @@ -887,13 +902,13 @@ describe('kargo adapter tests', function () { }); } - sandbox.stub(spec, '_getCrb').callsFake(function() { + sandbox.stub(spec, '_getCrb').callsFake(function () { return crb; }); }); function getUserSyncsWhenAllowed(gdprConsent, usPrivacy, gppConsent) { - return spec.getUserSyncs({iframeEnabled: true}, null, gdprConsent, usPrivacy, gppConsent); + return spec.getUserSyncs({ iframeEnabled: true }, null, gdprConsent, usPrivacy, gppConsent); } function getUserSyncsWhenForbidden() { @@ -931,26 +946,26 @@ describe('kargo adapter tests', function () { } } - it('handles user syncs when there is a client id', function() { + it('handles user syncs when there is a client id', function () { turnOnClientId(); safelyRun(() => expect(getUserSyncsWhenAllowed()).to.deep.equal(getSyncUrls())); }); - it('no user syncs when there is no client id', function() { + it('no user syncs when there is no client id', function () { safelyRun(() => expect(getUserSyncsWhenAllowed()).to.be.an('array').that.is.empty); }); - it('no user syncs when there is no us privacy consent', function() { + it('no user syncs when there is no us privacy consent', function () { turnOnClientId(); safelyRun(() => expect(getUserSyncsWhenAllowed(null, '1YYY')).to.be.an('array').that.is.empty); }); - it('pass through us privacy consent', function() { + it('pass through us privacy consent', function () { turnOnClientId(); safelyRun(() => expect(getUserSyncsWhenAllowed(null, '1YNY')).to.deep.equal(getSyncUrls(0, '', '1YNY'))); }); - it('pass through gdpr consent', function() { + it('pass through gdpr consent', function () { turnOnClientId(); safelyRun(() => expect(getUserSyncsWhenAllowed({ gdprApplies: true, consentString: 'consentstring' })).to.deep.equal(getSyncUrls(1, 'consentstring', ''))); }); @@ -960,13 +975,13 @@ describe('kargo adapter tests', function () { safelyRun(() => expect(getUserSyncsWhenAllowed(null, null, { consentString: 'gppString', applicableSections: [-1] })).to.deep.equal(getSyncUrls('', '', '', 'gppString', '-1'))); }); - it('no user syncs when there is outdated browser', function() { + it('no user syncs when there is outdated browser', function () { turnOnClientId(); simulateOutdatedBrowser(); safelyRun(() => expect(getUserSyncsWhenAllowed()).to.be.an('array').that.is.empty); }); - it('no user syncs when no iframe syncing allowed', function() { + it('no user syncs when no iframe syncing allowed', function () { turnOnClientId(); safelyRun(() => expect(getUserSyncsWhenForbidden()).to.be.an('array').that.is.empty); }); From f606397af87ff65ed119b181173c5bda0b42107a Mon Sep 17 00:00:00 2001 From: Saar Amrani Date: Wed, 12 Jul 2023 14:36:46 +0300 Subject: [PATCH 33/92] Vidazoo Bid Adapter: change max chunkSize to 20 & remove auctionId from payload (#10212) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server * multi poc * feat: added support for single request. * feat: change to bids. * change to if else * Fixed test * feat: change max chunkSize to 20. * remove auctionId from payload. --------- Co-authored-by: Udi Talias Co-authored-by: roman --- modules/vidazooBidAdapter.js | 12 +++++------- test/spec/modules/vidazooBidAdapter_spec.js | 5 ----- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 42c7031fb2a9..71b9eae29a52 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -56,7 +56,6 @@ function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout adUnitCode, schain, mediaTypes, - auctionId, ortb2Imp, bidderRequestId, bidRequestsCount, @@ -112,8 +111,6 @@ function buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout gpid: gpid, cat: cat, pagecat: pagecat, - // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 - auctionId: auctionId, transactionId: ortb2Imp?.ext?.tid, bidderRequestId: bidderRequestId, bidRequestsCount: bidRequestsCount, @@ -179,9 +176,9 @@ function buildSingleRequest(bidRequests, bidderRequest, topWindowUrl, bidderTime const sizes = parseSizesInput(bid.sizes); return buildRequestData(bid, topWindowUrl, sizes, bidderRequest, bidderTimeout) }); - const CHUNK_SIZE = Math.min(10, config.getConfig('vidazoo.chunkSize') || 10); + const chunkSize = Math.min(20, config.getConfig('vidazoo.chunkSize') || 10); - const chunkedData = chunk(data, CHUNK_SIZE); + const chunkedData = chunk(data, chunkSize); return chunkedData.map(chunk => { return { method: 'POST', @@ -226,14 +223,15 @@ function buildRequests(validBidRequests, bidderRequest) { const requests = []; if (singleRequestMode) { - // We need to split the requests into banner and video requests + // banner bids are sent as a single request const bannerBidRequests = validBidRequests.filter(bid => isArray(bid.mediaTypes) ? bid.mediaTypes.includes(BANNER) : bid.mediaTypes[BANNER] !== undefined); if (bannerBidRequests.length > 0) { const singleRequests = buildSingleRequest(bannerBidRequests, bidderRequest, topWindowUrl, bidderTimeout); requests.push(...singleRequests); } - // Video Logic + // video bids are sent as a single request for each bid + const videoBidRequests = validBidRequests.filter(bid => bid.mediaTypes[VIDEO] !== undefined); videoBidRequests.forEach(validBidRequest => { const sizes = parseSizesInput(validBidRequest.sizes); diff --git a/test/spec/modules/vidazooBidAdapter_spec.js b/test/spec/modules/vidazooBidAdapter_spec.js index 157bbd0fd946..864f2b8551ca 100644 --- a/test/spec/modules/vidazooBidAdapter_spec.js +++ b/test/spec/modules/vidazooBidAdapter_spec.js @@ -40,7 +40,6 @@ const BID = { 'placementCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250], [300, 600]], 'bidderRequestId': '1fdb5ff1b6eaa7', - 'auctionId': 'auction_id', 'bidRequestsCount': 4, 'bidderRequestsCount': 3, 'bidderWinsCount': 1, @@ -64,7 +63,6 @@ const VIDEO_BID = { tid: '56e184c6-bde9-497b-b9b9-cf47a61381ee', } }, - 'auctionId': 'auction_id', 'bidRequestsCount': 4, 'bidderRequestsCount': 3, 'bidderWinsCount': 1, @@ -288,7 +286,6 @@ describe('VidazooBidAdapter', function () { usPrivacy: 'consent_string', gppString: 'gpp_string', gppSid: [7], - auctionId: 'auction_id', bidRequestsCount: 4, bidderRequestsCount: 3, bidderWinsCount: 1, @@ -362,7 +359,6 @@ describe('VidazooBidAdapter', function () { usPrivacy: 'consent_string', gppString: 'gpp_string', gppSid: [7], - auctionId: 'auction_id', bidRequestsCount: 4, bidderRequestsCount: 3, bidderWinsCount: 1, @@ -436,7 +432,6 @@ describe('VidazooBidAdapter', function () { usPrivacy: 'consent_string', gppString: 'gpp_string', gppSid: [7], - auctionId: 'auction_id', bidRequestsCount: 4, bidderRequestsCount: 3, bidderWinsCount: 1, From 89a1e1eafa60e11edaa04198e5bc639e67d14186 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Wed, 12 Jul 2023 05:03:26 -0700 Subject: [PATCH 34/92] Fix sua low entropy check for empty objects (#10167) --- src/fpd/sua.js | 10 ++++++++-- test/spec/fpd/enrichment_spec.js | 8 +++++++- test/spec/fpd/sua_spec.js | 12 ++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/fpd/sua.js b/src/fpd/sua.js index 30b2be4c13ba..565c3e1fd52e 100644 --- a/src/fpd/sua.js +++ b/src/fpd/sua.js @@ -15,6 +15,12 @@ export const HIGH_ENTROPY_HINTS = [ 'fullVersionList' ] +export const LOW_ENTROPY_HINTS = [ + 'brands', + 'mobile', + 'platform' +] + /** * Returns low entropy UA client hints encoded as an ortb2.6 device.sua object; or null if no UA client hints are available. */ @@ -32,7 +38,7 @@ export const getLowEntropySUA = lowEntropySUAAccessor(); export const getHighEntropySUA = highEntropySUAAccessor(); export function lowEntropySUAAccessor(uaData = window.navigator?.userAgentData) { - const sua = isEmpty(uaData) ? null : Object.freeze(uaDataToSUA(SUA_SOURCE_LOW_ENTROPY, uaData)); + const sua = (uaData && LOW_ENTROPY_HINTS.some(h => typeof uaData[h] !== 'undefined')) ? Object.freeze(uaDataToSUA(SUA_SOURCE_LOW_ENTROPY, uaData)) : null; return function () { return sua; } @@ -85,7 +91,7 @@ export function uaDataToSUA(source, uaData) { if (uaData.fullVersionList || uaData.brands) { sua.browsers = (uaData.fullVersionList || uaData.brands).map(({brand, version}) => toBrandVersion(brand, version)); } - if (uaData.hasOwnProperty('mobile')) { + if (typeof uaData['mobile'] !== 'undefined') { sua.mobile = uaData.mobile ? 1 : 0; } ['model', 'bitness', 'architecture'].forEach(prop => { diff --git a/test/spec/fpd/enrichment_spec.js b/test/spec/fpd/enrichment_spec.js index 328846ca081a..3b3afb15f8c5 100644 --- a/test/spec/fpd/enrichment_spec.js +++ b/test/spec/fpd/enrichment_spec.js @@ -275,7 +275,13 @@ describe('FPD enrichment', () => { describe('sua', () => { it('does not set device.sua if resolved sua is null', () => { - sandbox.stub(dep, 'getHighEntropySUA').returns(Promise.resolve()) + sandbox.stub(dep, 'getHighEntropySUA').returns(Promise.resolve()); + // Add hints so it will attempt to retrieve high entropy values + config.setConfig({ + firstPartyData: { + uaHints: ['bitness'], + } + }); return fpd().then(ortb2 => { expect(ortb2.device.sua).to.not.exist; }) diff --git a/test/spec/fpd/sua_spec.js b/test/spec/fpd/sua_spec.js index 431f47268d3b..63e0068d0efc 100644 --- a/test/spec/fpd/sua_spec.js +++ b/test/spec/fpd/sua_spec.js @@ -165,6 +165,14 @@ describe('uaDataToSUA', () => { }); describe('lowEntropySUAAccessor', () => { + // Set up a mock data with readonly property + class MockUserAgentData {} + Object.defineProperty(MockUserAgentData.prototype, 'mobile', { + value: false, + writable: false, + enumerable: true + }); + function getSUA(uaData) { return lowEntropySUAAccessor(uaData)(); } @@ -181,6 +189,10 @@ describe('lowEntropySUAAccessor', () => { it('should return null if uaData is empty', () => { expect(getSUA({})).to.eql(null); }) + + it('should return mobile and source', () => { + expect(getSUA(new MockUserAgentData())).to.eql({mobile: 0, source: 1}) + }) }); describe('highEntropySUAAccessor', () => { From 874d080a507b7a8be252763556453513c63a77ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Fermeaux?= <32789499+fermeaux@users.noreply.github.com> Date: Wed, 12 Jul 2023 14:05:02 +0200 Subject: [PATCH 35/92] Ogury Bid Adapter: init new user sync iframe (#10158) --- modules/oguryBidAdapter.js | 45 +-- test/spec/modules/oguryBidAdapter_spec.js | 316 ++++++++++++++-------- 2 files changed, 237 insertions(+), 124 deletions(-) diff --git a/modules/oguryBidAdapter.js b/modules/oguryBidAdapter.js index 718ae44c83c8..4fd9b711b425 100644 --- a/modules/oguryBidAdapter.js +++ b/modules/oguryBidAdapter.js @@ -11,7 +11,7 @@ const DEFAULT_TIMEOUT = 1000; const BID_HOST = 'https://mweb-hb.presage.io/api/header-bidding-request'; const TIMEOUT_MONITORING_HOST = 'https://ms-ads-monitoring-events.presage.io'; const MS_COOKIE_SYNC_DOMAIN = 'https://ms-cookie-sync.presage.io'; -const ADAPTER_VERSION = '1.4.1'; +const ADAPTER_VERSION = '1.5.0'; function getClientWidth() { const documentElementClientWidth = window.top.document.documentElement.clientWidth @@ -46,22 +46,35 @@ function isBidRequestValid(bid) { } function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { - if (!syncOptions.pixelEnabled) return []; + const consent = (gdprConsent && gdprConsent.consentString) || ''; - return [ - { - type: 'image', - url: `${MS_COOKIE_SYNC_DOMAIN}/v1/init-sync/bid-switch?iab_string=${(gdprConsent && gdprConsent.consentString) || ''}&source=prebid` - }, - { - type: 'image', - url: `${MS_COOKIE_SYNC_DOMAIN}/ttd/init-sync?iab_string=${(gdprConsent && gdprConsent.consentString) || ''}&source=prebid` - }, - { - type: 'image', - url: `${MS_COOKIE_SYNC_DOMAIN}/xandr/init-sync?iab_string=${(gdprConsent && gdprConsent.consentString) || ''}&source=prebid` - } - ] + if (syncOptions.iframeEnabled) { + return [ + { + type: 'iframe', + url: `${MS_COOKIE_SYNC_DOMAIN}/user-sync.html?gdpr_consent=${consent}&source=prebid` + } + ]; + } + + if (syncOptions.pixelEnabled) { + return [ + { + type: 'image', + url: `${MS_COOKIE_SYNC_DOMAIN}/v1/init-sync/bid-switch?iab_string=${consent}&source=prebid` + }, + { + type: 'image', + url: `${MS_COOKIE_SYNC_DOMAIN}/ttd/init-sync?iab_string=${consent}&source=prebid` + }, + { + type: 'image', + url: `${MS_COOKIE_SYNC_DOMAIN}/xandr/init-sync?iab_string=${consent}&source=prebid` + } + ]; + } + + return []; } function buildRequests(validBidRequests, bidderRequest) { diff --git a/test/spec/modules/oguryBidAdapter_spec.js b/test/spec/modules/oguryBidAdapter_spec.js index d25363de9c94..bbe538550944 100644 --- a/test/spec/modules/oguryBidAdapter_spec.js +++ b/test/spec/modules/oguryBidAdapter_spec.js @@ -113,132 +113,232 @@ describe('OguryBidAdapter', function () { let syncOptions, gdprConsent; beforeEach(() => { - syncOptions = {pixelEnabled: true}; gdprConsent = { gdprApplies: true, consentString: 'CPJl4C8PJl4C8OoAAAENAwCMAP_AAH_AAAAAAPgAAAAIAPgAAAAIAAA.IGLtV_T9fb2vj-_Z99_tkeYwf95y3p-wzhheMs-8NyZeH_B4Wv2MyvBX4JiQKGRgksjLBAQdtHGlcTQgBwIlViTLMYk2MjzNKJrJEilsbO2dYGD9Pn8HT3ZCY70-vv__7v3ff_3g' }; }); - it('should return syncs array with three elements of type image', () => { - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + describe('pixel', () => { + beforeEach(() => { + syncOptions = { pixelEnabled: true }; + }); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.contain('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch'); - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.contain('https://ms-cookie-sync.presage.io/ttd/init-sync'); - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.contain('https://ms-cookie-sync.presage.io/xandr/init-sync'); - }); + it('should return syncs array with three elements of type image', () => { + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - it('should set the source as query param', () => { - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs[0].url).to.contain('source=prebid'); - expect(userSyncs[1].url).to.contain('source=prebid'); - expect(userSyncs[2].url).to.contain('source=prebid'); - }); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.contain('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch'); + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.contain('https://ms-cookie-sync.presage.io/ttd/init-sync'); + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.contain('https://ms-cookie-sync.presage.io/xandr/init-sync'); + }); - it('should set the tcString as query param', () => { - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs[0].url).to.contain(`iab_string=${gdprConsent.consentString}`); - expect(userSyncs[1].url).to.contain(`iab_string=${gdprConsent.consentString}`); - expect(userSyncs[2].url).to.contain(`iab_string=${gdprConsent.consentString}`); - }); + it('should set the source as query param', () => { + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs[0].url).to.contain('source=prebid'); + expect(userSyncs[1].url).to.contain('source=prebid'); + expect(userSyncs[2].url).to.contain('source=prebid'); + }); - it('should return an empty array when pixel is disable', () => { - syncOptions.pixelEnabled = false; - expect(spec.getUserSyncs(syncOptions, [], gdprConsent)).to.have.lengthOf(0); - }); + it('should set the tcString as query param', () => { + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs[0].url).to.contain(`iab_string=${gdprConsent.consentString}`); + expect(userSyncs[1].url).to.contain(`iab_string=${gdprConsent.consentString}`); + expect(userSyncs[2].url).to.contain(`iab_string=${gdprConsent.consentString}`); + }); - it('should return syncs array with three elements of type image when consentString is undefined', () => { - gdprConsent = { - gdprApplies: true, - consentString: undefined - }; + it('should return an empty array when pixel is disable', () => { + syncOptions.pixelEnabled = false; + expect(spec.getUserSyncs(syncOptions, [], gdprConsent)).to.have.lengthOf(0); + }); - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') - }); + it('should return syncs array with three elements of type image when consentString is undefined', () => { + gdprConsent = { + gdprApplies: true, + consentString: undefined + }; - it('should return syncs array with three elements of type image when consentString is null', () => { - gdprConsent = { - gdprApplies: true, - consentString: null - }; + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') - }); + it('should return syncs array with three elements of type image when consentString is null', () => { + gdprConsent = { + gdprApplies: true, + consentString: null + }; - it('should return syncs array with three elements of type image when gdprConsent is undefined', () => { - gdprConsent = undefined; - - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') - }); + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); - it('should return syncs array with three elements of type image when gdprConsent is null', () => { - gdprConsent = null; - - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') - }); + it('should return syncs array with three elements of type image when gdprConsent is undefined', () => { + gdprConsent = undefined; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); - it('should return syncs array with three elements of type image when gdprConsent is null and gdprApplies is false', () => { - gdprConsent = { - gdprApplies: false, - consentString: null - }; + it('should return syncs array with three elements of type image when gdprConsent is null', () => { + gdprConsent = null; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); + + it('should return syncs array with three elements of type image when gdprConsent is null and gdprApplies is false', () => { + gdprConsent = { + gdprApplies: false, + consentString: null + }; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); + + it('should return syncs array with three elements of type image when gdprConsent is empty string and gdprApplies is false', () => { + gdprConsent = { + gdprApplies: false, + consentString: '' + }; - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(3); + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') + expect(userSyncs[1].type).to.equal('image'); + expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') + expect(userSyncs[2].type).to.equal('image'); + expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + }); }); - it('should return syncs array with three elements of type image when gdprConsent is empty string and gdprApplies is false', () => { - gdprConsent = { - gdprApplies: false, - consentString: '' - }; + describe('iframe', () => { + beforeEach(() => { + syncOptions = { iframeEnabled: true }; + }); - const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); - expect(userSyncs).to.have.lengthOf(3); - expect(userSyncs[0].type).to.equal('image'); - expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/v1/init-sync/bid-switch?iab_string=&source=prebid') - expect(userSyncs[1].type).to.equal('image'); - expect(userSyncs[1].url).to.equal('https://ms-cookie-sync.presage.io/ttd/init-sync?iab_string=&source=prebid') - expect(userSyncs[2].type).to.equal('image'); - expect(userSyncs[2].url).to.equal('https://ms-cookie-sync.presage.io/xandr/init-sync?iab_string=&source=prebid') + it('should return syncs array with one element of type iframe', () => { + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + + expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs[0].type).to.equal('iframe'); + expect(userSyncs[0].url).to.contain('https://ms-cookie-sync.presage.io/user-sync.html'); + }); + + it('should set the source as query param', () => { + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs[0].url).to.contain('source=prebid'); + }); + + it('should set the tcString as query param', () => { + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs[0].url).to.contain(`gdpr_consent=${gdprConsent.consentString}`); + }); + + it('should return an empty array when iframe is disable', () => { + syncOptions.iframeEnabled = false; + expect(spec.getUserSyncs(syncOptions, [], gdprConsent)).to.have.lengthOf(0); + }); + + it('should return syncs array with one element of type iframe when consentString is undefined', () => { + gdprConsent = { + gdprApplies: true, + consentString: undefined + }; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs[0].type).to.equal('iframe'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') + }); + + it('should return syncs array with one element of type iframe when consentString is null', () => { + gdprConsent = { + gdprApplies: true, + consentString: null + }; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs[0].type).to.equal('iframe'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') + }); + + it('should return syncs array with one element of type iframe when gdprConsent is undefined', () => { + gdprConsent = undefined; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs[0].type).to.equal('iframe'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') + }); + + it('should return syncs array with one element of type iframe when gdprConsent is null', () => { + gdprConsent = null; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs[0].type).to.equal('iframe'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') + }); + + it('should return syncs array with one element of type iframe when gdprConsent is null and gdprApplies is false', () => { + gdprConsent = { + gdprApplies: false, + consentString: null + }; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs[0].type).to.equal('iframe'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') + }); + + it('should return syncs array with one element of type iframe when gdprConsent is empty string and gdprApplies is false', () => { + gdprConsent = { + gdprApplies: false, + consentString: '' + }; + + const userSyncs = spec.getUserSyncs(syncOptions, [], gdprConsent); + expect(userSyncs).to.have.lengthOf(1); + expect(userSyncs[0].type).to.equal('iframe'); + expect(userSyncs[0].url).to.equal('https://ms-cookie-sync.presage.io/user-sync.html?gdpr_consent=&source=prebid') + }); }); }); @@ -311,7 +411,7 @@ describe('OguryBidAdapter', function () { }, ext: { prebidversion: '$prebid.version$', - adapterversion: '1.4.1' + adapterversion: '1.5.0' }, device: { w: stubbedWidth, @@ -713,7 +813,7 @@ describe('OguryBidAdapter', function () { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[0].adomain }, nurl: openRtbBidResponse.body.seatbid[0].bid[0].nurl, - adapterVersion: '1.4.1', + adapterVersion: '1.5.0', prebidVersion: '$prebid.version$' }, { requestId: openRtbBidResponse.body.seatbid[0].bid[1].impid, @@ -730,7 +830,7 @@ describe('OguryBidAdapter', function () { advertiserDomains: openRtbBidResponse.body.seatbid[0].bid[1].adomain }, nurl: openRtbBidResponse.body.seatbid[0].bid[1].nurl, - adapterVersion: '1.4.1', + adapterVersion: '1.5.0', prebidVersion: '$prebid.version$' }] From e3180c7a07fc4c23714df65694d993152a012470 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Wed, 12 Jul 2023 08:16:06 -0400 Subject: [PATCH 36/92] Prebid Core : add optional targeting keys CRID and DSP (#10216) * Update auction.js * Update constants.json * Update auction.js * Update auctionmanager_spec.js * Update auction.js * Update auction.js * Update auctionmanager_spec.js --- src/auction.js | 26 ++++++++++++++++++++++++-- src/constants.json | 4 +++- test/spec/auctionmanager_spec.js | 30 +++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/auction.js b/src/auction.js index 69febccc94e3..48e1a8e34369 100644 --- a/src/auction.js +++ b/src/auction.js @@ -4,7 +4,7 @@ * In Prebid 0.x, $$PREBID_GLOBAL$$ had _bidsRequested and _bidsReceived as public properties. * Starting 1.0, Prebid will support concurrent auctions. Each auction instance will store private properties, bidsRequested and bidsReceived. * - * AuctionManager will create instance of auction and will store all the auctions. + * AuctionManager will create an instance of auction and will store all the auctions. * */ @@ -820,6 +820,16 @@ export const getPriceByGranularity = (granularity) => { } } +/** + * This function returns a function to get crid from bid response + * @returns {function} + */ +export const getCreativeId = () => { + return (bid) => { + return (bid.creativeId) ? bid.creativeId : ''; + } +} + /** * This function returns a function to get first advertiser domain from bid response meta * @returns {function} @@ -830,6 +840,16 @@ export const getAdvertiserDomain = () => { } } +/** + * This function returns a function to get dsp name or id from bid response meta + * @returns {function} + */ +export const getDSP = () => { + return (bid) => { + return (bid.meta && (bid.meta.networkId || bid.meta.networkName)) ? deepAccess(bid, 'meta.networkName') || deepAccess(bid, 'meta.networkId') : ''; + } +} + /** * This function returns a function to get the primary category id from bid response meta * @returns {function} @@ -866,6 +886,8 @@ function defaultAdserverTargeting() { createKeyVal(TARGETING_KEYS.FORMAT, 'mediaType'), createKeyVal(TARGETING_KEYS.ADOMAIN, getAdvertiserDomain()), createKeyVal(TARGETING_KEYS.ACAT, getPrimaryCatId()), + createKeyVal(TARGETING_KEYS.DSP, getDSP()), + createKeyVal(TARGETING_KEYS.CRID, getCreativeId()), ] } @@ -957,7 +979,7 @@ function setKeys(keyValues, bidderSettings, custBidObj, bidReq) { if ( ((typeof bidderSettings.suppressEmptyKeys !== 'undefined' && bidderSettings.suppressEmptyKeys === true) || - key === CONSTANTS.TARGETING_KEYS.DEAL || key === CONSTANTS.TARGETING_KEYS.ACAT) && // hb_deal & hb_acat are suppressed automatically if not set + key === CONSTANTS.TARGETING_KEYS.DEAL || key === CONSTANTS.TARGETING_KEYS.ACAT || key === CONSTANTS.TARGETING_KEYS.DSP || key === CONSTANTS.TARGETING_KEYS.CRID) && // hb_deal & hb_acat are suppressed automatically if not set ( isEmptyStr(value) || value === null || diff --git a/src/constants.json b/src/constants.json index afc4503cbfb5..f198c8fcac4a 100644 --- a/src/constants.json +++ b/src/constants.json @@ -77,7 +77,9 @@ "CACHE_ID": "hb_cache_id", "CACHE_HOST": "hb_cache_host", "ADOMAIN": "hb_adomain", - "ACAT": "hb_acat" + "ACAT": "hb_acat", + "CRID": "hb_crid", + "DSP": "hb_dsp" }, "DEFAULT_TARGETING_KEYS": { "BIDDER": "hb_bidder", diff --git a/test/spec/auctionmanager_spec.js b/test/spec/auctionmanager_spec.js index dd59b16d4929..4061e757d97f 100644 --- a/test/spec/auctionmanager_spec.js +++ b/test/spec/auctionmanager_spec.js @@ -185,9 +185,11 @@ describe('auctionmanager.js', function () { adId: '1adId', source: 'client', mediaType: 'banner', + creativeId: 'monkeys', meta: { advertiserDomains: ['adomain'], - primaryCatId: 'IAB-test' + primaryCatId: 'IAB-test', + networkId: '123987' } }; @@ -202,6 +204,8 @@ describe('auctionmanager.js', function () { expected[ CONSTANTS.TARGETING_KEYS.FORMAT ] = bid.mediaType; expected[ CONSTANTS.TARGETING_KEYS.ADOMAIN ] = bid.meta.advertiserDomains[0]; expected[ CONSTANTS.TARGETING_KEYS.ACAT ] = bid.meta.primaryCatId; + expected[ CONSTANTS.TARGETING_KEYS.DSP ] = bid.meta.networkId; + expected[ CONSTANTS.TARGETING_KEYS.CRID ] = bid.creativeId; if (bid.mediaType === 'video') { expected[ CONSTANTS.TARGETING_KEYS.UUID ] = bid.videoCacheKey; expected[ CONSTANTS.TARGETING_KEYS.CACHE_ID ] = bid.videoCacheKey; @@ -304,6 +308,18 @@ describe('auctionmanager.js', function () { return bidResponse.meta.advertiserDomains[0]; } }, + { + key: CONSTANTS.TARGETING_KEYS.CRID, + val: function (bidResponse) { + return bidResponse.creativeId; + } + }, + { + key: CONSTANTS.TARGETING_KEYS.DSP, + val: function (bidResponse) { + return bidResponse.meta.networkId; + } + }, { key: CONSTANTS.TARGETING_KEYS.ACAT, val: function (bidResponse) { @@ -388,6 +404,18 @@ describe('auctionmanager.js', function () { return bidResponse.meta.advertiserDomains[0]; } }, + { + key: CONSTANTS.TARGETING_KEYS.CRID, + val: function (bidResponse) { + return bidResponse.creativeId; + } + }, + { + key: CONSTANTS.TARGETING_KEYS.DSP, + val: function (bidResponse) { + return bidResponse.meta.networkId; + } + }, { key: CONSTANTS.TARGETING_KEYS.ACAT, val: function (bidResponse) { From a9f16ee4da673cacd30eb0b1db0c209ede8ed03a Mon Sep 17 00:00:00 2001 From: Ishara Chan tung <80039383+github-ishara-chan-tung@users.noreply.github.com> Date: Wed, 12 Jul 2023 16:33:05 +0200 Subject: [PATCH 37/92] Add video.plcmt to Teads bid adpater (#10221) --- modules/teadsBidAdapter.js | 2 + test/spec/modules/teadsBidAdapter_spec.js | 91 +++++++++++++++++------ 2 files changed, 72 insertions(+), 21 deletions(-) diff --git a/modules/teadsBidAdapter.js b/modules/teadsBidAdapter.js index 6e1006ecf003..12a5bdd13e5f 100644 --- a/modules/teadsBidAdapter.js +++ b/modules/teadsBidAdapter.js @@ -220,6 +220,7 @@ function buildRequestObject(bid) { let placementId = getValue(bid.params, 'placementId'); let pageId = getValue(bid.params, 'pageId'); const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid'); + const videoPlcmt = deepAccess(bid, 'mediaTypes.video.plcmt'); reqObj.sizes = getSizes(bid); reqObj.bidId = getBidIdParameter('bidId', bid); @@ -229,6 +230,7 @@ function buildRequestObject(bid) { reqObj.adUnitCode = getBidIdParameter('adUnitCode', bid); reqObj.transactionId = bid.ortb2Imp?.ext?.tid || ''; if (gpid) { reqObj.gpid = gpid; } + if (videoPlcmt) { reqObj.videoPlcmt = videoPlcmt; } return reqObj; } diff --git a/test/spec/modules/teadsBidAdapter_spec.js b/test/spec/modules/teadsBidAdapter_spec.js index a4b94135badb..0771ac62e677 100644 --- a/test/spec/modules/teadsBidAdapter_spec.js +++ b/test/spec/modules/teadsBidAdapter_spec.js @@ -108,21 +108,21 @@ describe('teadsBidAdapter', () => { } ]; - let bidderResquestDefault = { + let bidderRequestDefault = { 'auctionId': '1d1a030790a475', 'bidderRequestId': '22edbae2733bf6', 'timeout': 3000 }; it('should send bid request to ENDPOINT via POST', function() { - const request = spec.buildRequests(bidRequests, bidderResquestDefault); + const request = spec.buildRequests(bidRequests, bidderRequestDefault); expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('POST'); }); it('should not send auctionId in bid request ', function() { - const request = spec.buildRequests(bidRequests, bidderResquestDefault); + const request = spec.buildRequests(bidRequests, bidderRequestDefault); const payload = JSON.parse(request.data); expect(payload.data[0].auctionId).to.not.exist @@ -169,6 +169,55 @@ describe('teadsBidAdapter', () => { expect(payload.gdpr_iab.apiVersion).to.equal(2); }); + it('should add videoPlcmt to payload', function () { + let bidRequestWithVideoPlcmt = Object.assign({}, bidRequests[0], { + mediaTypes: { + video: { + plcmt: 1 + } + } + }); + + const request = spec.buildRequests([bidRequestWithVideoPlcmt], bidderRequestDefault); + const payload = JSON.parse(request.data); + + expect(payload.data[0].videoPlcmt).to.exist; + expect(payload.data[0].videoPlcmt).to.equal(1); + }); + + it('should not add videoPlcmt to payload if empty', function () { + let bidRequestWithNullVideoPlcmt = Object.assign({}, bidRequests[0], { + mediaTypes: { + video: { + plcmt: null + } + } + }); + + let bidRequestWithEmptyVideoPlcmt = Object.assign({}, bidRequests[0], { + mediaTypes: { + video: { + plcmt: '' + } + } + }); + + const request1 = spec.buildRequests([bidRequestWithNullVideoPlcmt], bidderRequestDefault); + const request2 = spec.buildRequests([bidRequestWithEmptyVideoPlcmt], bidderRequestDefault); + const payload1 = JSON.parse(request1.data); + const payload2 = JSON.parse(request2.data); + + expect(payload1.data[0].videoPlcmt).to.not.exist; + expect(payload2.data[0].videoPlcmt).to.not.exist; + }); + + it('should not add videoPlcmt to payload if it is not in bid request', function () { + const request = spec.buildRequests(bidRequests, bidderRequestDefault); + const payload = JSON.parse(request.data); + + expect(payload.data[0].videoPlcmt).to.not.exist; + }); + it('should add referer info to payload', function () { const bidRequest = Object.assign({}, bidRequests[0]) const bidderRequest = { @@ -186,7 +235,7 @@ describe('teadsBidAdapter', () => { }); it('should add networkBandwidth info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderResquestDefault); + const request = spec.buildRequests(bidRequests, bidderRequestDefault); const payload = JSON.parse(request.data); const bandwidth = window.navigator && window.navigator.connection && window.navigator.connection.downlink; @@ -201,7 +250,7 @@ describe('teadsBidAdapter', () => { }); it('should add pageReferrer info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderResquestDefault); + const request = spec.buildRequests(bidRequests, bidderRequestDefault); const payload = JSON.parse(request.data); expect(payload.pageReferrer).to.exist; @@ -209,7 +258,7 @@ describe('teadsBidAdapter', () => { }); it('should add timeToFirstByte info to payload', function () { - const request = spec.buildRequests(bidRequests, bidderResquestDefault); + const request = spec.buildRequests(bidRequests, bidderRequestDefault); const payload = JSON.parse(request.data); const performance = window.performance || window.webkitPerformance || window.msPerformance || window.mozPerformance; @@ -410,7 +459,7 @@ describe('teadsBidAdapter', () => { } }); - const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const request = spec.buildRequests([bidRequest], bidderRequestDefault); const payload = JSON.parse(request.data); expect(payload.schain).to.exist; @@ -458,7 +507,7 @@ describe('teadsBidAdapter', () => { } }); - const requestWithUserAgentClientHints = spec.buildRequests([bidRequest], bidderResquestDefault); + const requestWithUserAgentClientHints = spec.buildRequests([bidRequest], bidderRequestDefault); const payload = JSON.parse(requestWithUserAgentClientHints.data); expect(payload.userAgentClientHints).to.exist; @@ -489,7 +538,7 @@ describe('teadsBidAdapter', () => { } ); - const defaultRequest = spec.buildRequests(bidRequests, bidderResquestDefault); + const defaultRequest = spec.buildRequests(bidRequests, bidderRequestDefault); expect(JSON.parse(defaultRequest.data).userAgentClientHints).to.not.exist; }); @@ -566,7 +615,7 @@ describe('teadsBidAdapter', () => { userId: {} // no property -> assumption that the system is disabled }; - const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const request = spec.buildRequests([bidRequest], bidderRequestDefault); const payload = JSON.parse(request.data); for (const userId in userIdModules) { @@ -575,7 +624,7 @@ describe('teadsBidAdapter', () => { }); it(`should not add param to payload if user id field is absent`, function () { - const request = spec.buildRequests([baseBidRequest], bidderResquestDefault); + const request = spec.buildRequests([baseBidRequest], bidderRequestDefault); const payload = JSON.parse(request.data); for (const userId in userIdModules) { @@ -592,7 +641,7 @@ describe('teadsBidAdapter', () => { } }; - const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const request = spec.buildRequests([bidRequest], bidderRequestDefault); const payload = JSON.parse(request.data); expect(payload).not.to.have.property('liveRampId'); @@ -612,7 +661,7 @@ describe('teadsBidAdapter', () => { userId: userIdObject }; - const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const request = spec.buildRequests([bidRequest], bidderRequestDefault); const payload = JSON.parse(request.data); expect(payload['unifiedId2']).to.equal('unifiedId2-id'); @@ -641,7 +690,7 @@ describe('teadsBidAdapter', () => { } }; - const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const request = spec.buildRequests([bidRequest], bidderRequestDefault); const payload = JSON.parse(request.data); expect(payload).not.to.have.property('firstPartyCookieTeadsId'); @@ -659,7 +708,7 @@ describe('teadsBidAdapter', () => { } }; - const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const request = spec.buildRequests([bidRequest], bidderRequestDefault); const payload = JSON.parse(request.data); expect(payload).not.to.have.property('firstPartyCookieTeadsId'); @@ -677,7 +726,7 @@ describe('teadsBidAdapter', () => { } }; - const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const request = spec.buildRequests([bidRequest], bidderRequestDefault); const payload = JSON.parse(request.data); @@ -697,7 +746,7 @@ describe('teadsBidAdapter', () => { } }; - const request = spec.buildRequests([bidRequest], bidderResquestDefault); + const request = spec.buildRequests([bidRequest], bidderRequestDefault); const payload = JSON.parse(request.data); @@ -750,7 +799,7 @@ describe('teadsBidAdapter', () => { }; } ); - const request = spec.buildRequests(updatedBidRequests, bidderResquestDefault); + const request = spec.buildRequests(updatedBidRequests, bidderRequestDefault); const payload = JSON.parse(request.data); expect(payload.data[0].gpid).to.equal('1111/home-left-0'); @@ -767,7 +816,7 @@ describe('teadsBidAdapter', () => { } })); - const request = spec.buildRequests(updatedBidRequests, bidderResquestDefault); + const request = spec.buildRequests(updatedBidRequests, bidderRequestDefault); const payload = JSON.parse(request.data); return payload.data.forEach(bid => { @@ -784,7 +833,7 @@ describe('teadsBidAdapter', () => { } })); - const request = spec.buildRequests(updatedBidRequests, bidderResquestDefault); + const request = spec.buildRequests(updatedBidRequests, bidderRequestDefault); const payload = JSON.parse(request.data); return payload.data.forEach(bid => { @@ -795,7 +844,7 @@ describe('teadsBidAdapter', () => { function checkMediaTypesSizes(mediaTypes, expectedSizes) { const bidRequestWithBannerSizes = Object.assign(bidRequests[0], mediaTypes); - const requestWithBannerSizes = spec.buildRequests([bidRequestWithBannerSizes], bidderResquestDefault); + const requestWithBannerSizes = spec.buildRequests([bidRequestWithBannerSizes], bidderRequestDefault); const payloadWithBannerSizes = JSON.parse(requestWithBannerSizes.data); return payloadWithBannerSizes.data.forEach(bid => { From 9cf61d1fe517f4831143d5f8e26ab6605a1e50b5 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 12 Jul 2023 12:33:06 -0700 Subject: [PATCH 38/92] video module examples: fix `requestBids` invocations (#10224) --- integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html | 2 +- integrationExamples/videoModule/jwplayer/eventListeners.html | 4 ++-- integrationExamples/videoModule/jwplayer/eventsUI.html | 2 +- .../videoModule/jwplayer/gamAdServerMediation.html | 2 +- integrationExamples/videoModule/jwplayer/mediaMetadata.html | 2 +- integrationExamples/videoModule/jwplayer/playlist.html | 2 +- integrationExamples/videoModule/videojs/bidMarkedAsUsed.html | 2 +- .../videoModule/videojs/bidRequestScheduling.html | 2 +- integrationExamples/videoModule/videojs/eventListeners.html | 2 +- integrationExamples/videoModule/videojs/eventsUI.html | 2 +- .../videoModule/videojs/gamAdServerMediation.html | 2 +- integrationExamples/videoModule/videojs/mediaMetadata.html | 2 +- integrationExamples/videoModule/videojs/playlist.html | 2 +- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html index 044b4295e677..6aa996783545 100644 --- a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html @@ -84,7 +84,7 @@ console.log('An Ad Impression came from a Bid: ', e); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/jwplayer/eventListeners.html b/integrationExamples/videoModule/jwplayer/eventListeners.html index 0b43ff44d6c6..5332270f5609 100644 --- a/integrationExamples/videoModule/jwplayer/eventListeners.html +++ b/integrationExamples/videoModule/jwplayer/eventListeners.html @@ -29,7 +29,7 @@ } }] }]; - + pbjs.que.push(function () { pbjs.setConfig({ video: { @@ -237,7 +237,7 @@ console.log('The ad error resulted from a bid! \n', event); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/jwplayer/eventsUI.html b/integrationExamples/videoModule/jwplayer/eventsUI.html index 69ace5f6c2bd..bdfe5f3b883b 100644 --- a/integrationExamples/videoModule/jwplayer/eventsUI.html +++ b/integrationExamples/videoModule/jwplayer/eventsUI.html @@ -278,7 +278,7 @@ eventHandler(e, "bidImpression", "auction", ); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html index 296a3265bd10..fc7c1b9486cf 100644 --- a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html +++ b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html @@ -106,7 +106,7 @@ console.log('An Ad Impression came from a Bid: ', e); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/jwplayer/mediaMetadata.html b/integrationExamples/videoModule/jwplayer/mediaMetadata.html index 815f5c4d6d76..03f74f5bd0fd 100644 --- a/integrationExamples/videoModule/jwplayer/mediaMetadata.html +++ b/integrationExamples/videoModule/jwplayer/mediaMetadata.html @@ -66,7 +66,7 @@ console.log('videos pb contentLoaded: ', e); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/jwplayer/playlist.html b/integrationExamples/videoModule/jwplayer/playlist.html index b77a1ec05fc6..223fee15c6fb 100644 --- a/integrationExamples/videoModule/jwplayer/playlist.html +++ b/integrationExamples/videoModule/jwplayer/playlist.html @@ -107,7 +107,7 @@ console.log('videos pb playlistComplete: ', e); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html b/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html index b645a58a4fc4..d6656bc0c939 100644 --- a/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/videojs/bidMarkedAsUsed.html @@ -109,7 +109,7 @@ console.log('An Ad Impression came from a Bid: ', e); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/videojs/bidRequestScheduling.html b/integrationExamples/videoModule/videojs/bidRequestScheduling.html index 48f8b8685e15..eb10fda89a2c 100644 --- a/integrationExamples/videoModule/videojs/bidRequestScheduling.html +++ b/integrationExamples/videoModule/videojs/bidRequestScheduling.html @@ -111,7 +111,7 @@ pbjs.onEvent('videoTime', (e) => { // when the video reaches the midroll, request bids and update the offset. if (e.position >= midrollOffset) { - pbjs.requestBids(adUnits); + pbjs.requestBids(); midrollOffset += 10; } }); diff --git a/integrationExamples/videoModule/videojs/eventListeners.html b/integrationExamples/videoModule/videojs/eventListeners.html index 16edaf4da414..1966f134e029 100644 --- a/integrationExamples/videoModule/videojs/eventListeners.html +++ b/integrationExamples/videoModule/videojs/eventListeners.html @@ -225,7 +225,7 @@ console.log('videos pb bid Error: ', e); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/videojs/eventsUI.html b/integrationExamples/videoModule/videojs/eventsUI.html index 04e0ca9eaf89..9eba09f7a52e 100644 --- a/integrationExamples/videoModule/videojs/eventsUI.html +++ b/integrationExamples/videoModule/videojs/eventsUI.html @@ -301,7 +301,7 @@ eventHandler(e, "bidImpression", "auction", ); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/videojs/gamAdServerMediation.html b/integrationExamples/videoModule/videojs/gamAdServerMediation.html index 9efd77e96815..6ffc1a67c031 100644 --- a/integrationExamples/videoModule/videojs/gamAdServerMediation.html +++ b/integrationExamples/videoModule/videojs/gamAdServerMediation.html @@ -123,7 +123,7 @@ console.log('An Ad Impression came from a Bid: ', e); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/videojs/mediaMetadata.html b/integrationExamples/videoModule/videojs/mediaMetadata.html index eef12b0e19d6..ede076fd8141 100644 --- a/integrationExamples/videoModule/videojs/mediaMetadata.html +++ b/integrationExamples/videoModule/videojs/mediaMetadata.html @@ -87,7 +87,7 @@ console.log('videos pb contentLoaded: ', e); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); diff --git a/integrationExamples/videoModule/videojs/playlist.html b/integrationExamples/videoModule/videojs/playlist.html index a9e3d1bc99b2..eb813f095f7e 100644 --- a/integrationExamples/videoModule/videojs/playlist.html +++ b/integrationExamples/videoModule/videojs/playlist.html @@ -134,7 +134,7 @@ console.log('videos pb complete: ', e); }); - pbjs.requestBids(adUnits); + pbjs.requestBids(); }); From fe5c18eaf32c9f2516a90d64f81b6e550974eff4 Mon Sep 17 00:00:00 2001 From: Carlos Felix Date: Wed, 12 Jul 2023 15:21:41 -0500 Subject: [PATCH 39/92] 33Across Bid Adapter & UIM: Add GPP & COPPA support (#10173) * GPP in 33x bid adapter * Send GPP parameters to 33across ID system endpoint * always send gdpr consent string when present, regardless of gdprApplies flag * 33x coppa changes * fix failing 33across tests after master update --- modules/33acrossBidAdapter.js | 38 ++-- modules/33acrossIdSystem.js | 19 +- test/spec/modules/33acrossBidAdapter_spec.js | 223 ++++++++++++++++--- test/spec/modules/33acrossIdSystem_spec.js | 149 ++++++++++--- 4 files changed, 349 insertions(+), 80 deletions(-) diff --git a/modules/33acrossBidAdapter.js b/modules/33acrossBidAdapter.js index 26fc0a11fd5d..b965183de19c 100644 --- a/modules/33acrossBidAdapter.js +++ b/modules/33acrossBidAdapter.js @@ -167,6 +167,7 @@ function buildRequests(bidRequests, bidderRequest) { ttxSettings, gdprConsent, uspConsent, + gppConsent, pageUrl, referer } = _buildRequestParams(bidRequests, bidderRequest); @@ -181,6 +182,7 @@ function buildRequests(bidRequests, bidderRequest) { bidRequests: groupedRequests[key], gdprConsent, uspConsent, + gppConsent, pageUrl, referer, ttxSettings, @@ -200,20 +202,15 @@ function _buildRequestParams(bidRequests, bidderRequest) { gdprApplies: false }, bidderRequest && bidderRequest.gdprConsent); - const uspConsent = bidderRequest && bidderRequest.uspConsent; - - const pageUrl = bidderRequest?.refererInfo?.page; - - const referer = bidderRequest?.refererInfo?.ref; - adapterState.uniqueSiteIds = bidRequests.map(req => req.params.siteId).filter(uniques); return { ttxSettings, gdprConsent, - uspConsent, - pageUrl, - referer + uspConsent: bidderRequest?.uspConsent, + gppConsent: bidderRequest?.gppConsent, + pageUrl: bidderRequest?.refererInfo?.page, + referer: bidderRequest?.refererInfo?.ref } } @@ -247,10 +244,11 @@ function _getMRAKey(bidRequest) { } // Infer the necessary data from valid bid for a minimal ttxRequest and create HTTP request -function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageUrl, referer, ttxSettings, bidderRequest }) { +function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, gppConsent = {}, pageUrl, referer, ttxSettings, bidderRequest }) { const ttxRequest = {}; const firstBidRequest = bidRequests[0]; const { siteId, test } = firstBidRequest.params; + const coppaValue = config.getConfig('coppa'); /* * Infer data for the request payload @@ -296,6 +294,17 @@ function _createServerRequest({ bidRequests, gdprConsent = {}, uspConsent, pageU }); } + if (gppConsent.gppString) { + Object.assign(ttxRequest.regs, { + 'gpp': gppConsent.gppString, + 'gpp_sid': gppConsent.applicableSections + }); + } + + if (coppaValue !== undefined) { + ttxRequest.regs.coppa = Number(!!coppaValue); + } + ttxRequest.ext = { ttx: { prebidStartedAt: Date.now(), @@ -717,10 +726,10 @@ function _createBidResponse(bid, cur) { // Else no syncs // For logic on how we handle gdpr data see _createSyncs and module's unit tests // '33acrossBidAdapter#getUserSyncs' -function getUserSyncs(syncOptions, responses, gdprConsent, uspConsent) { +function getUserSyncs(syncOptions, responses, gdprConsent, uspConsent, gppConsent) { const syncUrls = ( (syncOptions.iframeEnabled) - ? adapterState.uniqueSiteIds.map((siteId) => _createSync({ gdprConsent, uspConsent, siteId })) + ? adapterState.uniqueSiteIds.map((siteId) => _createSync({ gdprConsent, uspConsent, gppConsent, siteId })) : ([]) ); @@ -731,15 +740,16 @@ function getUserSyncs(syncOptions, responses, gdprConsent, uspConsent) { } // Sync object will always be of type iframe for TTX -function _createSync({ siteId = 'zzz000000000003zzz', gdprConsent = {}, uspConsent }) { +function _createSync({ siteId = 'zzz000000000003zzz', gdprConsent = {}, uspConsent, gppConsent = {} }) { const ttxSettings = config.getConfig('ttxSettings'); const syncUrl = (ttxSettings && ttxSettings.syncUrl) || SYNC_ENDPOINT; const { consentString, gdprApplies } = gdprConsent; + const { gppString = '', applicableSections = [] } = gppConsent; const sync = { type: 'iframe', - url: `${syncUrl}&id=${siteId}&gdpr_consent=${encodeURIComponent(consentString)}&us_privacy=${encodeURIComponent(uspConsent)}` + url: `${syncUrl}&id=${siteId}&gdpr_consent=${encodeURIComponent(consentString)}&us_privacy=${encodeURIComponent(uspConsent)}&gpp=${encodeURIComponent(gppString)}&gpp_sid=${encodeURIComponent(applicableSections.join(','))}` }; if (typeof gdprApplies === 'boolean') { diff --git a/modules/33acrossIdSystem.js b/modules/33acrossIdSystem.js index be81b26b110b..14cd8586d50a 100644 --- a/modules/33acrossIdSystem.js +++ b/modules/33acrossIdSystem.js @@ -8,7 +8,7 @@ import { logMessage, logError } from '../src/utils.js'; import { ajaxBuilder } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; -import { uspDataHandler } from '../src/adapterManager.js'; +import { uspDataHandler, coppaDataHandler, gppDataHandler } from '../src/adapterManager.js'; const MODULE_NAME = '33acrossId'; const API_URL = 'https://lexicon.33across.com/v1/envelope'; @@ -37,19 +37,30 @@ function getEnvelope(response) { function calculateQueryStringParams(pid, gdprConsentData) { const uspString = uspDataHandler.getConsentData(); const gdprApplies = Boolean(gdprConsentData?.gdprApplies); + const coppaValue = coppaDataHandler.getCoppa(); + const gppConsent = gppDataHandler.getConsentData(); + const params = { pid, gdpr: Number(gdprApplies), src: CALLER_NAME, - ver: '$prebid.version$' + ver: '$prebid.version$', + coppa: Number(coppaValue) }; if (uspString) { params.us_privacy = uspString; } - if (gdprApplies) { - params.gdpr_consent = gdprConsentData.consentString || ''; + if (gppConsent) { + const { gppString = '', applicableSections = [] } = gppConsent; + + params.gpp = gppString; + params.gpp_sid = encodeURIComponent(applicableSections.join(',')) + } + + if (gdprConsentData?.consentString) { + params.gdpr_consent = gdprConsentData.consentString; } return params; diff --git a/test/spec/modules/33acrossBidAdapter_spec.js b/test/spec/modules/33acrossBidAdapter_spec.js index aa2621f1fa0c..9cc038428bca 100644 --- a/test/spec/modules/33acrossBidAdapter_spec.js +++ b/test/spec/modules/33acrossBidAdapter_spec.js @@ -200,6 +200,29 @@ describe('33acrossBidAdapter:', function () { return this; }; + this.withCoppa = coppaValue => { + Object.assign(ttxRequest.regs, { + coppa: coppaValue + }); + + return this; + }; + + this.withGppConsent = (consentString, applicableSections) => { + Object.assign(ttxRequest, { + regs: { + gpp: consentString, + gpp_sid: applicableSections, + ext: Object.assign( + {}, + ttxRequest.regs.ext + ) + } + }); + + return this; + }; + this.withSite = site => { Object.assign(ttxRequest, { site }); return this; @@ -1049,11 +1072,11 @@ describe('33acrossBidAdapter:', function () { }); it('returns corresponding test server requests with gdpr consent data', function() { - sandbox.stub(config, 'getConfig').callsFake(() => { - return { + sandbox.stub(config, 'getConfig') + .withArgs('ttxSettings') + .returns({ 'url': 'https://foo.com/hb/' - } - }); + }); const ttxRequest = new TtxRequestBuilder() .withBanner() @@ -1085,11 +1108,11 @@ describe('33acrossBidAdapter:', function () { }); it('returns corresponding test server requests with default gdpr consent data', function() { - sandbox.stub(config, 'getConfig').callsFake(() => { - return { + sandbox.stub(config, 'getConfig') + .withArgs('ttxSettings') + .returns({ 'url': 'https://foo.com/hb/' - } - }); + }); const ttxRequest = new TtxRequestBuilder() .withBanner() @@ -1128,11 +1151,11 @@ describe('33acrossBidAdapter:', function () { }); it('returns corresponding test server requests with us_privacy consent data', function() { - sandbox.stub(config, 'getConfig').callsFake(() => { - return { + sandbox.stub(config, 'getConfig') + .withArgs('ttxSettings') + .returns({ 'url': 'https://foo.com/hb/' - } - }); + }); const ttxRequest = new TtxRequestBuilder() .withBanner() @@ -1164,15 +1187,102 @@ describe('33acrossBidAdapter:', function () { }); it('returns corresponding test server requests with default us_privacy consent data', function() { - sandbox.stub(config, 'getConfig').callsFake(() => { - return { + sandbox.stub(config, 'getConfig') + .withArgs('ttxSettings') + .returns({ 'url': 'https://foo.com/hb/' + }); + + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .withUrl('https://foo.com/hb/') + .build(); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + + validateBuiltServerRequest(builtServerRequest, serverRequest); + }); + }); + + context('when coppa has been enabled', function() { + beforeEach(function() { + sandbox.stub(config, 'getConfig').withArgs('coppa').returns(true); + }); + + it('returns corresponding server requests with coppa: 1', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() + .withCoppa(1) + .build(); + + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + + validateBuiltServerRequest(builtServerRequest, serverRequest); + }); + }); + + context('when coppa has been disabled', function() { + beforeEach(function() { + sandbox.stub(config, 'getConfig').withArgs('coppa').returns(false); + }); + + it('returns corresponding server requests with coppa: 0', function() { + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() + .withCoppa(0) + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + + validateBuiltServerRequest(builtServerRequest, serverRequest); + }); + }); + + context('when GPP consent data exists', function() { + beforeEach(function() { + bidderRequest = { + ...bidderRequest, + gppConsent: { + gppString: 'foo', + applicableSections: '123' } - }); + } + }); + it('returns corresponding server requests with GPP consent data', function() { const ttxRequest = new TtxRequestBuilder() .withBanner() .withProduct() + .withGppConsent('foo', '123') + .build(); + const serverRequest = new ServerRequestBuilder() + .withData(ttxRequest) + .build(); + const [ builtServerRequest ] = spec.buildRequests(bidRequests, bidderRequest); + + validateBuiltServerRequest(builtServerRequest, serverRequest); + }); + + it('returns corresponding test server requests with GPP consent data', function() { + sandbox.stub(config, 'getConfig').withArgs('ttxSettings') + .returns({ + 'url': 'https://foo.com/hb/' + }); + + const ttxRequest = new TtxRequestBuilder() + .withBanner() + .withProduct() + .withGppConsent('foo', '123') .build(); const serverRequest = new ServerRequestBuilder() .withData(ttxRequest) @@ -1747,11 +1857,11 @@ describe('33acrossBidAdapter:', function () { context('when SRA mode is enabled', function() { it('builds a single request with multiple imps corresponding to each group {siteId, productId}', function() { - sandbox.stub(config, 'getConfig').callsFake(() => { - return { + sandbox.stub(config, 'getConfig') + .withArgs('ttxSettings') + .returns({ enableSRAMode: true - } - }); + }); const bidRequests = new BidRequestsBuilder() .addBid() @@ -2219,11 +2329,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined` + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined` + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` } ] @@ -2239,11 +2349,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gdpr=1` + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=&gdpr=1` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gdpr=1` + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=&gdpr=1` } ]; @@ -2259,11 +2369,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined&gdpr=1` + url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=&gdpr=1` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined&gdpr=1` + url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=&gdpr=1` } ]; @@ -2279,11 +2389,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gdpr=0` + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=&gdpr=0` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gdpr=0` + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=&gdpr=0` } ]; expect(syncResults).to.deep.equal(expectedSyncs); @@ -2298,11 +2408,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined` + url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined` + url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=` } ]; expect(syncResults).to.deep.equal(expectedSyncs); @@ -2317,11 +2427,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined&gdpr=0` + url: `${syncs[0].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=&gdpr=0` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined&gdpr=0` + url: `${syncs[1].url}&gdpr_consent=consent123A&us_privacy=undefined&gpp=&gpp_sid=&gdpr=0` } ]; expect(syncResults).to.deep.equal(expectedSyncs); @@ -2336,11 +2446,11 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined` + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined` + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` } ] @@ -2356,11 +2466,54 @@ describe('33acrossBidAdapter:', function () { const expectedSyncs = [ { type: 'iframe', - url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=foo` + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=foo&gpp=&gpp_sid=` + }, + { + type: 'iframe', + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=foo&gpp=&gpp_sid=` + } + ]; + + expect(syncResults).to.deep.equal(expectedSyncs); + }); + }); + + context('when there is no GPP data', function() { + it('returns sync urls with empty GPP params', function() { + spec.buildRequests(bidRequests); + + const syncResults = spec.getUserSyncs(syncOptions, {}); + const expectedSyncs = [ + { + type: 'iframe', + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` + }, + { + type: 'iframe', + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=&gpp_sid=` + } + ] + + expect(syncResults).to.deep.equal(expectedSyncs); + }) + }); + + context('when there is GPP data', function() { + it('returns sync urls with GPP consent string & GPP Section ID as params', function() { + spec.buildRequests(bidRequests); + + const syncResults = spec.getUserSyncs(syncOptions, {}, {}, undefined, { + gppString: 'foo', + applicableSections: ['123', '456'] + }); + const expectedSyncs = [ + { + type: 'iframe', + url: `${syncs[0].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=foo&gpp_sid=123%2C456` }, { type: 'iframe', - url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=foo` + url: `${syncs[1].url}&gdpr_consent=undefined&us_privacy=undefined&gpp=foo&gpp_sid=123%2C456` } ]; diff --git a/test/spec/modules/33acrossIdSystem_spec.js b/test/spec/modules/33acrossIdSystem_spec.js index 5070d2b88458..4f6d7c4a6c5e 100644 --- a/test/spec/modules/33acrossIdSystem_spec.js +++ b/test/spec/modules/33acrossIdSystem_spec.js @@ -2,7 +2,7 @@ import { thirthyThreeAcrossIdSubmodule } from 'modules/33acrossIdSystem.js'; import * as utils from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; -import { uspDataHandler } from 'src/adapterManager.js'; +import { uspDataHandler, coppaDataHandler, gppDataHandler } from 'src/adapterManager.js'; describe('33acrossIdSystem', () => { describe('name', () => { @@ -67,41 +67,35 @@ describe('33acrossIdSystem', () => { expect(request.url).to.contain('gdpr=1'); }); + }); - context('and the consent string is given', () => { - it('should call endpoint with the GDPR consent string', () => { - [ - { consentString: '', expected: '' }, - { consentString: undefined, expected: '' }, - { consentString: 'foo', expected: 'foo' } - ].forEach(({ consentString, expected }, index) => { - const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ - params: { - pid: '12345' - } - }, { - gdprApplies: true, - consentString - }); - - callback(completeCallback); - - expect(server.requests[index].url).to.contain(`gdpr_consent=${expected}`); - }); + context('when GDPR doesn\'t apply', () => { + it('should call endpoint with \'gdpr=0\'', () => { + const completeCallback = () => {}; + const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345' + } + }, { + gdprApplies: false }); + + callback(completeCallback); + + const [request] = server.requests; + + expect(request.url).to.contain('gdpr=0'); }); }); - context('when GDPR doesn\'t apply', () => { - it('should call endpoint with \'gdpr=0\' and no GDPR consent string parameter', () => { + context('when the GDPR consent string is given', () => { + it('should call endpoint with the GDPR consent string', () => { const completeCallback = () => {}; const { callback } = thirthyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } }, { - gdprApplies: false, consentString: 'foo' }); @@ -109,8 +103,7 @@ describe('33acrossIdSystem', () => { const [request] = server.requests; - expect(request.url).to.contain('gdpr=0'); - expect(request.url).not.to.contain('gdpr_consent'); + expect(request.url).to.contain('gdpr_consent=foo'); }); }); @@ -157,6 +150,108 @@ describe('33acrossIdSystem', () => { }); }); + context('when coppa is enabled', () => { + it('should call endpoint with an enabled coppa signal', () => { + const completeCallback = () => {}; + const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345' + } + }); + + sinon.stub(coppaDataHandler, 'getCoppa').returns(true); + + callback(completeCallback); + + const [request] = server.requests; + + expect(request.url).to.contain('coppa=1'); + + coppaDataHandler.getCoppa.restore(); + }); + }); + + context('when coppa is not enabled', () => { + it('should call endpoint with coppa signal not enabled', () => { + const completeCallback = () => {}; + const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345' + } + }); + + sinon.stub(coppaDataHandler, 'getCoppa').returns(false); + + callback(completeCallback); + + const [request] = server.requests; + + expect(request.url).to.contain('coppa=0'); + + coppaDataHandler.getCoppa.restore(); + }); + }); + + context('when a GPP consent string is given', () => { + beforeEach(() => { + sinon.stub(gppDataHandler, 'getConsentData'); + }); + + afterEach(() => { + gppDataHandler.getConsentData.restore(); + }); + + it('should call endpoint with the GPP consent string', () => { + [ + { gppString: '', expected: '' }, + { gppString: undefined, expected: '' }, + { gppString: 'foo', expected: 'foo' }, + ].forEach(({ gppString, expected }, index) => { + const completeCallback = () => {}; + const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345' + } + }); + + gppDataHandler.getConsentData.onCall(index).returns({ + gppString + }); + + callback(completeCallback); + + expect(server.requests[index].url).to.contain(`gpp=${expected}`); + }); + }); + + it('should call endpoint with the GPP applicable sections', () => { + const gppString = 'foo'; + + [ + { applicableSections: [], expected: '' }, + { applicableSections: undefined, expected: '' }, + { applicableSections: ['1'], expected: '1' }, + { applicableSections: ['1', '2'], expected: '1%2C2' }, + ].forEach(({ applicableSections, expected }, index) => { + const completeCallback = () => {}; + const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345' + } + }); + + gppDataHandler.getConsentData.onCall(index).returns({ + gppString: 'foo', + applicableSections + }); + + callback(completeCallback); + + expect(server.requests[index].url).to.contain(`gpp_sid=${expected}`); + }); + }); + }); + context('when the partner ID is not given', () => { it('should log an error', () => { const logErrorSpy = sinon.spy(utils, 'logError'); From be01782bd852df7a3fde09d3fe15235d3f74fc53 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 12 Jul 2023 18:04:16 -0700 Subject: [PATCH 40/92] gdprEnforcement: check for purpose 4 on `enrichUfpd` as well as `transmitUfpd` (#10219) --- modules/gdprEnforcement.js | 9 ++++++--- test/spec/modules/gdprEnforcement_spec.js | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/gdprEnforcement.js b/modules/gdprEnforcement.js index fa7418a90ab0..4140c1a54f41 100644 --- a/modules/gdprEnforcement.js +++ b/modules/gdprEnforcement.js @@ -24,7 +24,7 @@ import { import {registerActivityControl} from '../src/activities/rules.js'; import { ACTIVITY_ACCESS_DEVICE, - ACTIVITY_ENRICH_EIDS, + ACTIVITY_ENRICH_EIDS, ACTIVITY_ENRICH_UFPD, ACTIVITY_FETCH_BIDS, ACTIVITY_REPORT_ANALYTICS, ACTIVITY_SYNC_USER, ACTIVITY_TRANSMIT_UFPD @@ -235,7 +235,7 @@ export const fetchBidsRule = ((rule) => { export const reportAnalyticsRule = gdprRule(7, () => purpose7Rule, analyticsBlocked, (params) => getGvlidFromAnalyticsAdapter(params[ACTIVITY_PARAM_COMPONENT_NAME], params[ACTIVITY_PARAM_ANL_CONFIG])); -export const transmitUfpdRule = gdprRule(4, () => purpose4Rule, ufpdBlocked); +export const ufpdRule = gdprRule(4, () => purpose4Rule, ufpdBlocked); /** * Compiles the TCF2.0 enforcement results into an object, which is emitted as an event payload to "tcf2Enforcement" event. @@ -301,7 +301,10 @@ export function setEnforcementConfig(config) { RULE_HANDLES.push(registerActivityControl(ACTIVITY_FETCH_BIDS, RULE_NAME, fetchBidsRule)); } if (purpose4Rule) { - RULE_HANDLES.push(registerActivityControl(ACTIVITY_TRANSMIT_UFPD, RULE_NAME, transmitUfpdRule)); + RULE_HANDLES.push( + registerActivityControl(ACTIVITY_TRANSMIT_UFPD, RULE_NAME, ufpdRule), + registerActivityControl(ACTIVITY_ENRICH_UFPD, RULE_NAME, ufpdRule) + ); } if (purpose7Rule) { RULE_HANDLES.push(registerActivityControl(ACTIVITY_REPORT_ANALYTICS, RULE_NAME, reportAnalyticsRule)); diff --git a/test/spec/modules/gdprEnforcement_spec.js b/test/spec/modules/gdprEnforcement_spec.js index f571abcccea7..bfe3f8435afc 100644 --- a/test/spec/modules/gdprEnforcement_spec.js +++ b/test/spec/modules/gdprEnforcement_spec.js @@ -11,7 +11,7 @@ import { reportAnalyticsRule, setEnforcementConfig, STRICT_STORAGE_ENFORCEMENT, - syncUserRule, transmitUfpdRule, + syncUserRule, ufpdRule, validateRules } from 'modules/gdprEnforcement.js'; import {config} from 'src/config.js'; @@ -479,7 +479,7 @@ describe('gdpr enforcement', function () { const consent = setupConsentData(); consent.vendorData.purpose.consents[4] = true; consent.vendorData.vendor.consents[123] = true; - expectAllow(true, transmitUfpdRule(activityParams(MODULE_TYPE_BIDDER, 'mockBidder'))); + expectAllow(true, ufpdRule(activityParams(MODULE_TYPE_BIDDER, 'mockBidder'))); }); it('should return deny when purpose 4 consent is withheld', () => { @@ -498,7 +498,7 @@ describe('gdpr enforcement', function () { const consent = setupConsentData(); consent.vendorData.purpose.consents[4] = true; consent.vendorData.vendor.consents[123] = false; - expectAllow(false, transmitUfpdRule(activityParams(MODULE_TYPE_BIDDER, 'mockBidder'))) + expectAllow(false, ufpdRule(activityParams(MODULE_TYPE_BIDDER, 'mockBidder'))) }); }); From df103dd124373fbaea7457d7e150652b6a08ff05 Mon Sep 17 00:00:00 2001 From: Saveliev Taras Date: Thu, 13 Jul 2023 21:03:38 +0200 Subject: [PATCH 41/92] Yandex Bid Adapter: (#10152) * added adServerCurrency support * fixed auction price values for nurl Co-authored-by: Taras Saveliev --- modules/yandexBidAdapter.js | 27 ++++++++++------------ test/spec/modules/yandexBidAdapter_spec.js | 24 ++++++++++++++++--- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index 2870229f1db0..2a306acdcf03 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -2,6 +2,7 @@ import { formatQS, deepAccess, triggerPixel, _each, _map } from '../src/utils.js import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js' import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { config } from '../src/config.js'; const BIDDER_CODE = 'yandex'; const BIDDER_URL = 'https://bs.yandex.ru/prebid'; @@ -76,9 +77,11 @@ export const spec = { timeout = bidderRequest.timeout; } + const adServerCurrency = config.getConfig('currency.adServerCurrency'); + return validBidRequests.map((bidRequest) => { const { params } = bidRequest; - const { targetRef, withCredentials = true } = params; + const { targetRef, withCredentials = true, cur } = params; const { pageId, impId } = extractPlacementIds(params); @@ -107,6 +110,11 @@ export const spec = { imp.bidfloorcur = bidfloor.currency; } + const currency = cur || adServerCurrency; + if (currency) { + queryParams['ssp-cur'] = currency; + } + const queryParamsString = formatQS(queryParams); return { method: 'POST', @@ -132,24 +140,13 @@ export const spec = { interpretResponse: interpretResponse, onBidWon: function (bid) { - let nurl = bid['nurl']; + const nurl = bid['nurl']; if (!nurl) { return; } - let cpm, currency; - if (bid.hasOwnProperty('originalCurrency') && bid.hasOwnProperty('originalCpm')) { - cpm = bid.originalCpm; - currency = bid.originalCurrency; - } else { - cpm = bid.cpm; - currency = bid.currency; - } - cpm = deepAccess(bid, 'adserverTargeting.hb_pb') || cpm; - - const pixel = replaceAuctionPrice(nurl, cpm, currency); - triggerPixel(pixel); + triggerPixel(nurl); } } @@ -316,7 +313,7 @@ function interpretResponse(serverResponse, { bidRequest }) { width: bidReceived.w, height: bidReceived.h, creativeId: bidReceived.adid, - nurl: bidReceived.nurl, + nurl: replaceAuctionPrice(bidReceived.nurl, price, currency), netRevenue: true, ttl: DEFAULT_TTL, diff --git a/test/spec/modules/yandexBidAdapter_spec.js b/test/spec/modules/yandexBidAdapter_spec.js index 12d413a9c93b..eef476e21d24 100644 --- a/test/spec/modules/yandexBidAdapter_spec.js +++ b/test/spec/modules/yandexBidAdapter_spec.js @@ -2,7 +2,7 @@ import { assert, expect } from 'chai'; import { spec, NATIVE_ASSETS } from 'modules/yandexBidAdapter.js'; import { parseUrl } from 'src/utils.js'; import { BANNER, NATIVE } from '../../../src/mediaTypes'; -import {OPENRTB} from '../../../modules/rtbhouseBidAdapter'; +import { config } from '../../../src/config'; describe('Yandex adapter', function () { describe('isBidRequestValid', function () { @@ -90,6 +90,22 @@ describe('Yandex adapter', function () { expect(data.site.ref).to.equal('https://ya.ru/'); }); + it('should send currency if defined', function () { + config.setConfig({ + currency: { + adServerCurrency: 'USD' + } + }); + + const bannerRequest = getBidRequest(); + const requests = spec.buildRequests([bannerRequest], bidderRequest); + const { url } = requests[0]; + const parsedRequestUrl = parseUrl(url); + const { search: query } = parsedRequestUrl + + expect(query['ssp-cur']).to.equal('USD'); + }); + describe('banner', () => { it('should create valid banner object', () => { const bannerRequest = getBidRequest({ @@ -273,7 +289,7 @@ describe('Yandex adapter', function () { }, }); }); - }) + }); }); describe('interpretResponse', function () { @@ -294,6 +310,7 @@ describe('Yandex adapter', function () { 'example.com' ], adid: 'yabs.123=', + nurl: 'https://example.com/nurl/?price=${AUCTION_PRICE}&cur=${AUCTION_CURRENCY}', } ] }], @@ -314,11 +331,12 @@ describe('Yandex adapter', function () { const rtbBid = result[0]; expect(rtbBid.width).to.equal(300); expect(rtbBid.height).to.equal(250); - expect(rtbBid.cpm).to.be.within(0.1, 0.5); + expect(rtbBid.cpm).to.be.within(0.3, 0.3); expect(rtbBid.ad).to.equal(''); expect(rtbBid.currency).to.equal('USD'); expect(rtbBid.netRevenue).to.equal(true); expect(rtbBid.ttl).to.equal(180); + expect(rtbBid.nurl).to.equal('https://example.com/nurl/?price=0.3&cur=USD'); expect(rtbBid.meta.advertiserDomains).to.deep.equal(['example.com']); }); From 7149c36120f72684a130de97fc3b78edffc61fcf Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Fri, 14 Jul 2023 00:17:49 +0000 Subject: [PATCH 42/92] Prebid 8.4.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 33bef1f1cb54..2857723f7c93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.4.0-pre", + "version": "8.4.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 0c1cbd4850fb..f160d61b7761 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.4.0-pre", + "version": "8.4.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f4664a19ba0f0fa6aa45e89a46fb41762d1054ea Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Fri, 14 Jul 2023 00:17:49 +0000 Subject: [PATCH 43/92] Increment version to 8.5.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2857723f7c93..aa1294553ed9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.4.0", + "version": "8.5.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index f160d61b7761..f72547206f1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.4.0", + "version": "8.5.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 438976897ed30fca08d2959b4a7ecac9761defb7 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Fri, 14 Jul 2023 05:42:39 -0700 Subject: [PATCH 44/92] userID: break out EID configuration to individual submodules (#10229) * move eids config to submodules * update EID logic * Fix tests --- modules/33acrossIdSystem.js | 9 + modules/admixerIdSystem.js | 6 + modules/adqueryIdSystem.js | 6 + modules/adtelligentIdSystem.js | 6 + modules/amxIdSystem.js | 6 + modules/britepoolIdSystem.js | 6 + modules/connectIdSystem.js | 6 + modules/criteoIdSystem.js | 6 + modules/czechAdIdSystem.js | 6 + modules/dacIdSystem.js | 6 + modules/deepintentDpesIdSystem.js | 9 +- modules/dmdIdSystem.js | 6 + modules/euidIdSystem.js | 9 + modules/fabrickIdSystem.js | 6 + modules/ftrackIdSystem.js | 16 + modules/gravitoIdSystem.js | 6 + modules/growthCodeIdSystem.js | 21 +- modules/hadronIdSystem.js | 6 + modules/id5IdSystem.js | 16 +- modules/identityLinkIdSystem.js | 6 + modules/idxIdSystem.js | 6 + modules/imuIdSystem.js | 10 + modules/intentIqIdSystem.js | 6 + modules/justIdSystem.js | 6 + modules/kinessoIdSystem.js | 9 +- modules/liveIntentIdSystem.js | 53 ++ modules/lotamePanoramaIdSystem.js | 6 + modules/merkleIdSystem.js | 24 + modules/mwOpenLinkIdSystem.js | 6 + modules/naveggIdSystem.js | 6 + modules/netIdSystem.js | 6 + modules/novatiqIdSystem.js | 12 + modules/oneKeyIdSystem.js | 26 + modules/pairIdSystem.js | 6 + modules/parrableIdSystem.js | 40 +- modules/publinkIdSystem.js | 8 +- modules/quantcastIdSystem.js | 6 + modules/sharedIdSystem.js | 6 + modules/tapadIdSystem.js | 6 + modules/tncIdSystem.js | 6 + modules/uid2IdSystem.js | 14 + modules/unifiedIdSystem.js | 11 + modules/userId/eids.js | 451 +---------- modules/userId/index.js | 60 +- modules/utiqSystem.js | 9 + modules/verizonMediaIdSystem.js | 6 + modules/zeotapIdPlusIdSystem.js | 6 + .../modules/freewheel-sspBidAdapter_spec.js | 10 +- test/spec/modules/ixBidAdapter_spec.js | 5 +- test/spec/modules/outbrainBidAdapter_spec.js | 8 +- .../modules/prebidServerBidAdapter_spec.js | 22 +- test/spec/modules/pubmaticBidAdapter_spec.js | 379 +-------- test/spec/modules/rubiconBidAdapter_spec.js | 200 ++++- test/spec/modules/smaatoBidAdapter_spec.js | 10 +- test/spec/modules/userId_spec.js | 123 ++- test/spec/modules/vrtcalBidAdapter_spec.js | 9 +- test/spec/modules/xeBidAdapter_spec.js | 734 +++++++++--------- test/spec/modules/yahoosspBidAdapter_spec.js | 23 +- 58 files changed, 1203 insertions(+), 1295 deletions(-) diff --git a/modules/33acrossIdSystem.js b/modules/33acrossIdSystem.js index 14cd8586d50a..0f370237e213 100644 --- a/modules/33acrossIdSystem.js +++ b/modules/33acrossIdSystem.js @@ -126,6 +126,15 @@ export const thirthyThreeAcrossIdSubmodule = { }, calculateQueryStringParams(pid, gdprConsentData), { method: 'GET', withCredentials: true }); } }; + }, + eids: { + '33acrossId': { + source: '33across.com', + atype: 1, + getValue: function(data) { + return data.envelope; + } + }, } }; diff --git a/modules/admixerIdSystem.js b/modules/admixerIdSystem.js index 7fbebecfc127..0e3a56420a80 100644 --- a/modules/admixerIdSystem.js +++ b/modules/admixerIdSystem.js @@ -72,6 +72,12 @@ export const admixerIdSubmodule = { }; return { callback: resp }; + }, + eids: { + 'admixerId': { + source: 'admixer.net', + atype: 3 + }, } }; function retrieveVisitorId(url, callback) { diff --git a/modules/adqueryIdSystem.js b/modules/adqueryIdSystem.js index 5171802caba3..82df787a2b40 100644 --- a/modules/adqueryIdSystem.js +++ b/modules/adqueryIdSystem.js @@ -98,6 +98,12 @@ export const adqueryIdSubmodule = { } }; return {callback: resp}; + }, + eids: { + 'qid': { + source: 'adquery.io', + atype: 1 + }, } }; diff --git a/modules/adtelligentIdSystem.js b/modules/adtelligentIdSystem.js index fb3b5f6fe2a3..440ed9ade752 100644 --- a/modules/adtelligentIdSystem.js +++ b/modules/adtelligentIdSystem.js @@ -85,6 +85,12 @@ export const adtelligentIdModule = { } } + }, + eids: { + 'adtelligentId': { + source: 'adtelligent.com', + atype: 3 + }, } }; diff --git a/modules/amxIdSystem.js b/modules/amxIdSystem.js index 5deffa00dfe4..184eff76c34c 100644 --- a/modules/amxIdSystem.js +++ b/modules/amxIdSystem.js @@ -152,6 +152,12 @@ export const amxIdSubmodule = { return { callback }; }, + eids: { + amxId: { + source: 'amxdt.net', + atype: 1, + }, + } }; submodule('userId', amxIdSubmodule); diff --git a/modules/britepoolIdSystem.js b/modules/britepoolIdSystem.js index 2316fbb732de..b75fe9424b16 100644 --- a/modules/britepoolIdSystem.js +++ b/modules/britepoolIdSystem.js @@ -136,6 +136,12 @@ export const britepoolIdSubmodule = { } } return valueObj; + }, + eids: { + 'britepoolid': { + source: 'britepool.com', + atype: 3 + }, } }; diff --git a/modules/connectIdSystem.js b/modules/connectIdSystem.js index 1caff49199d6..e1c5b4272643 100644 --- a/modules/connectIdSystem.js +++ b/modules/connectIdSystem.js @@ -294,6 +294,12 @@ export const connectIdSubmodule = { */ getAjaxFn() { return ajax; + }, + eids: { + 'connectId': { + source: 'yahoo.com', + atype: 3 + }, } }; diff --git a/modules/criteoIdSystem.js b/modules/criteoIdSystem.js index b48612203f81..ee343d9b16a1 100644 --- a/modules/criteoIdSystem.js +++ b/modules/criteoIdSystem.js @@ -216,6 +216,12 @@ export const criteoIdSubmodule = { id: localData.bidId ? { criteoId: localData.bidId } : undefined, callback: result } + }, + eids: { + 'criteoId': { + source: 'criteo.com', + atype: 1 + }, } }; diff --git a/modules/czechAdIdSystem.js b/modules/czechAdIdSystem.js index 957b3ed30bd8..ae958aae198b 100644 --- a/modules/czechAdIdSystem.js +++ b/modules/czechAdIdSystem.js @@ -42,6 +42,12 @@ export const czechAdIdSubmodule = { getId () { const id = readId() return id ? { id: id } : undefined + }, + eids: { + 'czechAdId': { + source: 'czechadid.cz', + atype: 1 + }, } } diff --git a/modules/dacIdSystem.js b/modules/dacIdSystem.js index 5adca074c87f..ffdadef18e81 100644 --- a/modules/dacIdSystem.js +++ b/modules/dacIdSystem.js @@ -170,6 +170,12 @@ export const dacIdSystemSubmodule = { } return fetchAoneId(configParams.oid, cookie.fuuid); + }, + eids: { + 'dacId': { + source: 'impact-ad.jp', + atype: 1 + }, } }; diff --git a/modules/deepintentDpesIdSystem.js b/modules/deepintentDpesIdSystem.js index f2b50d535eb1..4d685592c04c 100644 --- a/modules/deepintentDpesIdSystem.js +++ b/modules/deepintentDpesIdSystem.js @@ -39,8 +39,13 @@ export const deepintentDpesSubmodule = { */ getId(config, consentData, cacheIdObj) { return cacheIdObj; - } - + }, + eids: { + 'deepintentId': { + source: 'deepintent.com', + atype: 3 + }, + }, }; submodule('userId', deepintentDpesSubmodule); diff --git a/modules/dmdIdSystem.js b/modules/dmdIdSystem.js index b42315d66eeb..2f910a8bd927 100644 --- a/modules/dmdIdSystem.js +++ b/modules/dmdIdSystem.js @@ -85,6 +85,12 @@ export const dmdIdSubmodule = { }; return { callback: resp }; } + }, + eids: { + 'dmdId': { + source: 'hcn.health', + atype: 3 + }, } }; diff --git a/modules/euidIdSystem.js b/modules/euidIdSystem.js index c4e15b11d156..6a3a0869c0e6 100644 --- a/modules/euidIdSystem.js +++ b/modules/euidIdSystem.js @@ -103,6 +103,15 @@ export const euidIdSubmodule = { _logInfo(`EUID getId returned`, result); return result; }, + eids: { + 'euid': { + source: 'euid.eu', + atype: 3, + getValue: function(data) { + return data.id; + } + }, + }, }; function decodeImpl(value) { diff --git a/modules/fabrickIdSystem.js b/modules/fabrickIdSystem.js index dfc2985b0db2..bc9c30cb4791 100644 --- a/modules/fabrickIdSystem.js +++ b/modules/fabrickIdSystem.js @@ -117,6 +117,12 @@ export const fabrickIdSubmodule = { } catch (e) { logError(`fabrickIdSystem encountered an error`, e); } + }, + eids: { + 'fabrickId': { + source: 'neustar.biz', + atype: 1 + }, } }; diff --git a/modules/ftrackIdSystem.js b/modules/ftrackIdSystem.js index 5f09a315b340..809f1311c42d 100644 --- a/modules/ftrackIdSystem.js +++ b/modules/ftrackIdSystem.js @@ -221,6 +221,22 @@ export const ftrackIdSubmodule = { if (usPrivacyVersion == 1 && usPrivacyOptOutSale === 'Y') consentValue = false; return consentValue; + }, + eids: { + 'ftrackId': { + source: 'flashtalking.com', + atype: 1, + getValue: function(data) { + let value = ''; + if (data && data.ext && data.ext.DeviceID) { + value = data.ext.DeviceID; + } + return value; + }, + getUidExt: function(data) { + return data && data.ext; + } + }, } }; diff --git a/modules/gravitoIdSystem.js b/modules/gravitoIdSystem.js index aa25ea7db2cd..70031ebd06e2 100644 --- a/modules/gravitoIdSystem.js +++ b/modules/gravitoIdSystem.js @@ -53,6 +53,12 @@ export const gravitoIdSystemSubmodule = { } return undefined; }, + eids: { + 'gravitompId': { + source: 'gravito.net', + atype: 1 + }, + } } submodule('userId', gravitoIdSystemSubmodule); diff --git a/modules/growthCodeIdSystem.js b/modules/growthCodeIdSystem.js index fae022f1a56c..aec49c64fa3d 100644 --- a/modules/growthCodeIdSystem.js +++ b/modules/growthCodeIdSystem.js @@ -5,7 +5,7 @@ * @requires module:modules/userId */ -import {logError, logInfo, tryAppendQueryString} from '../src/utils.js'; +import {logError, logInfo, pick, tryAppendQueryString} from '../src/utils.js'; import {ajax} from '../src/ajax.js'; import { submodule } from '../src/hook.js' import {getStorageManager} from '../src/storageManager.js'; @@ -166,6 +166,25 @@ export const growthCodeIdSubmodule = { } }; return { callback: resp }; + }, + eids: { + 'growthCodeId': { + getValue: function(data) { + return data.gc_id + }, + source: 'growthcode.io', + atype: 1, + getUidExt: function(data) { + const extendedData = pick(data, [ + 'h1', + 'h2', + 'h3', + ]); + if (Object.keys(extendedData).length) { + return extendedData; + } + } + }, } }; diff --git a/modules/hadronIdSystem.js b/modules/hadronIdSystem.js index 596bf9611e6f..c60f0f812a44 100644 --- a/modules/hadronIdSystem.js +++ b/modules/hadronIdSystem.js @@ -110,6 +110,12 @@ export const hadronIdSubmodule = { ajax(url, callbacks, undefined, {method: 'GET'}); }; return {callback: resp}; + }, + eids: { + 'hadronId': { + source: 'audigent.com', + atype: 1 + }, } }; diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index bb8a21264d0a..eff1f1366494 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -154,7 +154,21 @@ export const id5IdSubmodule = { logInfo(LOG_PREFIX + 'using cached ID', cacheIdObj); return cacheIdObj; - } + }, + eids: { + 'id5id': { + getValue: function(data) { + return data.uid + }, + source: 'id5-sync.com', + atype: 1, + getUidExt: function(data) { + if (data.ext) { + return data.ext; + } + } + }, + }, }; class IdFetchFlow { diff --git a/modules/identityLinkIdSystem.js b/modules/identityLinkIdSystem.js index ab10288f38fd..e8cef34f41e5 100644 --- a/modules/identityLinkIdSystem.js +++ b/modules/identityLinkIdSystem.js @@ -79,6 +79,12 @@ export const identityLinkSubmodule = { }; return { callback: resp }; + }, + eids: { + 'idl_env': { + source: 'liveramp.com', + atype: 3 + }, } }; // return envelope from third party endpoint diff --git a/modules/idxIdSystem.js b/modules/idxIdSystem.js index 3c00bbde34c4..bf807f199a6d 100644 --- a/modules/idxIdSystem.js +++ b/modules/idxIdSystem.js @@ -57,6 +57,12 @@ export const idxIdSubmodule = { } } return undefined; + }, + eids: { + 'idx': { + source: 'idx.lat', + atype: 1 + }, } }; submodule('userId', idxIdSubmodule); diff --git a/modules/imuIdSystem.js b/modules/imuIdSystem.js index 898f32b27b0a..38870c9403b7 100644 --- a/modules/imuIdSystem.js +++ b/modules/imuIdSystem.js @@ -160,6 +160,16 @@ export const imuIdSubmodule = { imppid: localData.ppid } }; + }, + eids: { + 'imppid': { + source: 'ppid.intimatemerger.com', + atype: 1 + }, + 'imuid': { + source: 'intimatemerger.com', + atype: 1 + }, } }; diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 364543948415..5164080c3178 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -193,6 +193,12 @@ export const intentIqIdSubmodule = { } }; return { callback: resp }; + }, + eids: { + 'intentIqId': { + source: 'intentiq.com', + atype: 1 + }, } }; diff --git a/modules/justIdSystem.js b/modules/justIdSystem.js index 15b1c90da4e8..26e9275bbaba 100644 --- a/modules/justIdSystem.js +++ b/modules/justIdSystem.js @@ -91,6 +91,12 @@ export const justIdSubmodule = { } } }; + }, + eids: { + 'justId': { + source: 'justtag.com', + atype: 1 + }, } }; diff --git a/modules/kinessoIdSystem.js b/modules/kinessoIdSystem.js index 632f3a669aa9..c13ed3976d3e 100644 --- a/modules/kinessoIdSystem.js +++ b/modules/kinessoIdSystem.js @@ -233,8 +233,13 @@ export const kinessoIdSubmodule = { const payloadString = JSON.stringify(kinessoIdPayload); ajax(kinessoSyncUrl(accountId, consentData), syncId(knnsoId), payloadString, {method: 'POST', withCredentials: true}); return {'id': knnsoId}; - } - + }, + eids: { + 'kpuid': { + source: 'kpuid.com', + atype: 3 + }, + }, }; // Register submodule for userId diff --git a/modules/liveIntentIdSystem.js b/modules/liveIntentIdSystem.js index 1c93427e3a91..33a702aa81f2 100644 --- a/modules/liveIntentIdSystem.js +++ b/modules/liveIntentIdSystem.js @@ -242,6 +242,59 @@ export const liveIntentIdSubmodule = { } return { callback: result }; + }, + eids: { + 'lipb': { + getValue: function(data) { + return data.lipbid; + }, + source: 'liveintent.com', + atype: 3, + getEidExt: function(data) { + if (Array.isArray(data.segments) && data.segments.length) { + return { + segments: data.segments + }; + } + } + }, + 'bidswitch': { + source: 'bidswitch.net', + atype: 3, + getValue: function(data) { + return data.id; + }, + getUidExt: function(data) { + if (data.ext) { + return data.ext; + } + } + }, + 'medianet': { + source: 'media.net', + atype: 3, + getValue: function(data) { + return data.id; + }, + getUidExt: function(data) { + if (data.ext) { + return data.ext; + } + } + }, + 'magnite': { + source: 'rubiconproject.com', + atype: 3, + getValue: function(data) { + return data.id; + }, + getUidExt: function(data) { + if (data.ext) { + return data.ext; + } + } + }, + } }; diff --git a/modules/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js index 02b01b8bd9db..808a67492b01 100644 --- a/modules/lotamePanoramaIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -368,6 +368,12 @@ export const lotamePanoramaIdSubmodule = { return { callback: resolveIdFunction }; }, + eids: { + lotamePanoramaId: { + source: 'crwdcntrl.net', + atype: 1, + }, + }, }; submodule('userId', lotamePanoramaIdSubmodule); diff --git a/modules/merkleIdSystem.js b/modules/merkleIdSystem.js index c522d588970a..fc77c7cc97d5 100644 --- a/modules/merkleIdSystem.js +++ b/modules/merkleIdSystem.js @@ -196,6 +196,30 @@ export const merkleIdSubmodule = { logInfo('User ID - merkleId not refreshed'); return {id: storedId}; + }, + eids: { + 'merkleId': { + atype: 3, + getSource: function(data) { + if (data?.ext?.ssp) { + return `${data.ext.ssp}.merkleinc.com` + } + return 'merkleinc.com' + }, + getValue: function(data) { + return data.id; + }, + getUidExt: function(data) { + if (data.keyID) { + return { + keyID: data.keyID + } + } + if (data.ext) { + return data.ext; + } + } + }, } }; diff --git a/modules/mwOpenLinkIdSystem.js b/modules/mwOpenLinkIdSystem.js index 9b1035cbf187..ff23547224b4 100644 --- a/modules/mwOpenLinkIdSystem.js +++ b/modules/mwOpenLinkIdSystem.js @@ -137,6 +137,12 @@ export const mwOpenLinkIdSubModule = { const submoduleConfigParams = (submoduleConfig && submoduleConfig.params) || {}; if (!isValidConfig(submoduleConfigParams)) return undefined; return setID(submoduleConfigParams); + }, + eids: { + 'mwOpenLinkId': { + source: 'mediawallahscript.com', + atype: 1 + }, } }; diff --git a/modules/naveggIdSystem.js b/modules/naveggIdSystem.js index 878ae7fadb21..8a472259873b 100644 --- a/modules/naveggIdSystem.js +++ b/modules/naveggIdSystem.js @@ -107,6 +107,12 @@ export const naveggIdSubmodule = { } } return undefined; + }, + eids: { + 'naveggId': { + source: 'navegg.com', + atype: 1 + }, } }; submodule('userId', naveggIdSubmodule); diff --git a/modules/netIdSystem.js b/modules/netIdSystem.js index 90c8735c9939..6f1ffe8b0e77 100644 --- a/modules/netIdSystem.js +++ b/modules/netIdSystem.js @@ -34,6 +34,12 @@ export const netIdSubmodule = { getId(config) { /* currently not possible */ return {}; + }, + eids: { + 'netId': { + source: 'netid.de', + atype: 1 + }, } }; diff --git a/modules/novatiqIdSystem.js b/modules/novatiqIdSystem.js index 7a801a945aed..7eced81d35e0 100644 --- a/modules/novatiqIdSystem.js +++ b/modules/novatiqIdSystem.js @@ -259,6 +259,18 @@ export const novatiqIdSubmodule = { srcId = configParams.sourceid; } return srcId; + }, + eids: { + 'novatiq': { + getValue: function(data) { + if (data.snowflake.id === undefined) { + return data.snowflake; + } + + return data.snowflake.id; + }, + source: 'novatiq.com', + }, } }; submodule('userId', novatiqIdSubmodule); diff --git a/modules/oneKeyIdSystem.js b/modules/oneKeyIdSystem.js index 8d8d594b533e..699a7a6ab957 100644 --- a/modules/oneKeyIdSystem.js +++ b/modules/oneKeyIdSystem.js @@ -72,6 +72,32 @@ export const oneKeyIdSubmodule = { return { callback: getIdsAndPreferences }; + }, + eids: { + 'oneKeyData': { + getValue: function(data) { + if (data && Array.isArray(data.identifiers) && data.identifiers[0]) { + return data.identifiers[0].value; + } + }, + source: 'paf', + atype: 1, + getEidExt: function(data) { + if (data && data.preferences) { + return {preferences: data.preferences}; + } + }, + getUidExt: function(data) { + if (data && Array.isArray(data.identifiers) && data.identifiers[0]) { + const id = data.identifiers[0]; + return { + version: id.version, + type: id.type, + source: id.source + }; + } + } + } } }; diff --git a/modules/pairIdSystem.js b/modules/pairIdSystem.js index f90e25fdd0f6..489b97d02e39 100644 --- a/modules/pairIdSystem.js +++ b/modules/pairIdSystem.js @@ -78,6 +78,12 @@ export const pairIdSubmodule = { return undefined; } return {'id': ids}; + }, + eids: { + 'pairId': { + source: 'google.com', + atype: 571187 + }, } }; diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 01ffec3c2494..3e3488f72f3e 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -7,7 +7,17 @@ // ci trigger: 1 -import {contains, deepClone, inIframe, isEmpty, isPlainObject, logError, logWarn, timestamp} from '../src/utils.js'; +import { + contains, + deepClone, + inIframe, + isEmpty, + isPlainObject, + logError, + logWarn, + pick, + timestamp +} from '../src/utils.js'; import {find} from '../src/polyfill.js'; import {ajax} from '../src/ajax.js'; import {submodule} from '../src/hook.js'; @@ -368,7 +378,33 @@ export const parrableIdSubmodule = { getId(config, gdprConsentData, currentStoredId) { const configParams = (config && config.params) || {}; return fetchId(configParams, gdprConsentData); - } + }, + eids: { + 'parrableId': { + source: 'parrable.com', + atype: 1, + getValue: function(parrableId) { + if (parrableId.eid) { + return parrableId.eid; + } + if (parrableId.ccpaOptout) { + // If the EID was suppressed due to a non consenting ccpa optout then + // we still wish to provide this as a reason to the adapters + return ''; + } + return null; + }, + getUidExt: function(parrableId) { + const extendedData = pick(parrableId, [ + 'ibaOptout', + 'ccpaOptout' + ]); + if (Object.keys(extendedData).length) { + return extendedData; + } + } + }, + }, }; submodule('userId', parrableIdSubmodule); diff --git a/modules/publinkIdSystem.js b/modules/publinkIdSystem.js index 6a5504b5ba0b..5b20dbb620a6 100644 --- a/modules/publinkIdSystem.js +++ b/modules/publinkIdSystem.js @@ -140,6 +140,12 @@ export const publinkIdSubmodule = { if (!storedId) { return {callback: makeCallback(config, consentData)}; } - } + }, + eids: { + 'publinkId': { + source: 'epsilon.com', + atype: 3 + }, + }, }; submodule('userId', publinkIdSubmodule); diff --git a/modules/quantcastIdSystem.js b/modules/quantcastIdSystem.js index 6a07082b61cf..2faf638fc0bb 100644 --- a/modules/quantcastIdSystem.js +++ b/modules/quantcastIdSystem.js @@ -214,6 +214,12 @@ export const quantcastIdSubmodule = { } return { id: fpa ? { quantcastId: fpa } : undefined }; + }, + eids: { + 'quantcastId': { + source: 'quantcast.com', + atype: 1 + }, } }; diff --git a/modules/sharedIdSystem.js b/modules/sharedIdSystem.js index 26f6f75b7a0e..9046d6a633d8 100644 --- a/modules/sharedIdSystem.js +++ b/modules/sharedIdSystem.js @@ -174,6 +174,12 @@ export const sharedIdSystemSubmodule = { }, domainOverride: domainOverrideToRootDomain(storage, 'sharedId'), + eids: { + 'pubcid': { + source: 'pubcid.org', + atype: 1 + }, + } }; submodule('userId', sharedIdSystemSubmodule); diff --git a/modules/tapadIdSystem.js b/modules/tapadIdSystem.js index 149ba22eb927..2c24f0627918 100644 --- a/modules/tapadIdSystem.js +++ b/modules/tapadIdSystem.js @@ -56,6 +56,12 @@ export const tapadIdSubmodule = { ); } } + }, + eids: { + 'tapadId': { + source: 'tapad.com', + atype: 1 + }, } } submodule('userId', tapadIdSubmodule); diff --git a/modules/tncIdSystem.js b/modules/tncIdSystem.js index 24e3c79d4df4..59254a9430fb 100644 --- a/modules/tncIdSystem.js +++ b/modules/tncIdSystem.js @@ -57,6 +57,12 @@ export const tncidSubModule = { return { callback: function (cb) { return tncCallback(cb); } } + }, + eids: { + 'tncid': { + source: 'thenewco.it', + atype: 3 + }, } } diff --git a/modules/uid2IdSystem.js b/modules/uid2IdSystem.js index f8178364e58e..a36b24e3ae30 100644 --- a/modules/uid2IdSystem.js +++ b/modules/uid2IdSystem.js @@ -83,6 +83,20 @@ export const uid2IdSubmodule = { _logInfo(`UID2 getId returned`, result); return result; }, + eids: { + 'uid2': { + source: 'uidapi.com', + atype: 3, + getValue: function(data) { + return data.id; + }, + getUidExt: function(data) { + if (data.ext) { + return data.ext; + } + } + }, + }, }; function decodeImpl(value) { diff --git a/modules/unifiedIdSystem.js b/modules/unifiedIdSystem.js index 8ec5fcd3f903..5576c40d960b 100644 --- a/modules/unifiedIdSystem.js +++ b/modules/unifiedIdSystem.js @@ -67,6 +67,17 @@ export const unifiedIdSubmodule = { ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true}); }; return {callback: resp}; + }, + eids: { + 'tdid': { + source: 'adserver.org', + atype: 1, + getUidExt: function() { + return { + rtiPartner: 'TDID' + }; + } + }, } }; diff --git a/modules/userId/eids.js b/modules/userId/eids.js index cf2a0408246e..dc362b411360 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -1,450 +1,10 @@ -import { pick, isFn, isStr, isPlainObject, deepAccess } from '../../src/utils.js'; +import {deepAccess, isFn, isPlainObject, isStr} from '../../src/utils.js'; -// Each user-id sub-module is expected to mention respective config here -export const USER_IDS_CONFIG = { - - // key-name : {config} - - // GrowthCode - 'growthCodeId': { - getValue: function(data) { - return data.gc_id - }, - source: 'growthcode.io', - atype: 1, - getUidExt: function(data) { - const extendedData = pick(data, [ - 'h1', - 'h2', - 'h3', - ]); - if (Object.keys(extendedData).length) { - return extendedData; - } - } - }, - - // utiq - 'utiq': { - source: 'utiq.com', - atype: 1, - getValue: function (data) { - return data; - }, - }, - - // intentIqId - 'intentIqId': { - source: 'intentiq.com', - atype: 1 - }, - - // naveggId - 'naveggId': { - source: 'navegg.com', - atype: 1 - }, - - // pairId - 'pairId': { - source: 'google.com', - atype: 571187 - }, - - // justId - 'justId': { - source: 'justtag.com', - atype: 1 - }, - - // pubCommonId - 'pubcid': { - source: 'pubcid.org', - atype: 1 - }, - - // unifiedId - 'tdid': { - source: 'adserver.org', - atype: 1, - getUidExt: function() { - return { - rtiPartner: 'TDID' - }; - } - }, - - // id5Id - 'id5id': { - getValue: function(data) { - return data.uid - }, - source: 'id5-sync.com', - atype: 1, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - - // ftrack - 'ftrackId': { - source: 'flashtalking.com', - atype: 1, - getValue: function(data) { - let value = ''; - if (data && data.ext && data.ext.DeviceID) { - value = data.ext.DeviceID; - } - return value; - }, - getUidExt: function(data) { - return data && data.ext; - } - }, - - // parrableId - 'parrableId': { - source: 'parrable.com', - atype: 1, - getValue: function(parrableId) { - if (parrableId.eid) { - return parrableId.eid; - } - if (parrableId.ccpaOptout) { - // If the EID was suppressed due to a non consenting ccpa optout then - // we still wish to provide this as a reason to the adapters - return ''; - } - return null; - }, - getUidExt: function(parrableId) { - const extendedData = pick(parrableId, [ - 'ibaOptout', - 'ccpaOptout' - ]); - if (Object.keys(extendedData).length) { - return extendedData; - } - } - }, - - // identityLink - 'idl_env': { - source: 'liveramp.com', - atype: 3 - }, - - // liveIntentId - 'lipb': { - getValue: function(data) { - return data.lipbid; - }, - source: 'liveintent.com', - atype: 3, - getEidExt: function(data) { - if (Array.isArray(data.segments) && data.segments.length) { - return { - segments: data.segments - }; - } - } - }, - - // bidswitchId - 'bidswitch': { - source: 'bidswitch.net', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - - // medianetId - 'medianet': { - source: 'media.net', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - - // magnite - 'magnite': { - source: 'rubiconproject.com', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - - // britepoolId - 'britepoolid': { - source: 'britepool.com', - atype: 3 - }, - - // dmdId - 'dmdId': { - source: 'hcn.health', - atype: 3 - }, - - // lotamePanoramaId - lotamePanoramaId: { - source: 'crwdcntrl.net', - atype: 1, - }, - - // criteo - 'criteoId': { - source: 'criteo.com', - atype: 1 - }, - - // merkleId - 'merkleId': { - atype: 3, - getSource: function(data) { - if (data?.ext?.ssp) { - return `${data.ext.ssp}.merkleinc.com` - } - return 'merkleinc.com' - }, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.keyID) { - return { - keyID: data.keyID - } - } - if (data.ext) { - return data.ext; - } - } - }, - - // NetId - 'netId': { - source: 'netid.de', - atype: 1 - }, - - // zeotapIdPlus - 'IDP': { - source: 'zeotap.com', - atype: 1 - }, - - // hadronId - 'hadronId': { - source: 'audigent.com', - atype: 1 - }, - - // quantcastId - 'quantcastId': { - source: 'quantcast.com', - atype: 1 - }, - - // IDx - 'idx': { - source: 'idx.lat', - atype: 1 - }, - - // Verizon Media ConnectID - 'connectid': { - source: 'verizonmedia.com', - atype: 3 - }, - - // Neustar Fabrick - 'fabrickId': { - source: 'neustar.biz', - atype: 1 - }, - - // MediaWallah OpenLink - 'mwOpenLinkId': { - source: 'mediawallahscript.com', - atype: 1 - }, - - 'tapadId': { - source: 'tapad.com', - atype: 1 - }, - - // Novatiq Snowflake - 'novatiq': { - getValue: function(data) { - if (data.snowflake.id === undefined) { - return data.snowflake; - } - - return data.snowflake.id; - }, - source: 'novatiq.com', - }, - - 'uid2': { - source: 'uidapi.com', - atype: 3, - getValue: function(data) { - return data.id; - }, - getUidExt: function(data) { - if (data.ext) { - return data.ext; - } - } - }, - - 'euid': { - source: 'euid.eu', - atype: 3, - getValue: function(data) { - return data.id; - } - }, - - 'deepintentId': { - source: 'deepintent.com', - atype: 3 - }, - - // Admixer Id - 'admixerId': { - source: 'admixer.net', - atype: 3 - }, - - // Adtelligent Id - 'adtelligentId': { - source: 'adtelligent.com', - atype: 3 - }, - - amxId: { - source: 'amxdt.net', - atype: 1, - }, - - 'publinkId': { - source: 'epsilon.com', - atype: 3 - }, - - 'kpuid': { - source: 'kpuid.com', - atype: 3 - }, - - 'imppid': { - source: 'ppid.intimatemerger.com', - atype: 1 - }, - - 'imuid': { - source: 'intimatemerger.com', - atype: 1 - }, - - // Yahoo ConnectID - 'connectId': { - source: 'yahoo.com', - atype: 3 - }, - - // Adquery ID - 'qid': { - source: 'adquery.io', - atype: 1 - }, - - // DAC ID - 'dacId': { - source: 'impact-ad.jp', - atype: 1 - }, - - // 33across ID - '33acrossId': { - source: '33across.com', - atype: 1, - getValue: function(data) { - return data.envelope; - } - }, - - // tncId - 'tncid': { - source: 'thenewco.it', - atype: 3 - }, - - // Gravito MP ID - 'gravitompId': { - source: 'gravito.net', - atype: 1 - }, - - // czechAdId - 'czechAdId': { - source: 'czechadid.cz', - atype: 1 - }, - - // OneKey Data - 'oneKeyData': { - getValue: function(data) { - if (data && Array.isArray(data.identifiers) && data.identifiers[0]) { - return data.identifiers[0].value; - } - }, - source: 'paf', - atype: 1, - getEidExt: function(data) { - if (data && data.preferences) { - return {preferences: data.preferences}; - } - }, - getUidExt: function(data) { - if (data && Array.isArray(data.identifiers) && data.identifiers[0]) { - const id = data.identifiers[0]; - return { - version: id.version, - type: id.type, - source: id.source - }; - } - } - } -}; +export const EID_CONFIG = new Map(); // this function will create an eid object for the given UserId sub-module function createEidObject(userIdData, subModuleKey) { - const conf = USER_IDS_CONFIG[subModuleKey]; + const conf = EID_CONFIG.get(subModuleKey); if (conf && userIdData) { let eid = {}; eid.source = isFn(conf['getSource']) ? conf['getSource'](userIdData) : conf['source']; @@ -510,11 +70,12 @@ export function buildEidPermissions(submodules) { submodules.filter(i => isPlainObject(i.idObj) && Object.keys(i.idObj).length) .forEach(i => { Object.keys(i.idObj).forEach(key => { + const eidConf = EID_CONFIG.get(key) || {}; if (deepAccess(i, 'config.bidders') && Array.isArray(i.config.bidders) && - deepAccess(USER_IDS_CONFIG, key + '.source')) { + eidConf.source) { eidPermissions.push( { - source: USER_IDS_CONFIG[key].source, + source: eidConf.source, bidders: i.config.bidders } ); diff --git a/modules/userId/index.js b/modules/userId/index.js index 6d4ddf9aa9ec..784ad7a7db17 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -133,7 +133,7 @@ import {getGlobal} from '../../src/prebidGlobal.js'; import adapterManager, {gdprDataHandler, gppDataHandler} from '../../src/adapterManager.js'; import CONSTANTS from '../../src/constants.json'; import {module, ready as hooksReady} from '../../src/hook.js'; -import {buildEidPermissions, createEidsArray, USER_IDS_CONFIG} from './eids.js'; +import {buildEidPermissions, createEidsArray, EID_CONFIG} from './eids.js'; import { getCoreStorageManager, getStorageManager, @@ -232,6 +232,7 @@ function submoduleMetrics(moduleName) { /** @param {Submodule[]} submodules */ export function setSubmoduleRegistry(submodules) { submoduleRegistry = submodules; + updateEIDConfig(submodules); } function cookieSetter(submodule, storageMgr) { @@ -465,7 +466,7 @@ function getSubmoduleId(submodules, sourceName) { const prioritisedIds = getPrioritizedCombinedSubmoduleIds(submodules); const eligibleIdName = Object.keys(prioritisedIds).find(idName => { - const config = USER_IDS_CONFIG[idName]; + const config = EID_CONFIG.get(idName); return config?.source === sourceName || (isFn(config?.getSource) && config.getSource() === sourceName); }); @@ -487,32 +488,33 @@ function getCombinedSubmoduleIdsForBidder(submodules, bidder) { return getPrioritizedCombinedSubmoduleIds(eligibleSubmodules); } +function collectByPriority(submodules, getIds, getName) { + return Object.fromEntries(Object.entries(submodules.reduce((carry, submod) => { + const ids = getIds(submod); + ids && Object.keys(ids).forEach(key => { + const maybeCurrentIdPriority = idPriority[key]?.indexOf(getName(submod)); + const currentIdPriority = isNumber(maybeCurrentIdPriority) ? maybeCurrentIdPriority : -1; + const currentIdState = {priority: currentIdPriority, value: ids[key]}; + if (carry[key]) { + const winnerIdState = currentIdState.priority > carry[key].priority ? currentIdState : carry[key]; + carry[key] = winnerIdState; + } else { + carry[key] = currentIdState; + } + }); + return carry; + }, {})).map(([k, v]) => [k, v.value])); +} + /** * @param {SubmoduleContainer[]} submodules */ function getPrioritizedCombinedSubmoduleIds(submodules) { - const combinedIdStates = submodules - .filter(i => isPlainObject(i.idObj) && Object.keys(i.idObj).length) - .reduce((carry, i) => { - Object.keys(i.idObj).forEach(key => { - const maybeCurrentIdPriority = idPriority[key]?.indexOf(i.submodule.name); - const currentIdPriority = isNumber(maybeCurrentIdPriority) ? maybeCurrentIdPriority : -1; - const currentIdState = {priority: currentIdPriority, value: i.idObj[key]}; - if (carry[key]) { - const winnerIdState = currentIdState.priority > carry[key].priority ? currentIdState : carry[key]; - carry[key] = winnerIdState; - } else { - carry[key] = currentIdState; - } - }); - return carry; - }, {}); - - const result = {}; - Object.keys(combinedIdStates).forEach(key => { - result[key] = combinedIdStates[key].value - }); - return result; + return collectByPriority( + submodules.filter(i => isPlainObject(i.idObj) && Object.keys(i.idObj).length), + (submod) => submod.idObj, + (submod) => submod.submodule.name + ) } /** @@ -997,10 +999,20 @@ function canUseStorage(submodule) { return false; } +function updateEIDConfig(submodules) { + EID_CONFIG.clear(); + Object.entries(collectByPriority( + submodules, + (mod) => mod.eids, + (mod) => mod.name + )).forEach(([id, conf]) => EID_CONFIG.set(id, conf)); +} + /** * update submodules by validating against existing configs and storage types */ function updateSubmodules() { + updateEIDConfig(submoduleRegistry); const configs = getValidSubmoduleConfigs(configRegistry, submoduleRegistry); if (!configs.length) { return; diff --git a/modules/utiqSystem.js b/modules/utiqSystem.js index 441bba5660f0..473dc5854a9b 100644 --- a/modules/utiqSystem.js +++ b/modules/utiqSystem.js @@ -124,6 +124,15 @@ export const utiqSubmodule = { return { callback: result }; } }, + eids: { + 'utiq': { + source: 'utiq.com', + atype: 1, + getValue: function (data) { + return data; + }, + }, + } }; submodule('userId', utiqSubmodule); diff --git a/modules/verizonMediaIdSystem.js b/modules/verizonMediaIdSystem.js index 27577ad0de44..6b22538fdc78 100644 --- a/modules/verizonMediaIdSystem.js +++ b/modules/verizonMediaIdSystem.js @@ -99,6 +99,12 @@ export const verizonMediaIdSubmodule = { */ getAjaxFn() { return ajax; + }, + eids: { + 'connectid': { + source: 'verizonmedia.com', + atype: 3 + }, } }; diff --git a/modules/zeotapIdPlusIdSystem.js b/modules/zeotapIdPlusIdSystem.js index 4cb827cbdff2..ab7bf7c237b0 100644 --- a/modules/zeotapIdPlusIdSystem.js +++ b/modules/zeotapIdPlusIdSystem.js @@ -60,6 +60,12 @@ export const zeotapIdPlusSubmodule = { getId() { const id = readCookie() || readFromLocalStorage(); return id ? { id } : undefined; + }, + eids: { + 'IDP': { + source: 'zeotap.com', + atype: 1 + }, } }; submodule('userId', zeotapIdPlusSubmodule); diff --git a/test/spec/modules/freewheel-sspBidAdapter_spec.js b/test/spec/modules/freewheel-sspBidAdapter_spec.js index fe04a430ce6c..c42c5e2528d2 100644 --- a/test/spec/modules/freewheel-sspBidAdapter_spec.js +++ b/test/spec/modules/freewheel-sspBidAdapter_spec.js @@ -135,11 +135,11 @@ describe('freewheelSSP BidAdapter Test', () => { it('should pass 3rd party IDs with the request when present', function () { const bidRequest = bidRequests[0]; - bidRequest.userIdAsEids = createEidsArray({ - tdid: 'TTD_ID_FROM_USER_ID_MODULE', - admixerId: 'admixerId_FROM_USER_ID_MODULE', - adtelligentId: 'adtelligentId_FROM_USER_ID_MODULE' - }); + bidRequest.userIdAsEids = [ + {source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}, + {source: 'admixer.net', uids: [{id: 'admixerId_FROM_USER_ID_MODULE', atype: 3}]}, + {source: 'adtelligent.com', uids: [{id: 'adtelligentId_FROM_USER_ID_MODULE', atype: 3}]}, + ]; const request = spec.buildRequests(bidRequests); const payload = request[0].data; expect(payload._fw_prebid_3p_UID).to.deep.equal(JSON.stringify([ diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 36610db6aa62..3bfbf706d723 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -3,7 +3,6 @@ import { config } from 'src/config.js'; import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; import { spec, storage, ERROR_CODES, FEATURE_TOGGLES, LOCAL_STORAGE_FEATURE_TOGGLES_KEY, REQUESTED_FEATURE_TOGGLES, combineImps, bidToVideoImp, bidToNativeImp, deduplicateImpExtFields, removeSiteIDs } from '../../../modules/ixBidAdapter.js'; -import { createEidsArray } from 'modules/userId/eids.js'; import { deepAccess, deepClone } from '../../../src/utils.js'; describe('IndexexchangeAdapter', function () { @@ -763,8 +762,6 @@ describe('IndexexchangeAdapter', function () { '33acrossId': { envelope: 'v1.5fs.1000.fjdiosmclds' } }; - const DEFAULT_USERIDASEIDS_DATA = createEidsArray(DEFAULT_USERID_DATA); - const DEFAULT_USERID_PAYLOAD = [ { source: 'liveramp.com', @@ -824,6 +821,8 @@ describe('IndexexchangeAdapter', function () { } ]; + const DEFAULT_USERIDASEIDS_DATA = DEFAULT_USERID_PAYLOAD; + const DEFAULT_USERID_BID_DATA = { lotamePanoramaId: 'bd738d136bdaa841117fe9b331bb4' }; diff --git a/test/spec/modules/outbrainBidAdapter_spec.js b/test/spec/modules/outbrainBidAdapter_spec.js index ab72ff85ab0a..d8690aeb6a59 100644 --- a/test/spec/modules/outbrainBidAdapter_spec.js +++ b/test/spec/modules/outbrainBidAdapter_spec.js @@ -2,8 +2,6 @@ import { expect } from 'chai'; import { spec } from 'modules/outbrainBidAdapter.js'; import { config } from 'src/config.js'; import { server } from 'test/mocks/xhr'; -import { createEidsArray } from 'modules/userId/eids.js'; -import * as utils from 'src/utils.js'; describe('Outbrain Adapter', function () { describe('Bid request and response', function () { @@ -511,9 +509,9 @@ describe('Outbrain Adapter', function () { let bidRequest = { bidId: 'bidId', params: {}, - userIdAsEids: createEidsArray({ - idl_env: 'id-value', - }), + userIdAsEids: [ + { source: 'liveramp.com', uids: [{ id: 'id-value', atype: 3 }] } + ], ...commonBidRequest, }; diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 895c37548761..e4f06c8835fa 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -2082,30 +2082,12 @@ describe('S2S Adapter', function () { } } }; - userIdBidRequest[0].bids[0].userIdAsEids = createEidsArray(userIdBidRequest[0].bids[0].userId); + userIdBidRequest[0].bids[0].userIdAsEids = [{id: 1}, {id: 2}]; adapter.callBids(REQUEST, userIdBidRequest, addBidResponse, done, ajax); let requestBid = JSON.parse(server.requests[0].requestBody); expect(typeof requestBid.user.ext.eids).is.equal('object'); - expect(Array.isArray(requestBid.user.ext.eids)).to.be.true; - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'adserver.org')).is.not.empty; - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'adserver.org')[0].uids[0].id).is.equal('abc123'); - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'criteo.com')).is.not.empty; - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'criteo.com')[0].uids[0].id).is.equal('44VmRDeUE3ZGJ5MzRkRVJHU3BIUlJ6TlFPQUFU'); - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'pubcid.org')).is.not.empty; - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'pubcid.org')[0].uids[0].id).is.equal('1234'); - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'parrable.com')).is.not.empty; - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'parrable.com')[0].uids[0].id).is.equal('01.1563917337.test-eid'); - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')).is.not.empty; - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].uids[0].id).is.equal('li-xyz'); - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].ext.segments.length).is.equal(2); - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].ext.segments[0]).is.equal('segA'); - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveintent.com')[0].ext.segments[1]).is.equal('segB'); - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'id5-sync.com')).is.not.empty; - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'id5-sync.com')[0].uids[0].id).is.equal('11111'); - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'id5-sync.com')[0].uids[0].ext.linkType).is.equal('some-link-type'); - // LiveRamp should exist - expect(requestBid.user.ext.eids.filter(eid => eid.source === 'liveramp.com')[0].uids[0].id).is.equal('0000-1111-2222-3333'); + expect(requestBid.user.ext.eids).to.eql([{id: 1}, {id: 2}]); }); it('when config \'currency.adServerCurrency\' value is a string: ORTB has property \'cur\' value set to a single item array', function () { diff --git a/test/spec/modules/pubmaticBidAdapter_spec.js b/test/spec/modules/pubmaticBidAdapter_spec.js index 53f5e12aa6d7..1ec9c36fac35 100644 --- a/test/spec/modules/pubmaticBidAdapter_spec.js +++ b/test/spec/modules/pubmaticBidAdapter_spec.js @@ -2160,7 +2160,7 @@ describe('PubMatic adapter', function () { sandbox.restore(); }); - describe('AdsrvrOrgId from userId module', function() { + describe('userIdAsEids', function() { let sandbox; beforeEach(() => { sandbox = sinon.sandbox.create(); @@ -2170,13 +2170,10 @@ describe('PubMatic adapter', function () { sandbox.restore(); }); - it('Request should have AdsrvrOrgId config params', function() { + it('Request should have EIDs', function() { bidRequests[0].userId = {}; bidRequests[0].userId.tdid = 'TTD_ID_FROM_USER_ID_MODULE'; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal([{ + bidRequests[0].userIdAsEids = [{ 'source': 'adserver.org', 'uids': [{ 'id': 'TTD_ID_FROM_USER_ID_MODULE', @@ -2185,383 +2182,19 @@ describe('PubMatic adapter', function () { 'rtiPartner': 'TDID' } }] - }]); - }); - - it('Request should have adsrvrOrgId from UserId Module if config and userId module both have TTD ID', function() { - sandbox.stub(config, 'getConfig').callsFake((key) => { - var config = { - adsrvrOrgId: { - 'TDID': 'TTD_ID_FROM_CONFIG', - 'TDID_LOOKUP': 'TRUE', - 'TDID_CREATED_AT': '2018-10-01T07:05:40' - } - }; - return config[key]; - }); - bidRequests[0].userId = {}; - bidRequests[0].userId.tdid = 'TTD_ID_FROM_USER_ID_MODULE'; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal([{ - 'source': 'adserver.org', - 'uids': [{ - 'id': 'TTD_ID_FROM_USER_ID_MODULE', - 'atype': 1, - 'ext': { - 'rtiPartner': 'TDID' - } - }] - }]); - }); - - it('Request should NOT have adsrvrOrgId params if userId is NOT object', function() { + }]; let request = spec.buildRequests(bidRequests, {}); let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal(undefined); + expect(data.user.eids).to.deep.equal(bidRequests[0].userIdAsEids); }); - it('Request should NOT have adsrvrOrgId params if userId.tdid is NOT string', function() { - bidRequests[0].userId = { - tdid: 1234 - }; + it('Request should NOT have EIDs userIdAsEids is NOT object', function() { let request = spec.buildRequests(bidRequests, {}); let data = JSON.parse(request.data); expect(data.user.eids).to.deep.equal(undefined); }); }); - describe('UserIds from request', function() { - describe('pubcommon Id', function() { - it('send the pubcommon id if it is present', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.pubcid = 'pub_common_user_id'; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal([{ - 'source': 'pubcid.org', - 'uids': [{ - 'id': 'pub_common_user_id', - 'atype': 1 - }] - }]); - }); - - it('do not pass if not string', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.pubcid = 1; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.pubcid = []; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.pubcid = null; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.pubcid = {}; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - }); - }); - - describe('ID5 Id', function() { - it('send the id5 id if it is present', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.id5id = { uid: 'id5-user-id' }; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal([{ - 'source': 'id5-sync.com', - 'uids': [{ - 'id': 'id5-user-id', - 'atype': 1 - }] - }]); - }); - - it('do not pass if not string', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.id5id = { uid: 1 }; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.id5id = { uid: [] }; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.id5id = { uid: null }; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.id5id = { uid: {} }; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - }); - }); - - describe('Criteo Id', function() { - it('send the criteo id if it is present', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.criteoId = 'criteo-user-id'; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal([{ - 'source': 'criteo.com', - 'uids': [{ - 'id': 'criteo-user-id', - 'atype': 1 - }] - }]); - }); - - it('do not pass if not string', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.criteoId = 1; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.criteoId = []; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.criteoId = null; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.criteoId = {}; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - }); - }); - - describe('IdentityLink Id', function() { - it('send the identity-link id if it is present', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.idl_env = 'identity-link-user-id'; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal([{ - 'source': 'liveramp.com', - 'uids': [{ - 'id': 'identity-link-user-id', - 'atype': 3 - }] - }]); - }); - - it('do not pass if not string', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.idl_env = 1; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.idl_env = []; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.idl_env = null; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.idl_env = {}; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - }); - }); - - describe('LiveIntent Id', function() { - it('send the LiveIntent id if it is present', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.lipb = { lipbid: 'live-intent-user-id' }; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal([{ - 'source': 'liveintent.com', - 'uids': [{ - 'id': 'live-intent-user-id', - 'atype': 3 - }] - }]); - }); - - it('do not pass if not string', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.lipb = { lipbid: 1 }; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.lipb.lipbid = []; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.lipb.lipbid = null; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.lipb.lipbid = {}; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - }); - }); - - describe('Parrable Id', function() { - it('send the Parrable id if it is present', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.parrableId = { eid: 'parrable-user-id' }; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal([{ - 'source': 'parrable.com', - 'uids': [{ - 'id': 'parrable-user-id', - 'atype': 1 - }] - }]); - }); - - it('do not pass if not object with eid key', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.parrableid = 1; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.parrableid = []; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.parrableid = null; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.parrableid = {}; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - }); - }); - - describe('Britepool Id', function() { - it('send the Britepool id if it is present', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.britepoolid = 'britepool-user-id'; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal([{ - 'source': 'britepool.com', - 'uids': [{ - 'id': 'britepool-user-id', - 'atype': 3 - }] - }]); - }); - - it('do not pass if not string', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.britepoolid = 1; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.britepoolid = []; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.britepoolid = null; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.britepoolid = {}; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - }); - }); - - describe('NetId', function() { - it('send the NetId if it is present', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.netId = 'netid-user-id'; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.deep.equal([{ - 'source': 'netid.de', - 'uids': [{ - 'id': 'netid-user-id', - 'atype': 1 - }] - }]); - }); - - it('do not pass if not string', function() { - bidRequests[0].userId = {}; - bidRequests[0].userId.netId = 1; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - let request = spec.buildRequests(bidRequests, {}); - let data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.netId = []; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.netId = null; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - bidRequests[0].userId.netId = {}; - bidRequests[0].userIdAsEids = createEidsArray(bidRequests[0].userId); - request = spec.buildRequests(bidRequests, {}); - data = JSON.parse(request.data); - expect(data.user.eids).to.equal(undefined); - }); - }); - }); - it('should pass device.sua if present in bidderRequest fpd ortb2 object', function () { const suaObject = {'source': 2, 'platform': {'brand': 'macOS', 'version': ['12', '4', '0']}, 'browsers': [{'brand': 'Not_A Brand', 'version': ['99', '0', '0', '0']}, {'brand': 'Google Chrome', 'version': ['109', '0', '5414', '119']}, {'brand': 'Chromium', 'version': ['109', '0', '5414', '119']}], 'mobile': 0, 'model': '', 'bitness': '64', 'architecture': 'x86'}; let request = spec.buildRequests(multipleMediaRequests, { diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 1410e481d4e2..317f03752f16 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -270,7 +270,81 @@ describe('the rubicon adapter', function () { }], criteoId: '1111', }; - bid.userIdAsEids = createEidsArray(bid.userId); + bid.userIdAsEids = [ + { + 'source': 'liveintent.com', + 'uids': [ + { + 'id': '0000-1111-2222-3333', + 'atype': 3 + } + ], + 'ext': { + 'segments': [ + 'segA', + 'segB' + ] + } + }, + { + 'source': 'liveramp.com', + 'uids': [ + { + 'id': '1111-2222-3333-4444', + 'atype': 3 + } + ] + }, + { + 'source': 'adserver.org', + 'uids': [ + { + 'id': '3000', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + } + ] + }, + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '4000', + 'atype': 1 + } + ] + }, + { + 'source': 'example.com', + 'uids': [ + { + 'id': '333333', + 'ext': { + 'stype': 'ppuid' + } + } + ] + }, + { + 'source': 'id-partner.com', + 'uids': [ + { + 'id': '4444444' + } + ] + }, + { + 'source': 'criteo.com', + 'uids': [ + { + 'id': '1111', + 'atype': 1 + } + ] + } + ]; return bidderRequest; } @@ -1224,7 +1298,20 @@ describe('the rubicon adapter', function () { clonedBid.userId = { tdid: 'abcd-efgh-ijkl-mnop-1234' }; - clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + clonedBid.userIdAsEids = [ + { + 'source': 'adserver.org', + 'uids': [ + { + 'id': 'abcd-efgh-ijkl-mnop-1234', + 'atype': 1, + 'ext': { + 'rtiPartner': 'TDID' + } + } + ] + } + ]; let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1241,7 +1328,23 @@ describe('the rubicon adapter', function () { segments: ['segA', 'segB'] } }; - clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + clonedBid.userIdAsEids = [ + { + 'source': 'liveintent.com', + 'uids': [ + { + 'id': '0000-1111-2222-3333', + 'atype': 3 + } + ], + 'ext': { + 'segments': [ + 'segA', + 'segB' + ] + } + } + ]; let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1258,7 +1361,23 @@ describe('the rubicon adapter', function () { segments: ['segD', 'segE'] } }; - clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + clonedBid.userIdAsEids = [ + { + 'source': 'liveintent.com', + 'uids': [ + { + 'id': '1111-2222-3333-4444', + 'atype': 3 + } + ], + 'ext': { + 'segments': [ + 'segD', + 'segE' + ] + } + } + ] let [request] = spec.buildRequests([clonedBid], bidderRequest); const unescapedData = unescape(request.data); @@ -1273,7 +1392,17 @@ describe('the rubicon adapter', function () { clonedBid.userId = { idl_env: '1111-2222-3333-4444' }; - clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + clonedBid.userIdAsEids = [ + { + 'source': 'liveramp.com', + 'uids': [ + { + 'id': '1111-2222-3333-4444', + 'atype': 3 + } + ] + } + ] let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1287,7 +1416,17 @@ describe('the rubicon adapter', function () { clonedBid.userId = { pubcid: '1111' }; - clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + clonedBid.userIdAsEids = [ + { + 'source': 'pubcid.org', + 'uids': [ + { + 'id': '1111', + 'atype': 1 + } + ] + } + ] let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1301,7 +1440,17 @@ describe('the rubicon adapter', function () { clonedBid.userId = { criteoId: '1111' }; - clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + clonedBid.userIdAsEids = [ + { + 'source': 'criteo.com', + 'uids': [ + { + 'id': '1111', + 'atype': 1 + } + ] + } + ] let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1328,7 +1477,27 @@ describe('the rubicon adapter', function () { }] }] }; - clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + clonedBid.userIdAsEids = [ + { + 'source': 'example.com', + 'uids': [ + { + 'id': '11111', + 'ext': { + 'stype': 'ppuid' + } + } + ] + }, + { + 'source': 'id-partner.com', + 'uids': [ + { + 'id': '222222' + } + ] + } + ]; let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); @@ -1347,7 +1516,20 @@ describe('the rubicon adapter', function () { } } }; - clonedBid.userIdAsEids = createEidsArray(clonedBid.userId); + clonedBid.userIdAsEids = [ + { + 'source': 'id5-sync.com', + 'uids': [ + { + 'id': '11111', + 'atype': 1, + 'ext': { + 'linkType': '22222' + } + } + ] + } + ]; let [request] = spec.buildRequests([clonedBid], bidderRequest); let data = parseQuery(request.data); diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 61a877f329dd..185dee2430fb 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -1095,17 +1095,15 @@ describe('smaatoBidAdapterTest', () => { criteoId: '123456', tdid: '89145' }, - userIdAsEids: createEidsArray({ - criteoId: '123456', - tdid: '89145' - }) + userIdAsEids: [ + {id: 1}, {id: 2} + ] }; const reqs = spec.buildRequests([userIdBidRequest], defaultBidderRequest); const req = extractPayloadOfFirstAndOnlyRequest(reqs); - expect(req.user.ext.eids).to.exist; - expect(req.user.ext.eids).to.have.length(2); + expect(req.user.ext.eids).to.eql(userIdBidRequest.userIdAsEids); }); }); diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index b1908bdf0858..acc016a903dd 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -12,7 +12,7 @@ import { setSubmoduleRegistry, syncDelay, } from 'modules/userId/index.js'; -import {createEidsArray} from 'modules/userId/eids.js'; +import {createEidsArray, EID_CONFIG} from 'modules/userId/eids.js'; import {config} from 'src/config.js'; import * as utils from 'src/utils.js'; import {getPrebidInternal} from 'src/utils.js'; @@ -86,7 +86,7 @@ describe('User ID', function () { } } - function createMockIdSubmodule(name, value, aliasName) { + function createMockIdSubmodule(name, value, aliasName, eids) { return { name, getId() { @@ -95,7 +95,8 @@ describe('User ID', function () { decode(v) { return v; }, - aliasName + aliasName, + eids } } @@ -594,6 +595,50 @@ describe('User ID', function () { }); }); + describe('EID updateConfig', () => { + function mockSubmod(name, eids) { + return createMockIdSubmodule(name, null, null, eids); + } + + it('does not choke if a submod does not provide an eids map', () => { + setSubmoduleRegistry([ + mockSubmod('mock1'), + mockSubmod('mock2') + ]); + expect(EID_CONFIG.size).to.equal(0); + }); + + it('should merge together submodules\' eid configs', () => { + setSubmoduleRegistry([ + mockSubmod('mock1', {mock1: {m: 1}}), + mockSubmod('mock2', {mock2: {m: 2}}) + ]); + expect(EID_CONFIG.get('mock1')).to.eql({m: 1}); + expect(EID_CONFIG.get('mock2')).to.eql({m: 2}); + }); + + it('should respect idPriority', () => { + config.setConfig({ + userSync: { + idPriority: { + m1: ['mod2', 'mod1'], + m2: ['mod1', 'mod2'] + }, + userIds: [ + { name: 'mod1' }, + { name: 'mod2' }, + ] + } + }); + setSubmoduleRegistry([ + mockSubmod('mod1', {m1: {i: 1}, m2: {i: 2}}), + mockSubmod('mod2', {m1: {i: 3}, m2: {i: 4}}) + ]); + expect(EID_CONFIG.get('m1')).to.eql({i: 3}); + expect(EID_CONFIG.get('m2')).to.eql({i: 2}); + }); + }) + it('should set googletag ppid correctly', function () { let adUnits = [getAdUnitMock()]; init(config); @@ -626,7 +671,25 @@ describe('User ID', function () { // some of the ids are padded to have length >= 32 characters createMockIdSubmodule('mockId1Module', {id: {uid2: {id: 'uid2_value_7ac66c0f148de9519b8bd264312c4d64'}}}), createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value_7ac66c0f148de9519b8bd264312c4d64', lipb: {lipbid: 'lipbid_value_from_mockId2Module_7ac66c0f148de9519b8bd264312c4d64'}}}), - createMockIdSubmodule('mockId3Module', {id: {uid2: {id: 'uid2_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64'}, pubcid: 'pubcid_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64', lipb: {lipbid: 'lipbid_value_7ac66c0f148de9519b8bd264312c4d64'}, merkleId: {id: 'merkleId_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64'}}}), + createMockIdSubmodule('mockId3Module', { + id: { + uid2: { + id: 'uid2_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64' + }, + pubcid: 'pubcid_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64', + lipb: { + lipbid: 'lipbid_value_7ac66c0f148de9519b8bd264312c4d64' + }, + merkleId: { + id: 'merkleId_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64' + } + } + }, null, { + uid2: { + source: 'uidapi.com', + getValue(data) { return data.id } + } + }), createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value_7ac66c0f148de9519b8bd264312c4d64'}}}) ]); @@ -717,6 +780,11 @@ describe('User ID', function () { }, decode(d) { return d + }, + eids: { + pubcid: { + source: 'pubcid.org', + } } }]); config.setConfig({ @@ -808,7 +876,25 @@ describe('User ID', function () { // some of the ids are padded to have length >= 32 characters createMockIdSubmodule('mockId1Module', {id: {uid2: {id: 'uid2_value_7ac66c0f148de9519b8bd264312c4d64'}}}), createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value_7ac66c0f148de9519b8bd264312c4d64', lipb: {lipbid: 'lipbid_value_from_mockId2Module_7ac66c0f148de9519b8bd264312c4d64'}}}), - createMockIdSubmodule('mockId3Module', {id: {uid2: {id: 'uid2_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64'}, pubcid: 'pubcid_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64', lipb: {lipbid: 'lipbid_value_7ac66c0f148de9519b8bd264312c4d64'}, merkleId: {id: 'merkleId_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64'}}}), + createMockIdSubmodule('mockId3Module', { + id: { + uid2: { + id: 'uid2_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64' + }, + pubcid: 'pubcid_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64', + lipb: { + lipbid: 'lipbid_value_7ac66c0f148de9519b8bd264312c4d64' + }, + merkleId: { + id: 'merkleId_value_from_mockId3Module_7ac66c0f148de9519b8bd264312c4d64' + } + } + }, null, { + uid2: { + source: 'uidapi.com', + getValue(data) { return data.id } + } + }), createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value_7ac66c0f148de9519b8bd264312c4d64'}}}) ]); @@ -3259,8 +3345,31 @@ describe('User ID', function () { setSubmoduleRegistry([ createMockIdSubmodule('mockId1Module', {id: {uid2: {id: 'uid2_value'}}}), createMockIdSubmodule('mockId2Module', {id: {pubcid: 'pubcid_value', lipb: {lipbid: 'lipbid_value_from_mockId2Module'}}}), - createMockIdSubmodule('mockId3Module', {id: {uid2: {id: 'uid2_value_from_mockId3Module'}, pubcid: 'pubcid_value_from_mockId3Module', lipb: {lipbid: 'lipbid_value'}, merkleId: {id: 'merkleId_value_from_mockId3Module'}}}), - createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value'}}}) + createMockIdSubmodule('mockId3Module', {id: {uid2: {id: 'uid2_value_from_mockId3Module'}, pubcid: 'pubcid_value_from_mockId3Module', lipb: {lipbid: 'lipbid_value'}, merkleId: {id: 'merkleId_value_from_mockId3Module'}}}, null, { + uid2: { + source: 'uidapi.com', + getValue(data) { + return data.id + } + }, + pubcid: { + source: 'pubcid.org', + }, + lipb: { + source: 'liveintent.com', + getValue(data) { + return data.lipbid + } + } + }), + createMockIdSubmodule('mockId4Module', {id: {merkleId: {id: 'merkleId_value'}}}, null, { + merkleId: { + source: 'merkleinc.com', + getValue(data) { + return data.id + } + } + }) ]); config.setConfig({ userSync: { diff --git a/test/spec/modules/vrtcalBidAdapter_spec.js b/test/spec/modules/vrtcalBidAdapter_spec.js index 764db3fa4f0c..609d5d0a5f77 100644 --- a/test/spec/modules/vrtcalBidAdapter_spec.js +++ b/test/spec/modules/vrtcalBidAdapter_spec.js @@ -79,9 +79,12 @@ describe('vrtcalBidAdapter', function () { }); it('pass 3rd party IDs with the request when present', function () { - bidRequests[0].userIdAsEids = createEidsArray({ - tdid: 'TTD_ID_FROM_USER_ID_MODULE' - }); + bidRequests[0].userIdAsEids = [ + { + source: 'adserver.org', + uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}] + } + ]; request = spec.buildRequests(bidRequests); expect(request[0].data).to.include(JSON.stringify({ext: {consent: 'gdpr-consent-string', eids: [{source: 'adserver.org', uids: [{id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: {rtiPartner: 'TDID'}}]}]}})); diff --git a/test/spec/modules/xeBidAdapter_spec.js b/test/spec/modules/xeBidAdapter_spec.js index 38b334c32c5f..914b0cacd716 100644 --- a/test/spec/modules/xeBidAdapter_spec.js +++ b/test/spec/modules/xeBidAdapter_spec.js @@ -41,414 +41,412 @@ defaultRequestVideo.mediaTypes = { skipppable: true } }; +describe('xeBidAdapter', () => { + describe('isBidRequestValid', function () { + it('should return false when request params is missing', function () { + const invalidRequest = deepClone(defaultRequest); + delete invalidRequest.params; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); -describe('isBidRequestValid', function () { - it('should return false when request params is missing', function () { - const invalidRequest = deepClone(defaultRequest); - delete invalidRequest.params; - expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); - }); - - it('should return false when required env param is missing', function () { - const invalidRequest = deepClone(defaultRequest); - delete invalidRequest.params.env; - expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); - }); + it('should return false when required env param is missing', function () { + const invalidRequest = deepClone(defaultRequest); + delete invalidRequest.params.env; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); - it('should return false when required placement param is missing', function () { - const invalidRequest = deepClone(defaultRequest); - delete invalidRequest.params.placement; - expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); - }); + it('should return false when required placement param is missing', function () { + const invalidRequest = deepClone(defaultRequest); + delete invalidRequest.params.placement; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); - it('should return false when video.playerSize is missing', function () { - const invalidRequest = deepClone(defaultRequestVideo); - delete invalidRequest.mediaTypes.video.playerSize; - expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); - }); + it('should return false when video.playerSize is missing', function () { + const invalidRequest = deepClone(defaultRequestVideo); + delete invalidRequest.mediaTypes.video.playerSize; + expect(spec.isBidRequestValid(invalidRequest)).to.equal(false); + }); - it('should return true when required params found', function () { - expect(spec.isBidRequestValid(defaultRequest)).to.equal(true); + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(defaultRequest)).to.equal(true); + }); }); -}); -describe('buildRequests', function () { - beforeEach(function () { - config.resetConfig(); - }); + describe('buildRequests', function () { + beforeEach(function () { + config.resetConfig(); + }); - it('should send request with correct structure', function () { - const request = spec.buildRequests([defaultRequest], {}); - expect(request.method).to.equal('POST'); - expect(request.url).to.equal(ENDPOINT); - expect(request.options).to.have.property('contentType').and.to.equal('application/json'); - expect(request).to.have.property('data'); - }); + it('should send request with correct structure', function () { + const request = spec.buildRequests([defaultRequest], {}); + expect(request.method).to.equal('POST'); + expect(request.url).to.equal(ENDPOINT); + expect(request.options).to.have.property('contentType').and.to.equal('application/json'); + expect(request).to.have.property('data'); + }); - it('should build basic request structure', function () { - const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; - expect(request).to.have.property('bidId').and.to.equal(defaultRequest.bidId); - expect(request).to.have.property('auctionId').and.to.equal(defaultRequest.auctionId); - expect(request).to.have.property('transactionId').and.to.equal(defaultRequest.ortb2Imp.ext.tid); - expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); - expect(request).to.have.property('bc').and.to.equal(1); - expect(request).to.have.property('floor').and.to.equal(null); - expect(request).to.have.property('banner').and.to.deep.equal({ sizes: [[300, 250], [300, 200]] }); - expect(request).to.have.property('gdprApplies').and.to.equal(0); - expect(request).to.have.property('consentString').and.to.equal(''); - expect(request).to.have.property('userEids').and.to.deep.equal([]); - expect(request).to.have.property('usPrivacy').and.to.equal(''); - expect(request).to.have.property('coppa').and.to.equal(0); - expect(request).to.have.property('sizes').and.to.deep.equal(['300x250', '300x200']); - expect(request).to.have.property('ext').and.to.deep.equal({}); - expect(request).to.have.property('env').and.to.deep.equal({ - env: 'xe', - placement: 'test-banner' - }); - expect(request).to.have.property('device').and.to.deep.equal({ - ua: navigator.userAgent, - lang: navigator.language + it('should build basic request structure', function () { + const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; + expect(request).to.have.property('bidId').and.to.equal(defaultRequest.bidId); + expect(request).to.have.property('auctionId').and.to.equal(defaultRequest.auctionId); + expect(request).to.have.property('transactionId').and.to.equal(defaultRequest.ortb2Imp.ext.tid); + expect(request).to.have.property('tz').and.to.equal(new Date().getTimezoneOffset()); + expect(request).to.have.property('bc').and.to.equal(1); + expect(request).to.have.property('floor').and.to.equal(null); + expect(request).to.have.property('banner').and.to.deep.equal({ sizes: [[300, 250], [300, 200]] }); + expect(request).to.have.property('gdprApplies').and.to.equal(0); + expect(request).to.have.property('consentString').and.to.equal(''); + expect(request).to.have.property('userEids').and.to.deep.equal([]); + expect(request).to.have.property('usPrivacy').and.to.equal(''); + expect(request).to.have.property('coppa').and.to.equal(0); + expect(request).to.have.property('sizes').and.to.deep.equal(['300x250', '300x200']); + expect(request).to.have.property('ext').and.to.deep.equal({}); + expect(request).to.have.property('env').and.to.deep.equal({ + env: 'xe', + placement: 'test-banner' + }); + expect(request).to.have.property('device').and.to.deep.equal({ + ua: navigator.userAgent, + lang: navigator.language + }); }); - }); - it('should build request with schain', function () { - const schainRequest = deepClone(defaultRequest); - schainRequest.schain = { - validation: 'strict', - config: { - ver: '1.0' - } - }; - const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; - expect(request).to.have.property('schain').and.to.deep.equal({ - validation: 'strict', - config: { - ver: '1.0' - } + it('should build request with schain', function () { + const schainRequest = deepClone(defaultRequest); + schainRequest.schain = { + validation: 'strict', + config: { + ver: '1.0' + } + }; + const request = JSON.parse(spec.buildRequests([schainRequest], {}).data)[0]; + expect(request).to.have.property('schain').and.to.deep.equal({ + validation: 'strict', + config: { + ver: '1.0' + } + }); }); - }); - it('should build request with location', function () { - const bidderRequest = { - refererInfo: { - page: 'page', - location: 'location', - domain: 'domain', - ref: 'ref', - isAmp: false - } - }; - const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; - expect(request).to.have.property('location'); - const location = request.location; - expect(location).to.have.property('page').and.to.equal('page'); - expect(location).to.have.property('location').and.to.equal('location'); - expect(location).to.have.property('domain').and.to.equal('domain'); - expect(location).to.have.property('ref').and.to.equal('ref'); - expect(location).to.have.property('isAmp').and.to.equal(false); - }); + it('should build request with location', function () { + const bidderRequest = { + refererInfo: { + page: 'page', + location: 'location', + domain: 'domain', + ref: 'ref', + isAmp: false + } + }; + const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; + expect(request).to.have.property('location'); + const location = request.location; + expect(location).to.have.property('page').and.to.equal('page'); + expect(location).to.have.property('location').and.to.equal('location'); + expect(location).to.have.property('domain').and.to.equal('domain'); + expect(location).to.have.property('ref').and.to.equal('ref'); + expect(location).to.have.property('isAmp').and.to.equal(false); + }); - it('should build request with ortb2 info', function () { - const ortb2Request = deepClone(defaultRequest); - ortb2Request.ortb2 = { - site: { - name: 'name' - } - }; - const request = JSON.parse(spec.buildRequests([ortb2Request], {}).data)[0]; - expect(request).to.have.property('ortb2').and.to.deep.equal({ - site: { - name: 'name' - } + it('should build request with ortb2 info', function () { + const ortb2Request = deepClone(defaultRequest); + ortb2Request.ortb2 = { + site: { + name: 'name' + } + }; + const request = JSON.parse(spec.buildRequests([ortb2Request], {}).data)[0]; + expect(request).to.have.property('ortb2').and.to.deep.equal({ + site: { + name: 'name' + } + }); }); - }); - it('should build request with ortb2Imp info', function () { - const ortb2ImpRequest = deepClone(defaultRequest); - ortb2ImpRequest.ortb2Imp = { - ext: { - data: { - pbadslot: 'home1', - adUnitSpecificAttribute: '1' + it('should build request with ortb2Imp info', function () { + const ortb2ImpRequest = deepClone(defaultRequest); + ortb2ImpRequest.ortb2Imp = { + ext: { + data: { + pbadslot: 'home1', + adUnitSpecificAttribute: '1' + } } - } - }; - const request = JSON.parse(spec.buildRequests([ortb2ImpRequest], {}).data)[0]; - expect(request).to.have.property('ortb2Imp').and.to.deep.equal({ - ext: { - data: { - pbadslot: 'home1', - adUnitSpecificAttribute: '1' + }; + const request = JSON.parse(spec.buildRequests([ortb2ImpRequest], {}).data)[0]; + expect(request).to.have.property('ortb2Imp').and.to.deep.equal({ + ext: { + data: { + pbadslot: 'home1', + adUnitSpecificAttribute: '1' + } } - } + }); }); - }); - it('should build request with valid bidfloor', function () { - const bfRequest = deepClone(defaultRequest); - bfRequest.getFloor = () => ({ floor: 5, currency: 'USD' }); - const request = JSON.parse(spec.buildRequests([bfRequest], {}).data)[0]; - expect(request).to.have.property('floor').and.to.equal(5); - }); + it('should build request with valid bidfloor', function () { + const bfRequest = deepClone(defaultRequest); + bfRequest.getFloor = () => ({ floor: 5, currency: 'USD' }); + const request = JSON.parse(spec.buildRequests([bfRequest], {}).data)[0]; + expect(request).to.have.property('floor').and.to.equal(5); + }); - it('should build request with gdpr consent data if applies', function () { - const bidderRequest = { - gdprConsent: { - gdprApplies: true, - consentString: 'qwerty' - } - }; - const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; - expect(request).to.have.property('gdprApplies').and.equals(1); - expect(request).to.have.property('consentString').and.equals('qwerty'); - }); + it('should build request with gdpr consent data if applies', function () { + const bidderRequest = { + gdprConsent: { + gdprApplies: true, + consentString: 'qwerty' + } + }; + const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; + expect(request).to.have.property('gdprApplies').and.equals(1); + expect(request).to.have.property('consentString').and.equals('qwerty'); + }); - it('should build request with usp consent data if applies', function () { - const bidderRequest = { - uspConsent: '1YA-' - }; - const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; - expect(request).to.have.property('usPrivacy').and.equals('1YA-'); - }); + it('should build request with usp consent data if applies', function () { + const bidderRequest = { + uspConsent: '1YA-' + }; + const request = JSON.parse(spec.buildRequests([defaultRequest], bidderRequest).data)[0]; + expect(request).to.have.property('usPrivacy').and.equals('1YA-'); + }); - it('should build request with coppa 1', function () { - config.setConfig({ - coppa: true + it('should build request with coppa 1', function () { + config.setConfig({ + coppa: true + }); + const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; + expect(request).to.have.property('coppa').and.equals(1); }); - const request = JSON.parse(spec.buildRequests([defaultRequest], {}).data)[0]; - expect(request).to.have.property('coppa').and.equals(1); - }); - it('should build request with extended ids', function () { - const idRequest = deepClone(defaultRequest); - idRequest.userIdAsEids = createEidsArray({ - tdid: 'TTD_ID_FROM_USER_ID_MODULE', - pubcid: 'pubCommonId_FROM_USER_ID_MODULE' - }); - const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; - expect(request).to.have.property('userEids').and.deep.equal([ - { source: 'adserver.org', uids: [ { id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } } ] }, - { source: 'pubcid.org', uids: [ { id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 } ] } - ]); - }); + it('should build request with extended ids', function () { + const idRequest = deepClone(defaultRequest); + idRequest.userIdAsEids = [ + { source: 'adserver.org', uids: [ { id: 'TTD_ID_FROM_USER_ID_MODULE', atype: 1, ext: { rtiPartner: 'TDID' } } ] }, + { source: 'pubcid.org', uids: [ { id: 'pubCommonId_FROM_USER_ID_MODULE', atype: 1 } ] } + ]; + const request = JSON.parse(spec.buildRequests([idRequest], {}).data)[0]; + expect(request).to.have.property('userEids').and.deep.equal(idRequest.userIdAsEids); + }); - it('should build request with video', function () { - const request = JSON.parse(spec.buildRequests([defaultRequestVideo], {}).data)[0]; - expect(request).to.have.property('video').and.to.deep.equal({ - playerSize: [640, 480], - context: 'instream', - skipppable: true + it('should build request with video', function () { + const request = JSON.parse(spec.buildRequests([defaultRequestVideo], {}).data)[0]; + expect(request).to.have.property('video').and.to.deep.equal({ + playerSize: [640, 480], + context: 'instream', + skipppable: true + }); + expect(request).to.have.property('sizes').and.to.deep.equal(['640x480']); }); - expect(request).to.have.property('sizes').and.to.deep.equal(['640x480']); - }); -}); - -describe('interpretResponse', function () { - it('should return empty bids', function () { - const serverResponse = { - body: { - data: null - } - }; - - const invalidResponse = spec.interpretResponse(serverResponse, {}); - expect(invalidResponse).to.be.an('array').that.is.empty; }); - it('should interpret valid response', function () { - const serverResponse = { - body: { - data: [{ - requestId: 'qwerty', - cpm: 1, - currency: 'USD', - width: 300, - height: 250, - ttl: 600, - meta: { - advertiserDomains: ['xe.works'] - }, - ext: { - pixels: [ - [ 'iframe', 'surl1' ], - [ 'image', 'surl2' ], - ] - } - }] - } - }; - - const validResponse = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequest }); - const bid = validResponse[0]; - expect(validResponse).to.be.an('array').that.is.not.empty; - expect(bid.requestId).to.equal('qwerty'); - expect(bid.cpm).to.equal(1); - expect(bid.currency).to.equal('USD'); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); - expect(bid.ttl).to.equal(600); - expect(bid.meta).to.deep.equal({ advertiserDomains: ['xe.works'] }); - }); + describe('interpretResponse', function () { + it('should return empty bids', function () { + const serverResponse = { + body: { + data: null + } + }; - it('should interpret valid banner response', function () { - const serverResponse = { - body: { - data: [{ - requestId: 'qwerty', - cpm: 1, - currency: 'USD', - width: 300, - height: 250, - ttl: 600, - mediaType: 'banner', - creativeId: 'xe-demo-banner', - ad: 'ad', - meta: {} - }] - } - }; - - const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequest }); - const bid = validResponseBanner[0]; - expect(validResponseBanner).to.be.an('array').that.is.not.empty; - expect(bid.mediaType).to.equal('banner'); - expect(bid.creativeId).to.equal('xe-demo-banner'); - expect(bid.ad).to.equal('ad'); - }); + const invalidResponse = spec.interpretResponse(serverResponse, {}); + expect(invalidResponse).to.be.an('array').that.is.empty; + }); - it('should interpret valid video response', function () { - const serverResponse = { - body: { - data: [{ - requestId: 'qwerty', - cpm: 1, - currency: 'USD', - width: 600, - height: 480, - ttl: 600, - mediaType: 'video', - creativeId: 'xe-demo-video', - ad: 'vast-xml', - meta: {} - }] - } - }; - - const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequestVideo }); - const bid = validResponseBanner[0]; - expect(validResponseBanner).to.be.an('array').that.is.not.empty; - expect(bid.mediaType).to.equal('video'); - expect(bid.creativeId).to.equal('xe-demo-video'); - expect(bid.ad).to.equal('vast-xml'); - }); -}); + it('should interpret valid response', function () { + const serverResponse = { + body: { + data: [{ + requestId: 'qwerty', + cpm: 1, + currency: 'USD', + width: 300, + height: 250, + ttl: 600, + meta: { + advertiserDomains: ['xe.works'] + }, + ext: { + pixels: [ + [ 'iframe', 'surl1' ], + [ 'image', 'surl2' ], + ] + } + }] + } + }; + + const validResponse = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequest }); + const bid = validResponse[0]; + expect(validResponse).to.be.an('array').that.is.not.empty; + expect(bid.requestId).to.equal('qwerty'); + expect(bid.cpm).to.equal(1); + expect(bid.currency).to.equal('USD'); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + expect(bid.ttl).to.equal(600); + expect(bid.meta).to.deep.equal({ advertiserDomains: ['xe.works'] }); + }); -describe('getUserSyncs', function () { - it('shoukd handle no params', function () { - const opts = spec.getUserSyncs({}, []); - expect(opts).to.be.an('array').that.is.empty; - }); + it('should interpret valid banner response', function () { + const serverResponse = { + body: { + data: [{ + requestId: 'qwerty', + cpm: 1, + currency: 'USD', + width: 300, + height: 250, + ttl: 600, + mediaType: 'banner', + creativeId: 'xe-demo-banner', + ad: 'ad', + meta: {} + }] + } + }; + + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequest }); + const bid = validResponseBanner[0]; + expect(validResponseBanner).to.be.an('array').that.is.not.empty; + expect(bid.mediaType).to.equal('banner'); + expect(bid.creativeId).to.equal('xe-demo-banner'); + expect(bid.ad).to.equal('ad'); + }); - it('should return empty if sync is not allowed', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); - expect(opts).to.be.an('array').that.is.empty; + it('should interpret valid video response', function () { + const serverResponse = { + body: { + data: [{ + requestId: 'qwerty', + cpm: 1, + currency: 'USD', + width: 600, + height: 480, + ttl: 600, + mediaType: 'video', + creativeId: 'xe-demo-video', + ad: 'vast-xml', + meta: {} + }] + } + }; + + const validResponseBanner = spec.interpretResponse(serverResponse, { bidderRequest: defaultRequestVideo }); + const bid = validResponseBanner[0]; + expect(validResponseBanner).to.be.an('array').that.is.not.empty; + expect(bid.mediaType).to.equal('video'); + expect(bid.creativeId).to.equal('xe-demo-video'); + expect(bid.ad).to.equal('vast-xml'); + }); }); - it('should allow iframe sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [{ - body: { - data: [{ - requestId: 'qwerty', - ext: { - pixels: [ - [ 'iframe', 'surl1?a=b' ], - [ 'image', 'surl2?a=b' ], - ] - } - }] - } - }]); - expect(opts.length).to.equal(1); - expect(opts[0].type).to.equal('iframe'); - expect(opts[0].url).to.equal('surl1?a=b&us_privacy=&gdpr=0&gdpr_consent='); - }); + describe('getUserSyncs', function () { + it('shoukd handle no params', function () { + const opts = spec.getUserSyncs({}, []); + expect(opts).to.be.an('array').that.is.empty; + }); - it('should allow pixel sync', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ - body: { - data: [{ - requestId: 'qwerty', - ext: { - pixels: [ - [ 'iframe', 'surl1?a=b' ], - [ 'image', 'surl2?a=b' ], - ] - } - }] - } - }]); - expect(opts.length).to.equal(1); - expect(opts[0].type).to.equal('image'); - expect(opts[0].url).to.equal('surl2?a=b&us_privacy=&gdpr=0&gdpr_consent='); - }); + it('should return empty if sync is not allowed', function () { + const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: false}); + expect(opts).to.be.an('array').that.is.empty; + }); - it('should allow pixel sync and parse consent params', function () { - const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ - body: { - data: [{ - requestId: 'qwerty', - ext: { - pixels: [ - [ 'iframe', 'surl1?a=b' ], - [ 'image', 'surl2?a=b' ], - ] - } - }] - } - }], { - gdprApplies: 1, - consentString: '1YA-' - }); - expect(opts.length).to.equal(1); - expect(opts[0].type).to.equal('image'); - expect(opts[0].url).to.equal('surl2?a=b&us_privacy=&gdpr=1&gdpr_consent=1YA-'); - }); -}); + it('should allow iframe sync', function () { + const opts = spec.getUserSyncs({iframeEnabled: true, pixelEnabled: false}, [{ + body: { + data: [{ + requestId: 'qwerty', + ext: { + pixels: [ + [ 'iframe', 'surl1?a=b' ], + [ 'image', 'surl2?a=b' ], + ] + } + }] + } + }]); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('iframe'); + expect(opts[0].url).to.equal('surl1?a=b&us_privacy=&gdpr=0&gdpr_consent='); + }); -describe('getBidFloor', function () { - it('should return null when getFloor is not a function', () => { - const bid = { getFloor: 2 }; - const result = getBidFloor(bid); - expect(result).to.be.null; - }); + it('should allow pixel sync', function () { + const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + body: { + data: [{ + requestId: 'qwerty', + ext: { + pixels: [ + [ 'iframe', 'surl1?a=b' ], + [ 'image', 'surl2?a=b' ], + ] + } + }] + } + }]); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal('surl2?a=b&us_privacy=&gdpr=0&gdpr_consent='); + }); - it('should return null when getFloor doesnt return an object', () => { - const bid = { getFloor: () => 2 }; - const result = getBidFloor(bid); - expect(result).to.be.null; + it('should allow pixel sync and parse consent params', function () { + const opts = spec.getUserSyncs({iframeEnabled: false, pixelEnabled: true}, [{ + body: { + data: [{ + requestId: 'qwerty', + ext: { + pixels: [ + [ 'iframe', 'surl1?a=b' ], + [ 'image', 'surl2?a=b' ], + ] + } + }] + } + }], { + gdprApplies: 1, + consentString: '1YA-' + }); + expect(opts.length).to.equal(1); + expect(opts[0].type).to.equal('image'); + expect(opts[0].url).to.equal('surl2?a=b&us_privacy=&gdpr=1&gdpr_consent=1YA-'); + }); }); - it('should return null when floor is not a number', () => { - const bid = { - getFloor: () => ({ floor: 'string', currency: 'USD' }) - }; - const result = getBidFloor(bid); - expect(result).to.be.null; - }); + describe('getBidFloor', function () { + it('should return null when getFloor is not a function', () => { + const bid = { getFloor: 2 }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); - it('should return null when currency is not USD', () => { - const bid = { - getFloor: () => ({ floor: 5, currency: 'EUR' }) - }; - const result = getBidFloor(bid); - expect(result).to.be.null; - }); + it('should return null when getFloor doesnt return an object', () => { + const bid = { getFloor: () => 2 }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); - it('should return floor value when everything is correct', () => { - const bid = { - getFloor: () => ({ floor: 5, currency: 'USD' }) - }; - const result = getBidFloor(bid); - expect(result).to.equal(5); + it('should return null when floor is not a number', () => { + const bid = { + getFloor: () => ({ floor: 'string', currency: 'USD' }) + }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return null when currency is not USD', () => { + const bid = { + getFloor: () => ({ floor: 5, currency: 'EUR' }) + }; + const result = getBidFloor(bid); + expect(result).to.be.null; + }); + + it('should return floor value when everything is correct', () => { + const bid = { + getFloor: () => ({ floor: 5, currency: 'USD' }) + }; + const result = getBidFloor(bid); + expect(result).to.equal(5); + }); }); -}); +}) diff --git a/test/spec/modules/yahoosspBidAdapter_spec.js b/test/spec/modules/yahoosspBidAdapter_spec.js index c05841730829..40dc2b3c63b1 100644 --- a/test/spec/modules/yahoosspBidAdapter_spec.js +++ b/test/spec/modules/yahoosspBidAdapter_spec.js @@ -969,23 +969,7 @@ describe('Yahoo Advertising Bid Adapter:', () => { describe('User data', () => { 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', - britepoolid: 'britepoolid_FROM_USER_ID_MODULE', - deepintentId: 'deepintentId_FROM_USER_ID_MODULE', - publinkId: 'publinkId_FROM_USER_ID_MODULE', - intentIqId: 'intentIqId_FROM_USER_ID_MODULE', - idl_env: 'idl_env_FROM_USER_ID_MODULE', - imuid: 'imuid_FROM_USER_ID_MODULE', - criteoId: 'criteoId_FROM_USER_ID_MODULE', - fabrickId: 'fabrickId_FROM_USER_ID_MODULE', - }); - const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; - - expect(data.user.ext.eids).to.deep.equal([ + validBidRequests[0].userIdAsEids = [ {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}]}, @@ -998,7 +982,10 @@ describe('Yahoo Advertising Bid Adapter:', () => { {source: 'intimatemerger.com', uids: [{id: 'imuid_FROM_USER_ID_MODULE', atype: 1}]}, {source: 'criteo.com', uids: [{id: 'criteoId_FROM_USER_ID_MODULE', atype: 1}]}, {source: 'neustar.biz', uids: [{id: 'fabrickId_FROM_USER_ID_MODULE', atype: 1}]} - ]); + ]; + const data = spec.buildRequests(validBidRequests, bidderRequest)[0].data; + + expect(data.user.ext.eids).to.deep.equal(validBidRequests[0].userIdAsEids); }); it('should not set not allowed user eids sources', () => { From 601ed4ab1ae6182aba40850eca33c9ecfdca4139 Mon Sep 17 00:00:00 2001 From: mihaisandu07 <102022853+mihaisandu07@users.noreply.github.com> Date: Fri, 14 Jul 2023 15:52:12 +0300 Subject: [PATCH 45/92] Invibes Bid Adapter : change referrer formatting (#10017) * Update invibesBidAdapter.js Removed cutoff of url parameters from location param. * Update invibesBidAdapter_spec.js add unit test for cut off location * Update invibesBidAdapter_spec.js updated unit tests to do a better check on location href * changed the way InvibesBidAdapter gets the page location. * Update invibesBidAdapter.js simplified the fetching of the page url * Update invibesBidAdapter.js remove unused config library * updated invibes unit tests to accomodate the new page url fetching method --- modules/invibesBidAdapter.js | 10 +- test/spec/modules/invibesBidAdapter_spec.js | 155 ++++++++++++++++---- 2 files changed, 128 insertions(+), 37 deletions(-) diff --git a/modules/invibesBidAdapter.js b/modules/invibesBidAdapter.js index b2444043c226..0c0d1cdef875 100644 --- a/modules/invibesBidAdapter.js +++ b/modules/invibesBidAdapter.js @@ -1,4 +1,4 @@ -import { logInfo } from '../src/utils.js'; +import {logInfo} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getStorageManager} from '../src/storageManager.js'; @@ -9,7 +9,7 @@ const CONSTANTS = { SYNC_ENDPOINT: 'https://k.r66net.com/GetUserSync', TIME_TO_LIVE: 300, DEFAULT_CURRENCY: 'EUR', - PREBID_VERSION: 9, + PREBID_VERSION: 10, METHOD: 'GET', INVIBES_VENDOR_ID: 436, USERID_PROVIDERS: ['pubcid', 'pubProvidedId', 'uid2', 'zeotapIdPlus', 'id5id'], @@ -116,7 +116,7 @@ function buildRequest(bidRequests, bidderRequest) { bidParamsJson.userId = userIdModel; } let data = { - location: getDocumentLocation(topWin), + location: getDocumentLocation(bidderRequest), videoAdHtmlId: generateRandomId(), showFallback: currentQueryStringParams['advs'] === '0', ivbsCampIdsLocal: readFromLocalStorage('IvbsCampIdsLocal'), @@ -371,8 +371,8 @@ function generateRandomId() { return (Math.round(Math.random() * 1e12)).toString(36).substring(0, 10); } -function getDocumentLocation(topWin) { - return topWin.location.href.substring(0, 300).split(/[?#]/)[0]; +function getDocumentLocation(bidderRequest) { + return bidderRequest.refererInfo.page.substring(0, 300); } function getUserIds(bidUserId) { diff --git a/test/spec/modules/invibesBidAdapter_spec.js b/test/spec/modules/invibesBidAdapter_spec.js index 2b98a9f89601..7ee6b4649962 100644 --- a/test/spec/modules/invibesBidAdapter_spec.js +++ b/test/spec/modules/invibesBidAdapter_spec.js @@ -81,6 +81,13 @@ describe('invibesBidAdapter:', function () { } ]; + let bidderRequestWithPageInfo = { + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' + }, + auctionStart: Date.now() + } + let StubbedPersistence = function (initialValue) { var value = initialValue; return { @@ -174,17 +181,17 @@ describe('invibesBidAdapter:', function () { describe('buildRequests', function () { it('sends preventPageViewEvent as false on first call', function () { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.preventPageViewEvent).to.be.false; }); it('sends preventPageViewEvent as true on 2nd call', function () { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.preventPageViewEvent).to.be.true; }); it('sends bid request to ENDPOINT via GET', function () { - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('GET'); }); @@ -198,7 +205,7 @@ describe('invibesBidAdapter:', function () { customEndpoint: 'sub.domain.com/Bid/VideoAdContent' }, adUnitCode: 'test-div1' - }]); + }], bidderRequestWithPageInfo); expect(request.url).to.equal('sub.domain.com/Bid/VideoAdContent'); expect(request.method).to.equal('GET'); }); @@ -210,7 +217,7 @@ describe('invibesBidAdapter:', function () { params: { }, adUnitCode: 'test-div1' - }]); + }], bidderRequestWithPageInfo); expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('GET'); }); @@ -223,7 +230,7 @@ describe('invibesBidAdapter:', function () { placementId: null }, adUnitCode: 'test-div1' - }]); + }], bidderRequestWithPageInfo); expect(request.url).to.equal(ENDPOINT); expect(request.method).to.equal('GET'); }); @@ -236,7 +243,7 @@ describe('invibesBidAdapter:', function () { placementId: 'placement' }, adUnitCode: 'test-div1' - }]); + }], bidderRequestWithPageInfo); expect(request.url).to.equal('https://bid.videostep.com/Bid/VideoAdContent'); expect(request.method).to.equal('GET'); }); @@ -250,7 +257,7 @@ describe('invibesBidAdapter:', function () { domainId: 1001 }, adUnitCode: 'test-div1' - }]); + }], bidderRequestWithPageInfo); expect(request.url).to.equal('https://bid.videostep.com/Bid/VideoAdContent'); expect(request.method).to.equal('GET'); }); @@ -264,7 +271,7 @@ describe('invibesBidAdapter:', function () { domainId: 1002 }, adUnitCode: 'test-div1' - }]); + }], bidderRequestWithPageInfo); expect(request.url).to.equal('https://bid2.videostep.com/Bid/VideoAdContent'); expect(request.method).to.equal('GET'); }); @@ -277,7 +284,7 @@ describe('invibesBidAdapter:', function () { placementId: 'infeed_ivbs1' }, adUnitCode: 'test-div1' - }]); + }], bidderRequestWithPageInfo); expect(request.url).to.equal('https://bid.videostep.com/Bid/VideoAdContent'); expect(request.method).to.equal('GET'); }); @@ -290,7 +297,7 @@ describe('invibesBidAdapter:', function () { placementId: 'infeed_ivbs2' }, adUnitCode: 'test-div1' - }]); + }], bidderRequestWithPageInfo); expect(request.url).to.equal('https://bid2.videostep.com/Bid/VideoAdContent'); expect(request.method).to.equal('GET'); }); @@ -303,18 +310,18 @@ describe('invibesBidAdapter:', function () { placementId: 'infeed_ivbs10' }, adUnitCode: 'test-div1' - }]); + }], bidderRequestWithPageInfo); expect(request.url).to.equal('https://bid10.videostep.com/Bid/VideoAdContent'); expect(request.method).to.equal('GET'); }); it('sends cookies with the bid request', function () { - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.options.withCredentials).to.equal(true); }); it('has location, html id, placement and width/height', function () { - const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()}); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); const parsedData = request.data; expect(parsedData.location).to.exist; expect(parsedData.videoAdHtmlId).to.exist; @@ -323,13 +330,20 @@ describe('invibesBidAdapter:', function () { expect(parsedData.height).to.exist; }); + it('has location not cut off', function () { + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); + const parsedData = request.data; + expect(parsedData.location).to.contain('?'); + expect(parsedData.location).to.equal('https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue'); + }); + it('has capped ids if local storage variable is correctly formatted', function () { top.window.invibes.optIn = 1; top.window.invibes.purposes = [true, false, false, false, false, false, false, false, false, false]; localStorage.ivvcap = '{"9731":[1,1768600800000]}'; SetBidderAccess(); - const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()}); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.capCounts).to.equal('9731=1'); }); @@ -356,6 +370,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; @@ -366,59 +383,59 @@ describe('invibesBidAdapter:', function () { it('does not have capped ids if local storage variable is incorrectly formatted', function () { localStorage.ivvcap = ':[1,1574334216992]}'; - const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()}); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.capCounts).to.equal(''); }); it('does not have capped ids if local storage variable is expired', function () { localStorage.ivvcap = '{"9731":[1,1574330064104]}'; - const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()}); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.capCounts).to.equal(''); }); it('sends query string params from localstorage 1', function () { localStorage.ivbs = JSON.stringify({bvci: 1}); - const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()}); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.bvci).to.equal(1); }); it('sends query string params from localstorage 2', function () { localStorage.ivbs = JSON.stringify({invibbvlog: true}); - const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()}); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.invibbvlog).to.equal(true); }); it('does not send query string params from localstorage if unknwon', function () { localStorage.ivbs = JSON.stringify({someparam: true}); - const request = spec.buildRequests(bidRequests, {auctionStart: Date.now()}); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.someparam).to.be.undefined; }); it('sends all Placement Ids', function () { - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(JSON.parse(request.data.bidParamsJson).placementIds).to.contain(bidRequests[0].params.placementId); expect(JSON.parse(request.data.bidParamsJson).placementIds).to.contain(bidRequests[1].params.placementId); }); it('sends all adUnitCodes', function () { - const request = spec.buildRequests(bidRequests); + const request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(JSON.parse(request.data.bidParamsJson).adUnitCodes).to.contain(bidRequests[0].adUnitCode); expect(JSON.parse(request.data.bidParamsJson).adUnitCodes).to.contain(bidRequests[1].adUnitCode); }); it('sends all Placement Ids and userId', function () { - const request = spec.buildRequests(bidRequestsWithUserId); + const request = spec.buildRequests(bidRequestsWithUserId, bidderRequestWithPageInfo); expect(JSON.parse(request.data.bidParamsJson).userId).to.exist; }); it('sends undefined lid when no cookie', function () { - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.lId).to.be.undefined; }); it('sends pushed cids if they exist', function () { top.window.invibes.pushedCids = { 981: [] }; - let request = spec.buildRequests(bidRequests); + let request = spec.buildRequests(bidRequests, bidderRequestWithPageInfo); expect(request.data.pcids).to.contain(981); }); @@ -426,7 +443,18 @@ describe('invibesBidAdapter:', function () { top.window.invibes.optIn = 1; top.window.invibes.purposes = [true, false, false, false, false, false, false, false, false, false]; global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":0}'; - let bidderRequest = {gdprConsent: {vendorData: {vendorConsents: {436: true}}}}; + let bidderRequest = { + gdprConsent: { + vendorData: { + vendorConsents: { + 436: true + } + } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' + } + }; SetBidderAccess(); let request = spec.buildRequests(bidRequests, bidderRequest); @@ -455,6 +483,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -483,6 +514,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -511,6 +545,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -551,6 +588,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -560,6 +600,9 @@ describe('invibesBidAdapter:', function () { let bidderRequest = { gdprConsent: { vendorData: null + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -588,6 +631,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -615,6 +661,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -642,6 +691,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -669,6 +721,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -683,6 +738,9 @@ describe('invibesBidAdapter:', function () { vendor: {consents: {436: false}}, purpose: {} } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -711,6 +769,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -734,6 +795,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -762,6 +826,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -790,6 +857,9 @@ describe('invibesBidAdapter:', function () { } } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -811,6 +881,9 @@ describe('invibesBidAdapter:', function () { 5: true } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -832,6 +905,9 @@ describe('invibesBidAdapter:', function () { 5: true } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -845,6 +921,9 @@ describe('invibesBidAdapter:', function () { gdprApplies: false, hasGlobalConsent: true, } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -866,6 +945,9 @@ describe('invibesBidAdapter:', function () { 5: true } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -887,6 +969,9 @@ describe('invibesBidAdapter:', function () { 5: true } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -906,6 +991,9 @@ describe('invibesBidAdapter:', function () { 3: true } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -927,6 +1015,9 @@ describe('invibesBidAdapter:', function () { 5: true } } + }, + refererInfo: { + page: 'https://randomWeb.com?someFakePara=fakeValue&secondParam=secondValue' } }; let request = spec.buildRequests(bidRequests, bidderRequest); @@ -1214,13 +1305,13 @@ describe('invibesBidAdapter:', function () { describe('getUserSyncs', function () { it('returns undefined if disableUserSyncs not passed as bid request param ', function () { - spec.buildRequests(bidRequestsWithUserId); + spec.buildRequests(bidRequestsWithUserId, bidderRequestWithPageInfo); let response = spec.getUserSyncs({iframeEnabled: true}); expect(response).to.equal(undefined); }); it('returns an iframe if enabled', function () { - spec.buildRequests(bidRequests); + spec.buildRequests(bidRequests, bidderRequestWithPageInfo); let response = spec.getUserSyncs({iframeEnabled: true}); expect(response.type).to.equal('iframe'); @@ -1229,7 +1320,7 @@ describe('invibesBidAdapter:', function () { it('returns an iframe with params if enabled', function () { top.window.invibes.optIn = 1; - spec.buildRequests(bidRequests); + spec.buildRequests(bidRequests, bidderRequestWithPageInfo); let response = spec.getUserSyncs({iframeEnabled: true}); expect(response.type).to.equal('iframe'); @@ -1239,7 +1330,7 @@ describe('invibesBidAdapter:', function () { it('returns an iframe with params including if enabled', function () { top.window.invibes.optIn = 1; - spec.buildRequests(bidRequests); + spec.buildRequests(bidRequests, bidderRequestWithPageInfo); global.document.cookie = 'ivbsdid={"id":"dvdjkams6nkq","cr":' + Date.now() + ',"hc":0}'; SetBidderAccess(); @@ -1252,7 +1343,7 @@ describe('invibesBidAdapter:', function () { }); it('returns an iframe with params including if enabled read from LocalStorage', function () { - spec.buildRequests(bidRequests); + spec.buildRequests(bidRequests, bidderRequestWithPageInfo); top.window.invibes.optIn = 1; localStorage.ivbsdid = 'dvdjkams6nkq'; @@ -1266,7 +1357,7 @@ describe('invibesBidAdapter:', function () { }); it('returns undefined if iframe not enabled ', function () { - spec.buildRequests(bidRequests); + spec.buildRequests(bidRequests, bidderRequestWithPageInfo); let response = spec.getUserSyncs({iframeEnabled: false}); expect(response).to.equal(undefined); From 61a89c1bfec03db19149f04b2b340afdd767a2c3 Mon Sep 17 00:00:00 2001 From: Antonios Sarhanis Date: Fri, 14 Jul 2023 22:55:48 +1000 Subject: [PATCH 46/92] Adnuntius Bid Adaptor: re-enable alias use (#10232) - fixes a regression that blocked the use of unreserved aliases. - now aliases that are not reserved as deals can serve. --- modules/adnuntiusBidAdapter.js | 18 +- test/spec/modules/adnuntiusBidAdapter_spec.js | 180 +++++++++++------- 2 files changed, 121 insertions(+), 77 deletions(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index d1192c2eb642..e44e2b1471a7 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -6,11 +6,9 @@ import { getStorageManager } from '../src/storageManager.js'; const BIDDER_CODE = 'adnuntius'; const BIDDER_CODE_DEAL_ALIAS_BASE = 'adndeal'; -const BIDDER_CODE_DEAL_ALIAS_1 = BIDDER_CODE_DEAL_ALIAS_BASE + '1'; -const BIDDER_CODE_DEAL_ALIAS_2 = BIDDER_CODE_DEAL_ALIAS_BASE + '2'; -const BIDDER_CODE_DEAL_ALIAS_3 = BIDDER_CODE_DEAL_ALIAS_BASE + '3'; -const BIDDER_CODE_DEAL_ALIAS_4 = BIDDER_CODE_DEAL_ALIAS_BASE + '4'; -const BIDDER_CODE_DEAL_ALIAS_5 = BIDDER_CODE_DEAL_ALIAS_BASE + '5'; +const BIDDER_CODE_DEAL_ALIASES = [1, 2, 3, 4, 5].map(num => { + return BIDDER_CODE_DEAL_ALIAS_BASE + num; +}); const ENDPOINT_URL = 'https://ads.adnuntius.delivery/i'; const ENDPOINT_URL_EUROPE = 'https://europe.delivery.adnuntius.com/i'; const GVLID = 855; @@ -54,13 +52,7 @@ const AU_ID_REGEX = new RegExp('^[0-9A-Fa-f]{1,20}$'); export const spec = { code: BIDDER_CODE, - aliases: [ - BIDDER_CODE_DEAL_ALIAS_1, - BIDDER_CODE_DEAL_ALIAS_2, - BIDDER_CODE_DEAL_ALIAS_3, - BIDDER_CODE_DEAL_ALIAS_4, - BIDDER_CODE_DEAL_ALIAS_5 - ], + aliases: BIDDER_CODE_DEAL_ALIASES, gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], isBidRequestValid: function (bid) { @@ -178,7 +170,7 @@ export const spec = { const hasBidAdUnits = adUnits.filter((au) => { const bid = bidsById[au.targetId]; - if (bid && bid.bidder === BIDDER_CODE) { + if (bid && bid.bidder && BIDDER_CODE_DEAL_ALIASES.indexOf(bid.bidder) < 0) { return au.matchedAdCount > 0; } else { // We do NOT accept bids when using this adaptor via one of the diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index b1657ed1596d..4ddbaaa2e2a3 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -12,7 +12,7 @@ describe('adnuntiusBidAdapter', function() { const EURO_URL = 'https://europe.delivery.adnuntius.com/i?tzo='; const GVLID = 855; const usi = utils.generateUUID() - const meta = [{ key: 'usi', value: usi }] + const meta = [{key: 'usi', value: usi}] before(() => { getGlobal().bidderSettings = { @@ -20,7 +20,7 @@ describe('adnuntiusBidAdapter', function() { storageAllowed: true } }; - const storage = getStorageManager({ bidderCode: 'adnuntius' }) + const storage = getStorageManager({bidderCode: 'adnuntius'}) storage.setDataInLocalStorage('adn.metaData', JSON.stringify(meta)) }); @@ -102,6 +102,69 @@ describe('adnuntiusBidAdapter', function() { bidder: 'adnuntius' } + const deals = [ + { + 'destinationUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fc%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com', + 'assets': { + 'Image': { + 'cdnId': 'https://cdn.adnuntius.com/cdn/iBgqruUNbaUb2hmD3vws7WTi84jg_WB_-VOF_FeOZ7A.png', + 'width': '640', + 'height': '480' + } + }, + 'text': {}, + 'choices': {}, + 'clickUrl': 'https://ads.adnuntius.delivery/c/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'urls': { + 'destination': 'https://ads.adnuntius.delivery/c/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg?ct=673&r=http%3A%2F%2Fadnuntius.com' + }, + 'urlsEsc': { + 'destination': 'https%3A%2F%2Fads.adnuntius.delivery%2Fc%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com' + }, + 'destinationUrls': { + 'destination': 'https://adnuntius.com' + }, + 'cpm': { + 'amount': 9, + 'currency': 'USD' + }, + 'bid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'grossBid': { + 'amount': 0.009, + 'currency': 'USD' + }, + 'netBid': { + 'amount': 0.0081, + 'currency': 'USD' + }, + 'dealId': 'abc123xyz', + 'impressionTrackingUrls': [], + 'impressionTrackingUrlsEsc': [], + 'adId': 'adn-id-1064238860', + 'selectedColumn': '0', + 'selectedColumnPosition': '0', + 'renderedPixel': 'https://ads.adnuntius.delivery/b/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg.html', + 'renderedPixelEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fb%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg.html', + 'visibleUrl': 'https://ads.adnuntius.delivery/s?rt=yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'visibleUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fs%3Frt%3DyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'viewUrl': 'https://ads.adnuntius.delivery/v?rt=yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'viewUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fv%3Frt%3DyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'rt': 'yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', + 'creativeWidth': '640', + 'creativeHeight': '480', + 'creativeId': 's90t0q03pm', + 'lineItemId': 'cr3hnkkxhnkw9ldy', + 'layoutId': 'buyers_network_image_layout_1', + 'layoutName': 'Image', + 'layoutExternalReference': '', + 'html': "\n\n\n \n \n \n\n\n
\n
\"\"/
\n
\n \n\n", + 'renderTemplate': '' + } + ]; + const serverResponse = { body: { 'adUnits': [ @@ -111,68 +174,7 @@ describe('adnuntiusBidAdapter', function() { 'html': "\n\n\n \n \n \n\n\n
\n
\"\"/
\n
\n \n\n", 'matchedAdCount': 1, 'responseId': 'adn-rsp--229633088', - 'deals': [ - { - 'destinationUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fc%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com', - 'assets': { - 'Image': { - 'cdnId': 'https://cdn.adnuntius.com/cdn/iBgqruUNbaUb2hmD3vws7WTi84jg_WB_-VOF_FeOZ7A.png', - 'width': '640', - 'height': '480' - } - }, - 'text': {}, - 'choices': {}, - 'clickUrl': 'https://ads.adnuntius.delivery/c/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'urls': { - 'destination': 'https://ads.adnuntius.delivery/c/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg?ct=673&r=http%3A%2F%2Fadnuntius.com' - }, - 'urlsEsc': { - 'destination': 'https%3A%2F%2Fads.adnuntius.delivery%2Fc%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com' - }, - 'destinationUrls': { - 'destination': 'https://adnuntius.com' - }, - 'cpm': { - 'amount': 9, - 'currency': 'USD' - }, - 'bid': { - 'amount': 0.009, - 'currency': 'USD' - }, - 'grossBid': { - 'amount': 0.009, - 'currency': 'USD' - }, - 'netBid': { - 'amount': 0.0081, - 'currency': 'USD' - }, - 'dealId': 'abc123xyz', - 'impressionTrackingUrls': [], - 'impressionTrackingUrlsEsc': [], - 'adId': 'adn-id-1064238860', - 'selectedColumn': '0', - 'selectedColumnPosition': '0', - 'renderedPixel': 'https://ads.adnuntius.delivery/b/yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg.html', - 'renderedPixelEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fb%2FyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg.html', - 'visibleUrl': 'https://ads.adnuntius.delivery/s?rt=yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'visibleUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fs%3Frt%3DyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'viewUrl': 'https://ads.adnuntius.delivery/v?rt=yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'viewUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fv%3Frt%3DyQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'rt': 'yQtMUwYBn5P4v72WJMqLW4z7uJOBFXJTfjoRyz0z_wsAAAAQCtjQz9kbGWD4nuZy3q6HaCYxq6Lckz2kThplNb227EJdQ5032jcIGkf-UrPmXCU2EbXVaQ3Ok6_FNLuIDTONJyx6ZZCB10wGqA3OaSe1EqwQp84u1_5iQZAWDk73UYf7_vcIypn7ev-SICZ3qaevb2jYSRqTVZx6AiBZQQGlzlOOrbZU9AU1F-JwTds-YV3qtJHGlxI2peWFIuxFlOYyeX9Kzg', - 'creativeWidth': '640', - 'creativeHeight': '480', - 'creativeId': 's90t0q03pm', - 'lineItemId': 'cr3hnkkxhnkw9ldy', - 'layoutId': 'buyers_network_image_layout_1', - 'layoutName': 'Image', - 'layoutExternalReference': '', - 'html': "\n\n\n \n \n \n\n\n
\n
\"\"/
\n
\n \n\n", - 'renderTemplate': '' - } - ], + 'deals': deals, 'ads': [ { 'destinationUrlEsc': 'http%3A%2F%2Fads.adnuntius.delivery%2Fc%2F5Mu-vFVsaf4dWWx8uyZoV7Mz0sPkF1_j9bSupMwX8dMAAAAQCtjQz9kbGWD4nuZy3q6HaCYxqqTYxj2sS0FkMriztxcORshj3zRbT0KsV7XnDXE0F-OsHX7Ok6_FNLuIDTOMJyx6ZZCB10wGqA3OaSe1Eq9D85h8gP1gGsobC0KsAISm_PYNkJ6ve6qZLnB79fX6XHLYSRqTBM8sBCRXQAetnVzeo7AHoQhkFeouS444YA_q4JCTlRI2peWFIuxFlOYyeX9Kzg%3Fct%3D673%26r%3Dhttp%253A%252F%252Fadnuntius.com', @@ -782,6 +784,56 @@ describe('adnuntiusBidAdapter', function() { expect(interpretedResponse[1].dealId).to.equal('not-in-deal-array-here'); expect(interpretedResponse[1].dealCount).to.equal(0); }); + + it('should not process valid response when passed alt bidder that is an adndeal', function() { + const altBidder = { + bid: [ + { + bidder: 'adndeal1', + bidId: 'adn-0000000000000551', + } + ] + }; + serverResponse.body.adUnits[0].deals = []; + + const interpretedResponse = spec.interpretResponse(serverResponse, altBidder); + expect(interpretedResponse).to.have.lengthOf(0); + + serverResponse.body.adUnits[0].deals = deals; + }); + + it('should return valid response when passed alt bidder', function() { + const altBidder = { + bid: [ + { + bidder: 'adn-alt', + bidId: 'adn-0000000000000551', + } + ] + }; + serverResponse.body.adUnits[0].deals = []; + + const interpretedResponse = spec.interpretResponse(serverResponse, altBidder); + expect(interpretedResponse).to.have.lengthOf(1); + + const ad = serverResponse.body.adUnits[0].ads[0]; + expect(interpretedResponse[0].bidderCode).to.equal('adn-alt'); + expect(interpretedResponse[0].cpm).to.equal(ad.bid.amount * 1000); + expect(interpretedResponse[0].width).to.equal(Number(ad.creativeWidth)); + expect(interpretedResponse[0].height).to.equal(Number(ad.creativeHeight)); + expect(interpretedResponse[0].creativeId).to.equal(ad.creativeId); + expect(interpretedResponse[0].currency).to.equal(ad.bid.currency); + expect(interpretedResponse[0].netRevenue).to.equal(false); + expect(interpretedResponse[0].meta).to.have.property('advertiserDomains'); + expect(interpretedResponse[0].meta.advertiserDomains).to.have.lengthOf(1); + expect(interpretedResponse[0].meta.advertiserDomains[0]).to.equal('adnuntius.com'); + expect(interpretedResponse[0].ad).to.equal(serverResponse.body.adUnits[0].html); + expect(interpretedResponse[0].ttl).to.equal(360); + expect(interpretedResponse[0].dealId).to.equal('not-in-deal-array-here'); + expect(interpretedResponse[0].dealCount).to.equal(0); + + serverResponse.body.adUnits[0].deals = deals; + }); }); describe('interpretVideoResponse', function() { From 03f93f9c227df3330f20e700f12ec9f2c8800c6e Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Sat, 15 Jul 2023 00:34:36 +0300 Subject: [PATCH 47/92] Adkernel: gpp support (#10228) --- modules/adkernelBidAdapter.js | 10 ++++++++-- test/spec/modules/adkernelBidAdapter_spec.js | 9 +++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 64567832dbd5..6382be0751db 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -456,7 +456,9 @@ function makeUser(bidderRequest, fpd) { if (eids) { deepSetValue(user, 'ext.eids', eids); } - if (!isEmpty(user)) { return {user: user}; } + if (!isEmpty(user)) { + return {user: user}; + } } /** @@ -465,13 +467,17 @@ function makeUser(bidderRequest, fpd) { * @returns {{regs: Object} | undefined} */ function makeRegulations(bidderRequest) { - let {gdprConsent, uspConsent} = bidderRequest; + let {gdprConsent, uspConsent, gppConsent} = bidderRequest; let regs = {}; if (gdprConsent) { if (gdprConsent.gdprApplies !== undefined) { deepSetValue(regs, 'regs.ext.gdpr', ~~gdprConsent.gdprApplies); } } + if (gppConsent) { + deepSetValue(regs, 'regs.gpp', gppConsent.gppString); + deepSetValue(regs, 'regs.gpp_sid', gppConsent.applicableSections); + } if (uspConsent) { deepSetValue(regs, 'regs.ext.us_privacy', uspConsent); } diff --git a/test/spec/modules/adkernelBidAdapter_spec.js b/test/spec/modules/adkernelBidAdapter_spec.js index 45498d2734a0..90200a801c54 100644 --- a/test/spec/modules/adkernelBidAdapter_spec.js +++ b/test/spec/modules/adkernelBidAdapter_spec.js @@ -354,11 +354,16 @@ describe('Adkernel adapter', function () { it('should contain gdpr-related information if consent is configured', function () { let [_, bidRequests] = buildRequest([bid1_zone1], - buildBidderRequest('https://example.com/index.html', - {gdprConsent: {gdprApplies: true, consentString: 'test-consent-string', vendorData: {}}, uspConsent: '1YNN'})); + buildBidderRequest('https://example.com/index.html', { + gdprConsent: {gdprApplies: true, consentString: 'test-consent-string', vendorData: {}}, + uspConsent: '1YNN', + gppConsent: {gppString: 'DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA', applicableSections: [2]}} + )); let bidRequest = bidRequests[0]; expect(bidRequest).to.have.property('regs'); expect(bidRequest.regs.ext).to.be.eql({'gdpr': 1, 'us_privacy': '1YNN'}); + expect(bidRequest.regs.gpp).to.be.eql('DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA'); + expect(bidRequest.regs.gpp_sid).to.be.eql([2]); expect(bidRequest).to.have.property('user'); expect(bidRequest.user.ext).to.be.eql({'consent': 'test-consent-string'}); }); From 263cb6ac60e9f311c71dd609e40931a740f754b3 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Mon, 17 Jul 2023 21:34:37 +0300 Subject: [PATCH 48/92] Adkernel Bid Adapter: add adpluto_dsp alias (#10237) --- modules/adkernelAdnBidAdapter.js | 2 +- test/spec/modules/adkernelAdnBidAdapter_spec.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/adkernelAdnBidAdapter.js b/modules/adkernelAdnBidAdapter.js index 4db46aca3c6e..81067a3efcfe 100644 --- a/modules/adkernelAdnBidAdapter.js +++ b/modules/adkernelAdnBidAdapter.js @@ -159,7 +159,7 @@ export const spec = { code: 'adkernelAdn', gvlid: GVLID, supportedMediaTypes: [BANNER, VIDEO], - aliases: ['engagesimply'], + aliases: ['engagesimply', 'adpluto_dsp'], isBidRequestValid: function(bidRequest) { return 'params' in bidRequest && diff --git a/test/spec/modules/adkernelAdnBidAdapter_spec.js b/test/spec/modules/adkernelAdnBidAdapter_spec.js index ff7ed9f145d6..cfee5693cf59 100644 --- a/test/spec/modules/adkernelAdnBidAdapter_spec.js +++ b/test/spec/modules/adkernelAdnBidAdapter_spec.js @@ -426,8 +426,7 @@ describe('AdkernelAdn adapter', function () { describe('adapter configuration', () => { it('should have aliases', () => { - expect(spec.aliases).to.have.lengthOf(1); - expect(spec.aliases[0]).to.be.equal('engagesimply'); + expect(spec.aliases).to.be.an('array'); }); }); }); From be297016c11580c03ec2100b004b32924f758b1a Mon Sep 17 00:00:00 2001 From: prebid-sp <131664583+prebid-sp@users.noreply.github.com> Date: Tue, 18 Jul 2023 00:12:11 +0530 Subject: [PATCH 49/92] Silverpush Bid Adapter : endpoint change (#10234) * added silverpush bid adapter * fixed linting and added new test cases * added check for video feature test cases * publisher id validation fix * removed test url and ip * pub id var add * typo fix * rerun * changed request endpoint * sync with upstream * changed url --------- Co-authored-by: Amit Jangra --- modules/silverpushBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/silverpushBidAdapter.js b/modules/silverpushBidAdapter.js index df81e1443802..5403f3bd88cd 100644 --- a/modules/silverpushBidAdapter.js +++ b/modules/silverpushBidAdapter.js @@ -12,7 +12,7 @@ const bidderConfig = 'sp_pb_ortb'; const bidderVersion = '1.0.0'; const DEFAULT_CURRENCY = 'USD'; -export const REQUEST_URL = 'https://apac.chocolateplatform.com/bidder/?identifier=prebidchoc'; +export const REQUEST_URL = 'https://prebid.chocolateplatform.co/bidder/?identifier=prebidchoc'; export const SP_OUTSTREAM_PLAYER_URL = 'https://xaido.sgp1.cdn.digitaloceanspaces.com/prebid/spoutstream.min.js'; const VIDEO_ORTB_PARAMS = [ @@ -110,7 +110,7 @@ export const CONVERTER = ortbConverter({ bidResponse.meta.paf.content_id = utils.deepAccess(bid, 'ext.paf.content_id'); } - bidResponse = buildVideoVastResponse(bidResponse) + bidResponse = buildVideoVastResponse(bidResponse); bidResponse = buildVideoOutstreamResponse(bidResponse, context) return bidResponse; From 33a5fe20269c2c0a06b6d96e6c67a95bdc8ef4f0 Mon Sep 17 00:00:00 2001 From: John Salis Date: Tue, 18 Jul 2023 09:56:35 -0400 Subject: [PATCH 50/92] Beachfront Bid Adapter : GPP support (#10227) * add gpp support to beachfront adapter * bump beachfront adapter version --------- Co-authored-by: John Salis --- modules/beachfrontBidAdapter.js | 38 ++++++++++++++++--- .../spec/modules/beachfrontBidAdapter_spec.js | 37 +++++++++++++++++- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/modules/beachfrontBidAdapter.js b/modules/beachfrontBidAdapter.js index 37de8e637a96..658fc30b43b2 100644 --- a/modules/beachfrontBidAdapter.js +++ b/modules/beachfrontBidAdapter.js @@ -7,14 +7,15 @@ import { isFn, logWarn, parseSizesInput, - parseUrl + parseUrl, + formatQS } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {Renderer} from '../src/Renderer.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {find, includes} from '../src/polyfill.js'; -const ADAPTER_VERSION = '1.19'; +const ADAPTER_VERSION = '1.20'; const ADAPTER_NAME = 'BFIO_PREBID'; const OUTSTREAM = 'outstream'; const CURRENCY = 'USD'; @@ -22,6 +23,8 @@ const CURRENCY = 'USD'; export const VIDEO_ENDPOINT = 'https://reachms.bfmio.com/bid.json?exchange_id='; export const BANNER_ENDPOINT = 'https://display.bfmio.com/prebid_display'; export const OUTSTREAM_SRC = 'https://player-cdn.beachfrontmedia.com/playerapi/loader/outstream.js'; +export const SYNC_IFRAME_ENDPOINT = 'https://sync.bfmio.com/sync_iframe'; +export const SYNC_IMAGE_ENDPOINT = 'https://sync.bfmio.com/syncb'; export const VIDEO_TARGETING = ['mimes', 'playbackmethod', 'maxduration', 'placement', 'skip', 'skipmin', 'skipafter']; export const DEFAULT_MIMES = ['video/mp4', 'application/javascript']; @@ -152,11 +155,22 @@ export const spec = { } }, - getUserSyncs(syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '') { - let syncs = []; + getUserSyncs(syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '', gppConsent = {}) { let { gdprApplies, consentString = '' } = gdprConsent; + let { gppString = '', applicableSections = [] } = gppConsent; let bannerResponse = find(serverResponses, (res) => isArray(res.body)); + let syncs = []; + let params = { + id: appId, + gdpr: gdprApplies ? 1 : 0, + gc: consentString, + gce: 1, + us_privacy: uspConsent, + gpp: gppString, + gpp_sid: Array.isArray(applicableSections) ? applicableSections.join(',') : '' + }; + if (bannerResponse) { if (syncOptions.iframeEnabled) { bannerResponse.body @@ -171,12 +185,12 @@ export const spec = { } else if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: `https://sync.bfmio.com/sync_iframe?ifg=1&id=${appId}&gdpr=${gdprApplies ? 1 : 0}&gc=${consentString}&gce=1&us_privacy=${uspConsent}` + url: `${SYNC_IFRAME_ENDPOINT}?ifg=1&${formatQS(params)}` }); } else if (syncOptions.pixelEnabled) { syncs.push({ type: 'image', - url: `https://sync.bfmio.com/syncb?pid=144&id=${appId}&gdpr=${gdprApplies ? 1 : 0}&gc=${consentString}&gce=1&us_privacy=${uspConsent}` + url: `${SYNC_IMAGE_ENDPOINT}?pid=144&${formatQS(params)}` }); } @@ -404,6 +418,12 @@ function createVideoRequestData(bid, bidderRequest) { deepSetValue(payload, 'user.ext.consent', consentString); } + if (bidderRequest && bidderRequest.gppConsent) { + let { gppString, applicableSections } = bidderRequest.gppConsent; + deepSetValue(payload, 'regs.gpp', gppString); + deepSetValue(payload, 'regs.gpp_sid', applicableSections); + } + if (bid.schain) { deepSetValue(payload, 'source.ext.schain', bid.schain); } @@ -459,6 +479,12 @@ function createBannerRequestData(bids, bidderRequest) { payload.gdprConsent = consentString; } + if (bidderRequest && bidderRequest.gppConsent) { + let { gppString, applicableSections } = bidderRequest.gppConsent; + payload.gpp = gppString; + payload.gppSid = applicableSections; + } + if (bids[0] && bids[0].schain) { payload.schain = bids[0].schain; } diff --git a/test/spec/modules/beachfrontBidAdapter_spec.js b/test/spec/modules/beachfrontBidAdapter_spec.js index 4e30b822a610..c0994985aae4 100644 --- a/test/spec/modules/beachfrontBidAdapter_spec.js +++ b/test/spec/modules/beachfrontBidAdapter_spec.js @@ -1,7 +1,6 @@ import { expect } from 'chai'; import { spec, VIDEO_ENDPOINT, BANNER_ENDPOINT, OUTSTREAM_SRC, DEFAULT_MIMES } from 'modules/beachfrontBidAdapter.js'; -import { config } from 'src/config.js'; -import { parseUrl, deepAccess } from 'src/utils.js'; +import { parseUrl } from 'src/utils.js'; describe('BeachfrontAdapter', function () { let bidRequests; @@ -296,6 +295,23 @@ describe('BeachfrontAdapter', function () { expect(data.user.ext.consent).to.equal(consentString); }); + it('must add GPP consent data to the request', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { video: {} }; + const gppString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + const applicableSections = [1, 2, 3]; + const bidderRequest = { + gppConsent: { + gppString, + applicableSections + } + }; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const data = requests[0].data; + expect(data.regs.gpp).to.equal(gppString); + expect(data.regs.gpp_sid).to.deep.equal(applicableSections); + }); + it('must add schain data to the request', () => { const schain = { ver: '1.0', @@ -517,6 +533,23 @@ describe('BeachfrontAdapter', function () { expect(data.gdprConsent).to.equal(consentString); }); + it('must add GPP consent data to the request', function () { + const bidRequest = bidRequests[0]; + bidRequest.mediaTypes = { banner: {} }; + const gppString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + const applicableSections = [1, 2, 3]; + const bidderRequest = { + gppConsent: { + gppString, + applicableSections + } + }; + const requests = spec.buildRequests([ bidRequest ], bidderRequest); + const data = requests[0].data; + expect(data.gpp).to.equal(gppString); + expect(data.gppSid).to.deep.equal(applicableSections); + }); + it('must add schain data to the request', () => { const schain = { ver: '1.0', From e68cae90a0e2c978efdbb167c9bff164aa80c803 Mon Sep 17 00:00:00 2001 From: Denis Logachov Date: Tue, 18 Jul 2023 17:15:03 +0300 Subject: [PATCH 51/92] Adkernel Bid Adapter: add adpluto alias (#10235) --- modules/adkernelBidAdapter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/adkernelBidAdapter.js b/modules/adkernelBidAdapter.js index 6382be0751db..7d9ea21802bd 100644 --- a/modules/adkernelBidAdapter.js +++ b/modules/adkernelBidAdapter.js @@ -100,7 +100,8 @@ export const spec = { {code: 'bidbuddy'}, {code: 'adliveconnect'}, {code: 'didnadisplay'}, - {code: 'qortex'} + {code: 'qortex'}, + {code: 'adpluto'} ], supportedMediaTypes: [BANNER, VIDEO, NATIVE], From 68fbc22bbcde420e32030df23c92f8d542828ca8 Mon Sep 17 00:00:00 2001 From: Andrew Slagle <42588549+spotxslagle@users.noreply.github.com> Date: Tue, 18 Jul 2023 08:29:27 -0600 Subject: [PATCH 52/92] Rubicon Bid Adapter: pass on Multiformat Data (#10240) * Add tests for multiformat data * Access multiformat data from mediaTypes * Add formats to rubicon, not params * Fix test --- modules/rubiconBidAdapter.js | 9 +++++++ test/spec/modules/rubiconBidAdapter_spec.js | 29 +++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/modules/rubiconBidAdapter.js b/modules/rubiconBidAdapter.js index afabdaca637e..2f0dcda8411b 100644 --- a/modules/rubiconBidAdapter.js +++ b/modules/rubiconBidAdapter.js @@ -200,6 +200,10 @@ export const converter = ortbConverter({ bidRequest.params.position === 'btf' && (imp.video.pos = 3); delete imp.ext?.prebid?.storedrequest; + if (bidRequest.params.bidonmultiformat === true && bidRequestType.length > 1) { + deepSetValue(imp, 'ext.prebid.bidder.rubicon.formats', bidRequestType); + } + setBidFloors(bidRequest, imp); return imp; @@ -497,6 +501,11 @@ export const spec = { data['rp_hard_floor'] = typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? floorInfo.floor : undefined; } + // Send multiformat data if requested + if (params.bidonmultiformat === true && deepAccess(bidRequest, 'mediaTypes') && Object.keys(bidRequest.mediaTypes).length > 1) { + data['p_formats'] = Object.keys(bidRequest.mediaTypes).join(','); + } + // add p_pos only if specified and valid // For SRA we need to explicitly put empty semi colons so AE treats it as empty, instead of copying the latter value let posMapping = {1: 'atf', 3: 'btf'}; diff --git a/test/spec/modules/rubiconBidAdapter_spec.js b/test/spec/modules/rubiconBidAdapter_spec.js index 317f03752f16..562e956d814c 100644 --- a/test/spec/modules/rubiconBidAdapter_spec.js +++ b/test/spec/modules/rubiconBidAdapter_spec.js @@ -2663,6 +2663,35 @@ describe('the rubicon adapter', function () { expect(pbsRequest.data.imp).to.have.nested.property('[0].native'); expect(fastlanteRequest.url).to.equal('https://fastlane.rubiconproject.com/a/api/fastlane.json'); }); + + it('should include multiformat data in the pbs request', () => { + const bidReq = addNativeToBidRequest(bidderRequest); + // add second mediaType + bidReq.bids[0].mediaTypes = { + ...bidReq.bids[0].mediaTypes, + banner: { + sizes: [[300, 250]] + } + }; + bidReq.bids[0].params.bidonmultiformat = true; + let [pbsRequest, fastlanteRequest] = spec.buildRequests(bidReq.bids, bidReq); + expect(pbsRequest.data.imp[0].ext.prebid.bidder.rubicon.formats).to.deep.equal(['native', 'banner']); + }); + + it('should include multiformat data in the fastlane request', () => { + const bidReq = addNativeToBidRequest(bidderRequest); + // add second mediaType + bidReq.bids[0].mediaTypes = { + ...bidReq.bids[0].mediaTypes, + banner: { + sizes: [[300, 250]] + } + }; + bidReq.bids[0].params.bidonmultiformat = true; + let [pbsRequest, fastlanteRequest] = spec.buildRequests(bidReq.bids, bidReq); + let formatsIncluded = fastlanteRequest.data.indexOf('formats=native%2Cbanner') !== -1; + expect(formatsIncluded).to.equal(true); + }); }); describe('with bidonmultiformat === false', () => { it('should send only banner request because there\'s no params.video', () => { From d7ba17a3c0ae9b1dadeb3ae6d858597e52216c27 Mon Sep 17 00:00:00 2001 From: wojciech-bialy-wpm <67895844+wojciech-bialy-wpm@users.noreply.github.com> Date: Tue, 18 Jul 2023 16:39:26 +0200 Subject: [PATCH 53/92] sspBC Bid Adapter: remove dependence on auctionId, improve notifications (#10222) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update tests for sspBC adapter Update tests for sspBC adapter: - change userSync test (due to tcf param appended in v4.6) - add tests for onBidWon and onTimeout * [sspbc-adapter] 5.3 updates: content-type for notifications * [sspbc-adapter] pass CTA to native bid * [sspbc-5.3] keep pbsize for detected adunits * [maintenance] - remove old test for sspBc bid adaptor * [sspbc-5.3] increment adaptor ver * [sspbc-adapter] maintenance update to sspBCBidAdapter * remove yarn.lock * Delete package-lock.json * remove package-lock.jsonfrom pull request * [sspbc-adapter] send pageViewId in request * [sspbc-adapter] update pageViewId test * [sspbc-adapter] add viewabiility tracker to native ads * [sspbc-adapter] add support for bid.admNative property * [sspbc-adapter] ensure that placement id length is always 3 (improves matching response to request) * [sspbc-adapter] read publisher id and custom ad label, then send them to banner creative * [sspbc-adapter] adlabel and pubid are set as empty strings, if not present in bid response * [sspbc-adapter] jstracker data fix * [sspbc-adapter] jstracker data fix * [sspbc-adapter] send tagid in notifications * [sspbc-adapter] add gvlid to spec; prepare getUserSyncs for iframe + image sync * update remote repo * cleanup of grupawp/prebid master branch * update sspBC adapter to v 5.9 * update tests for sspBC bid adapter --------- Co-authored-by: Wojciech Biały --- modules/sspBCBidAdapter.js | 49 +++++++++++++++++------ test/spec/modules/sspBCBidAdapter_spec.js | 37 +++++++++-------- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/modules/sspBCBidAdapter.js b/modules/sspBCBidAdapter.js index 93ce26e7a48e..70db18c61e19 100644 --- a/modules/sspBCBidAdapter.js +++ b/modules/sspBCBidAdapter.js @@ -12,13 +12,14 @@ const SYNC_URL = 'https://ssp.wp.pl/bidder/usersync'; const NOTIFY_URL = 'https://ssp.wp.pl/bidder/notify'; const GVLID = 676; const TMAX = 450; -const BIDDER_VERSION = '5.8'; +const BIDDER_VERSION = '5.9'; const DEFAULT_CURRENCY = 'PLN'; const W = window; const { navigator } = W; const oneCodeDetection = {}; const adUnitsCalled = {}; const adSizesCalled = {}; +const bidderRequestsMap = {}; const pageView = {}; var consentApiVersion; @@ -93,19 +94,17 @@ const getNotificationPayload = bidData => { const bids = isArray(bidData) ? bidData : [bidData]; if (bids.length > 0) { let result = { - requestId: undefined, siteId: [], slotId: [], tagid: [], } bids.forEach(bid => { - const { adUnitCode, auctionId, cpm, creativeId, meta, params: bidParams, requestId, timeout } = bid; + const { adUnitCode, cpm, creativeId, meta, mediaType, params: bidParams, bidderRequestId, requestId, timeout } = bid; const params = unpackParams(bidParams); // basic notification data const bidBasicData = { - // TODO: fix auctionId leak: https://github.com/prebid/Prebid.js/issues/9781 - requestId: auctionId || result.requestId, + requestId: bidderRequestId || bidderRequestsMap[requestId], timeout: timeout || result.timeout, pvid: pageView.id, } @@ -127,12 +126,14 @@ const getNotificationPayload = bidData => { if (cpm) { // non-empty bid data + const { advertiserDomains = [], networkName, pricepl } = meta; const bidNonEmptyData = { cpm, - cpmpl: meta && meta.pricepl, + cpmpl: pricepl, creativeId, - adomain: meta && meta.advertiserDomains && meta.advertiserDomains[0], - networkName: meta && meta.networkName, + adomain: advertiserDomains[0], + adtype: mediaType, + networkName, } result = { ...result, ...bidNonEmptyData } } @@ -447,10 +448,15 @@ var mapVideo = (slot, videoFromBid) => { }; const mapImpression = slot => { - const { adUnitCode, bidId, params = {}, ortb2Imp = {} } = slot; + const { adUnitCode, bidderRequestId, bidId, params = {}, ortb2Imp = {} } = slot; const { id, siteId, video } = params; const { ext = {} } = ortb2Imp; + /* + store bidId <-> bidderRequestId mapping for bidWon notification + */ + bidderRequestsMap[bidId] = bidderRequestId; + /* check max size for this imp, and check/store number this size was called (for current view) send this info as ext.pbsize @@ -494,15 +500,20 @@ const isNativeAd = bid => { return bid.admNative || (bid.adm && bid.adm.match(xmlTester)); } -const parseNative = (nativeData) => { +const parseNative = (nativeData, adUnitCode) => { const { link = {}, imptrackers: impressionTrackers, jstracker } = nativeData; const { url: clickUrl, clicktrackers: clickTrackers = [] } = link; + const macroReplacer = tracker => tracker.replace(new RegExp('%native_dom_id%', 'g'), adUnitCode); + let javascriptTrackers = isArray(jstracker) ? jstracker : jstracker && [jstracker]; + + // replace known macros in js trackers + javascriptTrackers = javascriptTrackers && javascriptTrackers.map(macroReplacer); const result = { clickUrl, clickTrackers, impressionTrackers, - javascriptTrackers: isArray(jstracker) ? jstracker : jstracker && [jstracker], + javascriptTrackers, }; nativeData.assets.forEach(asset => { @@ -627,6 +638,8 @@ const spec = { return true; }, buildRequests(validBidRequests, bidderRequest) { + logWarn('DEBUG: buildRequests', bidderRequest.auctionId, bidderRequest.bidderRequestId); + // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); @@ -644,7 +657,7 @@ const spec = { const ref = bidderRequest.refererInfo.ref; const payload = { - id: bidderRequest.auctionId, + id: bidderRequest.bidderRequestId, site: { id: siteId ? `${siteId}` : undefined, publisher: publisherId ? { id: publisherId } : undefined, @@ -725,6 +738,7 @@ const spec = { if (bidRequest && site.id && !strIncludes(site.id, 'bidid')) { // found a matching request; add this bid + const { adUnitCode } = bidRequest; // store site data for future notification oneCodeDetection[bidId] = [site.id, site.slot]; @@ -760,7 +774,7 @@ const spec = { // check native object try { const nativeData = serverBid.admNative || JSON.parse(serverBid.adm).native; - bid.native = parseNative(nativeData); + bid.native = parseNative(nativeData, adUnitCode); bid.width = 1; bid.height = 1; } catch (err) { @@ -807,6 +821,15 @@ const spec = { } }, + onBidViewable(bid) { + const payload = getNotificationPayload(bid); + if (payload) { + payload.event = 'bidViewable'; + sendNotification(payload); + return payload; + } + }, + onBidWon(bid) { const payload = getNotificationPayload(bid); if (payload) { diff --git a/test/spec/modules/sspBCBidAdapter_spec.js b/test/spec/modules/sspBCBidAdapter_spec.js index a95f08314b59..71619424e4b7 100644 --- a/test/spec/modules/sspBCBidAdapter_spec.js +++ b/test/spec/modules/sspBCBidAdapter_spec.js @@ -38,7 +38,7 @@ describe('SSPBC adapter', function () { }, auctionId, bidderRequestId, - bidId: auctionId + '1', + bidId: bidderRequestId + '1', transactionId, }, { @@ -60,7 +60,7 @@ describe('SSPBC adapter', function () { }, auctionId, bidderRequestId, - bidId: auctionId + '2', + bidId: bidderRequestId + '2', transactionId, } ]; @@ -83,7 +83,7 @@ describe('SSPBC adapter', function () { ], auctionId, bidderRequestId, - bidId: auctionId + '1', + bidId: bidderRequestId + '1', transactionId, }; const bid_native = { @@ -122,7 +122,7 @@ describe('SSPBC adapter', function () { ], auctionId, bidderRequestId, - bidId: auctionId + '1', + bidId: bidderRequestId + '1', transactionId, }; const bid_video = { @@ -144,7 +144,7 @@ describe('SSPBC adapter', function () { ], auctionId, bidderRequestId, - bidId: auctionId + '1', + bidId: bidderRequestId + '1', transactionId, }; const bids_timeouted = [{ @@ -155,7 +155,7 @@ describe('SSPBC adapter', function () { siteId: '8816', }], auctionId, - bidId: auctionId + '1', + bidId: bidderRequestId + '1', timeout: 100, }, { @@ -166,7 +166,7 @@ describe('SSPBC adapter', function () { siteId: '8816', }], auctionId, - bidId: auctionId + '2', + bidId: bidderRequestId + '2', timeout: 100, } ]; @@ -198,7 +198,7 @@ describe('SSPBC adapter', function () { }, auctionId, bidderRequestId, - bidId: auctionId + '1', + bidId: bidderRequestId + '1', transactionId, }]; const bidRequest = { @@ -293,7 +293,7 @@ describe('SSPBC adapter', function () { }; const serverResponse = { 'body': { - 'id': auctionId, + 'id': bidderRequestId, 'seatbid': [{ 'bid': [{ 'id': '3347324c-6889-46d2-a800-ae78a5214c06', @@ -333,7 +333,7 @@ describe('SSPBC adapter', function () { }; const serverResponseSingle = { 'body': { - 'id': auctionId, + 'id': bidderRequestId, 'seatbid': [{ 'bid': [{ 'id': '3347324c-6889-46d2-a800-ae78a5214c06', @@ -358,11 +358,11 @@ describe('SSPBC adapter', function () { }; const serverResponseOneCode = { 'body': { - 'id': auctionId, + 'id': bidderRequestId, 'seatbid': [{ 'bid': [{ 'id': '3347324c-6889-46d2-a800-ae78a5214c06', - 'impid': 'bidid-' + auctionId + '1', + 'impid': 'bidid-' + bidderRequestId + '1', 'price': 1, 'adid': 'lxHWkB7OnZeso3QiN1N4', 'nurl': '', @@ -385,11 +385,11 @@ describe('SSPBC adapter', function () { }; const serverResponseVideo = { 'body': { - 'id': auctionId, + 'id': bidderRequestId, 'seatbid': [{ 'bid': [{ 'id': '3347324c-6889-46d2-a800-ae78a5214c06', - 'impid': 'bidid-' + auctionId + '1', + 'impid': 'bidid-' + bidderRequestId + '1', 'price': 1, 'adid': 'lxHWkB7OnZeso3QiN1N4', 'nurl': '', @@ -413,11 +413,11 @@ describe('SSPBC adapter', function () { }; const serverResponseNative = { 'body': { - 'id': auctionId, + 'id': bidderRequestId, 'seatbid': [{ 'bid': [{ 'id': '3347324c-6889-46d2-a800-ae78a5214c06', - 'impid': 'bidid-' + auctionId + '1', + 'impid': 'bidid-' + bidderRequestId + '1', 'price': 1, 'adid': 'lxHWkB7OnZeso3QiN1N4', 'nurl': '', @@ -438,7 +438,7 @@ describe('SSPBC adapter', function () { }; const emptyResponse = { 'body': { - 'id': auctionId, + 'id': bidderRequestId, } } return { @@ -696,7 +696,7 @@ describe('SSPBC adapter', function () { let notificationPayload = spec.onBidWon(bid); expect(notificationPayload).to.have.property('event').that.equals('bidWon'); - expect(notificationPayload).to.have.property('requestId').that.equals(bid.auctionId); + expect(notificationPayload).to.have.property('requestId').that.equals(bid.bidderRequestId); expect(notificationPayload).to.have.property('tagid').that.deep.equals([bid.adUnitCode]); expect(notificationPayload).to.have.property('siteId').that.is.an('array'); expect(notificationPayload).to.have.property('slotId').that.is.an('array'); @@ -717,7 +717,6 @@ describe('SSPBC adapter', function () { let notificationPayload = spec.onTimeout(bids_timeouted); expect(notificationPayload).to.have.property('event').that.equals('timeout'); - expect(notificationPayload).to.have.property('requestId').that.equals(bids_timeouted[0].auctionId); expect(notificationPayload).to.have.property('tagid').that.deep.equals([bids_timeouted[0].adUnitCode, bids_timeouted[1].adUnitCode]); }); }); From 0ae65f962d0841e38ef9f0c6eb1193b8dc6cfbeb Mon Sep 17 00:00:00 2001 From: Jason Piros Date: Tue, 18 Jul 2023 07:52:44 -0700 Subject: [PATCH 54/92] Consumable Bid Adapter: add gpp user sync params (#10214) * consumableBidAdapter: add gpp user sync params * consumableBidAdapter: fix spacing error --- modules/consumableBidAdapter.js | 10 +++++++++- test/spec/modules/consumableBidAdapter_spec.js | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/modules/consumableBidAdapter.js b/modules/consumableBidAdapter.js index 4e2a92fb5949..c78ff7cdf511 100644 --- a/modules/consumableBidAdapter.js +++ b/modules/consumableBidAdapter.js @@ -65,6 +65,11 @@ export const spec = { }; } + if (bidderRequest && bidderRequest.gppConsent && bidderRequest.gppConsent.gppString) { + data.gpp = bidderRequest.gppConsent.gppString; + data.gpp_sid = bidderRequest.gppConsent.applicableSections; + } + if (bidderRequest && bidderRequest.uspConsent) { data.ccpa = bidderRequest.uspConsent; } @@ -180,7 +185,7 @@ export const spec = { return bidResponses; }, - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { let syncUrl = 'https://sync.serverbid.com/ss/' + siteId + '.html'; if (syncOptions.iframeEnabled) { @@ -191,6 +196,9 @@ export const spec = { syncUrl = appendUrlParam(syncUrl, `gdpr=0&gdpr_consent=${gdprConsent.consentString}`); } } + if (gppConsent && gppConsent.gppString) { + syncUrl = appendUrlParam(syncUrl, `gpp=${gppConsent.gppString}&gpp_sid=${gppConsent.applicableSections}`); + } if (uspConsent && uspConsent.consentString) { syncUrl = appendUrlParam(syncUrl, `us_privacy=${uspConsent.consentString}`); diff --git a/test/spec/modules/consumableBidAdapter_spec.js b/test/spec/modules/consumableBidAdapter_spec.js index 556dce447b96..deeb8f7100d2 100644 --- a/test/spec/modules/consumableBidAdapter_spec.js +++ b/test/spec/modules/consumableBidAdapter_spec.js @@ -53,6 +53,10 @@ const BIDDER_REQUEST_1 = { consentString: 'consent-test', gdprApplies: false }, + gppConsent: { + applicableSections: [1, 2], + gppString: 'consent-string' + }, refererInfo: { referer: 'http://example.com/page.html', reachedTop: true, @@ -647,6 +651,17 @@ describe('Consumable BidAdapter', function () { expect(opts[0].url).to.equal('https://sync.serverbid.com/ss/730181.html?gdpr=0&gdpr_consent=GDPR_CONSENT_STRING'); }) + it('should return a sync url if iframe syncs are enabled and GPP applies', function () { + let gppConsent = { + applicableSections: [1, 2], + gppString: 'GPP_CONSENT_STRING' + } + let opts = spec.getUserSyncs(syncOptions, [AD_SERVER_RESPONSE], {}, {}, gppConsent); + + expect(opts.length).to.equal(1); + expect(opts[0].url).to.equal('https://sync.serverbid.com/ss/730181.html?gpp=GPP_CONSENT_STRING&gpp_sid=1,2'); + }) + it('should return a sync url if iframe syncs are enabled and USP applies', function () { let uspConsent = { consentString: 'USP_CONSENT_STRING', From 7bfe87d3f2b0d9357b12dd1c418d7236e8b65814 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jul 2023 07:51:12 -0700 Subject: [PATCH 55/92] Bump word-wrap from 1.2.3 to 1.2.4 (#10242) Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4. - [Release notes](https://github.com/jonschlinkert/word-wrap/releases) - [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4) --- updated-dependencies: - dependency-name: word-wrap dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index aa1294553ed9..a043b290499b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "prebid.js", - "version": "8.4.0-pre", + "version": "8.5.0-pre", "license": "Apache-2.0", "dependencies": { "@babel/core": "^7.16.7", @@ -24904,9 +24904,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -44558,9 +44558,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", + "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", "dev": true }, "wordwrap": { From baca408f14c95dc2680054439f79f31975348de0 Mon Sep 17 00:00:00 2001 From: hzchen98 Date: Wed, 19 Jul 2023 17:02:42 +0200 Subject: [PATCH 56/92] SSMas Bid adapter : Initial release (#10171) * add ssmas adapter * ssmas adapter * privacy regulations * user sync * user sync consent * user sync params * set tagId * fix bid adapter doc * clean --- modules/ssmasBidAdapter.js | 126 +++++++++++ modules/ssmasBidAdapter.md | 35 ++++ test/spec/modules/ssmasBidAdapter_spec.js | 244 ++++++++++++++++++++++ 3 files changed, 405 insertions(+) create mode 100644 modules/ssmasBidAdapter.js create mode 100644 modules/ssmasBidAdapter.md create mode 100644 test/spec/modules/ssmasBidAdapter_spec.js diff --git a/modules/ssmasBidAdapter.js b/modules/ssmasBidAdapter.js new file mode 100644 index 000000000000..9ed1ec90dd82 --- /dev/null +++ b/modules/ssmasBidAdapter.js @@ -0,0 +1,126 @@ +import { BANNER } from '../src/mediaTypes.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { triggerPixel, deepSetValue } from '../src/utils.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js'; +import {config} from '../src/config.js'; + +export const SSMAS_CODE = 'ssmas'; +const SSMAS_SERVER = 'ads.ssmas.com'; +export const SSMAS_ENDPOINT = `http://${SSMAS_SERVER}/ortb`; +const SYNC_URL = `http://sync.ssmas.com/user_sync`; +export const SSMAS_REQUEST_METHOD = 'POST'; +const GDPR_VENDOR_ID = 1183; + +export const ssmasOrtbConverter = ortbConverter({ + context: { + netRevenue: true, + ttl: 300, + mediaType: BANNER, + }, + imp(buildImp, bidRequest, context) { + const imp = buildImp(bidRequest, context); + deepSetValue(imp, 'ext.placementId', bidRequest.params.placementId); + return imp; + }, +}); + +export const spec = { + code: SSMAS_CODE, + supportedMediaTypes: [BANNER], + gvlid: GDPR_VENDOR_ID, + + isBidRequestValid: (bid) => { + return !!bid.params.placementId && !!bid.bidId && bid.bidder === SSMAS_CODE; + }, + + buildRequests: (bidRequests, bidderRequest) => { + const data = ssmasOrtbConverter.toORTB({ bidRequests, bidderRequest }); + + const options = { + contentType: 'application/json', + withCredentials: false, + }; + + data.imp && data.imp.forEach(imp => { + if (imp.ext && imp.ext.placementId) { + imp.tagId = imp.ext.placementId; + } + }); + + data.regs = data.regs || {}; + data.regs.ext = data.regs.ext || {}; + + if (bidderRequest.gdprConsent) { + data.regs.ext.consent = bidderRequest.gdprConsent.consentString; + data.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + } + if (bidderRequest.uspConsent) { + data.regs.ext.consent = bidderRequest.uspConsent.consentString; + data.regs.ext.ccpa = 1; + } + if (config.getConfig('coppa') === true) { + data.regs.coppa = 1; + } + + return [ + { + method: SSMAS_REQUEST_METHOD, + url: SSMAS_ENDPOINT, + data, + options, + }, + ]; + }, + + interpretResponse: (serverResponse, bidRequest) => { + const bids = ssmasOrtbConverter.fromORTB({ + response: serverResponse.body, + request: bidRequest.data, + }).bids; + + return bids.filter((bid) => { + return bid.cpm > 0; + }); + }, + + onBidWon: (bid) => { + if (bid.burl) { + triggerPixel(bid.burl); + } + }, + + getUserSyncs: ( + syncOptions, + serverResponses, + gdprConsent, + uspConsent + ) => { + const syncs = []; + + let params = ['pbjs=1']; + + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params.push(`gdpr=${Boolean(gdprConsent.gdprApplies)}&gdpr_consent=${ + gdprConsent.consentString + }`); + } else { + params.push(`gdpr_consent=${gdprConsent.consentString}`); + } + } + + if (uspConsent && uspConsent.consentString) { + params.push(`ccpa_consent=${uspConsent.consentString}`); + } + + if (syncOptions.pixelEnabled && serverResponses.length > 0) { + syncs.push({ + type: 'image', + url: `${SYNC_URL}?${params.join('&')}` + }); + } + return syncs; + }, +}; + +registerBidder(spec); diff --git a/modules/ssmasBidAdapter.md b/modules/ssmasBidAdapter.md new file mode 100644 index 000000000000..1b15764a54dd --- /dev/null +++ b/modules/ssmasBidAdapter.md @@ -0,0 +1,35 @@ +# Overview + +Module Name: SSMas Bidder Adapter +Module Type: Bidder Adapter +Maintainer: hzchen.work@gmail.com + +# Description + +Module that connects to Sem Seo & Mas header bidding endpoint to fetch bids. +Supports Banner +Supported currencies: EUR + +Required parameters: +- placement id + +# Test Parameters +``` +var adUnits = [ + // Banner adUnit + { + code: 'banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'ssmas', + params: { + placementId: "10336" + } + }] + } +]; +``` diff --git a/test/spec/modules/ssmasBidAdapter_spec.js b/test/spec/modules/ssmasBidAdapter_spec.js new file mode 100644 index 000000000000..26c6f60da4b6 --- /dev/null +++ b/test/spec/modules/ssmasBidAdapter_spec.js @@ -0,0 +1,244 @@ +import { expect } from 'chai'; +import { spec, SSMAS_CODE, SSMAS_ENDPOINT, SSMAS_REQUEST_METHOD } from 'modules/ssmasBidAdapter.js'; +import {newBidder} from 'src/adapters/bidderFactory.js'; +import * as utils from 'src/utils.js'; + +describe('ssmasBidAdapter', function () { + const bid = { + bidder: SSMAS_CODE, + adUnitCode: 'adunit-code', + sizes: [[300, 250]], + bidId: '30b31c1838de1e', + bidderRequestId: '22edbae2733bf6', + auctionId: '1d1a030790a475', + params: { + placementId: '1' + } + }; + + const bidderRequest = { + 'bidderCode': SSMAS_CODE, + 'auctionId': 'd912faa2-174f-4636-b755-7396a0a964d8', + 'bidderRequestId': '109db5a5f5c6788', + 'bids': [ + bid + ], + 'auctionStart': 1684799653734, + 'timeout': 20000, + 'metrics': {}, + 'ortb2': { + 'site': { + 'domain': 'localhost:9999', + 'publisher': { + 'domain': 'localhost:9999' + }, + 'page': 'http://localhost:9999/integrationExamples/noadserver/basic_noadserver.html', + 'ref': 'http://localhost:9999/integrationExamples/noadserver/' + }, + 'device': { + 'w': 1536, + 'h': 711, + 'dnt': 0, + 'ua': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0', + 'language': 'es' + } + }, + 'start': 1684799653737 + }; + + describe('Build Requests', () => { + it('Check bid request', function () { + const request = spec.buildRequests([bid], bidderRequest); + expect(request[0].method).to.equal(SSMAS_REQUEST_METHOD); + expect(request[0].url).to.equal(SSMAS_ENDPOINT); + }); + }); + + describe('register adapter functions', () => { + const adapter = newBidder(spec); + it('is registered', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function () { + it('validate bid request building', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + + it('test bad bid request', function () { + // empty bid + expect(spec.isBidRequestValid({bidId: '', params: {}})).to.be.false; + + // empty bidId + bid.bidId = ''; + expect(spec.isBidRequestValid(bid)).to.be.false; + + // empty placementId + bid.bidId = '1231'; + bid.params.placementId = ''; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + + it('check bid request bidder is Sem Seo & Mas', function() { + const invalidBid = { + ...bid, bidder: 'invalidBidder' + }; + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('interpretResponse', function () { + let bidOrtbResponse = { + 'id': 'aa02e2fe-56d9-4713-88f9-d8672ceae8ab', + 'seatbid': [ + { + 'bid': [ + { + 'id': '0001', + 'impid': '3919400af0b73e8', + 'price': 7.01, + 'adid': null, + 'nurl': null, + 'adm': '', + 'adomain': [ + 'ssmas.com' + ], + 'iurl': null, + 'cid': null, + 'crid': '3547894', + 'attr': [], + 'api': 0, + 'protocol': 0, + 'dealid': null, + 'h': 600, + 'w': 300, + 'cat': null, + 'ext': null, + 'builder': { + 'id': '0001', + 'adid': null, + 'impid': '3919400af0b73e8', + 'adomainList': [ + 'ssmas.com' + ], + 'attrList': [] + }, + 'adomainList': [ + 'ssmas.com' + ], + 'attrList': [] + } + ], + 'seat': null, + 'group': 0 + } + ], + 'bidid': '408731cc-c018-4976-bfc6-89f9c61e97a0', + 'cur': 'EUR', + 'nbr': -1 + }; + let bidResponse = { + 'mediaType': 'banner', + 'ad': '', + 'requestId': '37c658fe8ba57b', + 'seatBidId': '0001', + 'cpm': 10, + 'currency': 'EUR', + 'width': 300, + 'height': 250, + 'dealId': null, + 'creative_id': '3547894', + 'creativeId': '3547894', + 'ttl': 30, + 'netRevenue': true, + 'meta': { + 'advertiserDomains': [ + 'ssmas.com' + ] + } + }; + let bidRequest = { + 'imp': [ + { + 'ext': { + 'tid': '937db9c3-c22d-4454-b786-fcad76a349e5', + 'data': { + 'pbadslot': 'test-div' + } + }, + 'id': '3919400af0b73e8', + 'banner': { + 'topframe': 1, + 'format': [ + { + 'w': 300, + 'h': 600 + } + ] + } + }, + { + 'ext': { + 'tid': '0c0d3d1b-0ad0-4786-896d-24c15fc6531d', + 'data': { + 'pbadslot': 'test-div2' + } + }, + 'id': '3919400af0b73e8', + 'banner': { + 'topframe': 1, + 'format': [ + { + 'w': 300, + 'h': 600 + } + ] + } + } + ], + 'site': { + 'domain': 'localhost:9999', + 'publisher': { + 'domain': 'localhost:9999' + }, + 'page': 'http://localhost:9999/integrationExamples/noadserver/basic_noadserver.html', + 'ref': 'http://localhost:9999/integrationExamples/noadserver/', + 'id': 1, + 'ext': { + 'placementId': 13144370 + } + }, + 'device': { + 'w': 1536, + 'h': 711, + 'dnt': 0, + 'ua': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0', + 'language': 'es' + }, + 'id': '8cc2f4b0-084d-4f40-acfa-5bec2023b1ab', + 'test': 0, + 'tmax': 20000, + 'source': { + 'tid': '8cc2f4b0-084d-4f40-acfa-5bec2023b1ab' + } + } + }); + + describe('test onBidWon function', function () { + beforeEach(function() { + sinon.stub(utils, 'triggerPixel'); + }); + afterEach(function() { + utils.triggerPixel.restore(); + }); + it('exists and is a function', () => { + expect(spec.onBidWon).to.exist.and.to.be.a('function'); + }); + it('should return nothing', function () { + var response = spec.onBidWon({}); + expect(response).to.be.an('undefined') + expect(utils.triggerPixel.called).to.equal(false); + }); + }); +}); From 8a3869c6cd37fcf103401183ecd900a9ba28c06f Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 19 Jul 2023 10:25:11 -0700 Subject: [PATCH 57/92] Remove outdated video example (#10248) --- .../gpt/pbjs_video_adUnit.html | 109 ------------------ 1 file changed, 109 deletions(-) delete mode 100644 integrationExamples/gpt/pbjs_video_adUnit.html diff --git a/integrationExamples/gpt/pbjs_video_adUnit.html b/integrationExamples/gpt/pbjs_video_adUnit.html deleted file mode 100644 index 080ca9be1426..000000000000 --- a/integrationExamples/gpt/pbjs_video_adUnit.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - Prebid.js video adUnit example - - - - - - - - - - - -
- -
- - - - - From a318783b5ca750f7d89fec0596a6645a7b0c4a65 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 19 Jul 2023 11:25:08 -0700 Subject: [PATCH 58/92] consentManagementGPP: do not cancel auctions when CMP is not version 1.0 (#10247) --- modules/consentManagementGpp.js | 11 ++++++++++- test/spec/modules/consentManagementGpp_spec.js | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/modules/consentManagementGpp.js b/modules/consentManagementGpp.js index 88851adfda51..393b7f8fe4e4 100644 --- a/modules/consentManagementGpp.js +++ b/modules/consentManagementGpp.js @@ -71,11 +71,20 @@ export function lookupIabConsent({onSuccess, onError}, mkClient = cmpClient) { : 'Detected GPP CMP is outside the current iframe where Prebid.js is located, calling it now...'; logInfo(startupMsg); + let versionMismatch = false; + cmp({ command: 'addEventListener', callback: function (evt) { - if (evt) { + if (evt && !versionMismatch) { logInfo(`Received a ${(cmp.isDirect ? 'direct' : 'postmsg')} response from GPP CMP for event`, evt); + const cmpVer = evt?.pingData?.gppVersion; + if (cmpVer != null && cmpVer !== '1.0') { + logWarn(`Unsupported GPP CMP version: ${cmpVer}. Continuing auction without consent`); + versionMismatch = true; + onSuccess(storeConsentData()); + return; + } if (evt.eventName === 'sectionChange' || evt.pingData.cmpStatus === 'loaded') { cmp({command: 'getGPPData'}).then((gppData) => { logInfo(`Received a ${cmp.isDirect ? 'direct' : 'postmsg'} response from GPP CMP for getGPPData`, gppData); diff --git a/test/spec/modules/consentManagementGpp_spec.js b/test/spec/modules/consentManagementGpp_spec.js index 17f8f6f6eac1..37776c15cea3 100644 --- a/test/spec/modules/consentManagementGpp_spec.js +++ b/test/spec/modules/consentManagementGpp_spec.js @@ -157,6 +157,22 @@ describe('consentManagementGpp', function () { expect(res.sectionData).to.eql(sectionData); }) }); + + it('continues with no consent when CMP version is not 1.0', () => { + const pm = runLookup(); + mockCmpEvent({ + eventName: 'listenerRegistered', + pingData: { + gppVersion: '1.1' + } + }); + return pm.then((res) => { + sinon.assert.match(res, { + gppString: undefined, + applicableSections: [] + }) + }) + }) }) describe('static consent string setConsentConfig value', () => { From 7305de56cdc848dc238e71a9937e824222ad83f9 Mon Sep 17 00:00:00 2001 From: vrtcal-dev <50931150+vrtcal-dev@users.noreply.github.com> Date: Wed, 19 Jul 2023 14:19:35 -0500 Subject: [PATCH 59/92] GDPR global vendor ID specification update; client side UA,IP,LANGUAGE,CATEGORY, and PAGE population update (#10252) Co-authored-by: Ubuntu --- modules/vrtcalBidAdapter.js | 16 ++++++++++------ test/spec/modules/vrtcalBidAdapter_spec.js | 7 ++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/modules/vrtcalBidAdapter.js b/modules/vrtcalBidAdapter.js index ff7839d0e02a..21870e3218ad 100644 --- a/modules/vrtcalBidAdapter.js +++ b/modules/vrtcalBidAdapter.js @@ -1,11 +1,14 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; import {ajax} from '../src/ajax.js'; -import {isFn, isPlainObject} from '../src/utils.js'; import { config } from '../src/config.js'; +import {deepAccess, isFn, isPlainObject} from '../src/utils.js'; + +const GVLID = 706; export const spec = { code: 'vrtcal', + gvlid: GVLID, supportedMediaTypes: [BANNER], isBidRequestValid: function (bid) { return true; @@ -62,13 +65,14 @@ export const spec = { site: { id: 'VRTCAL_FILLED', name: 'VRTCAL_FILLED', - cat: ['VRTCAL_FILLED'], - domain: decodeURIComponent(window.location.href).replace('https://', '').replace('http://', '').split('/')[0] - + cat: deepAccess(bid, 'ortb2.site.cat', []), + domain: decodeURIComponent(window.location.href).replace('https://', '').replace('http://', '').split('/')[0], + page: bid.refererInfo.page }, device: { - ua: 'VRTCAL_FILLED', - ip: 'VRTCAL_FILLED' + language: navigator.language, + ua: navigator.userAgent, + ip: deepAccess(bid, 'params.bidOverride.device.ip') || deepAccess(bid, 'params.ext.ip') || undefined }, regs: { coppa: coppa, diff --git a/test/spec/modules/vrtcalBidAdapter_spec.js b/test/spec/modules/vrtcalBidAdapter_spec.js index 609d5d0a5f77..cc4dc0a3882e 100644 --- a/test/spec/modules/vrtcalBidAdapter_spec.js +++ b/test/spec/modules/vrtcalBidAdapter_spec.js @@ -29,7 +29,12 @@ describe('vrtcalBidAdapter', function () { 'bidderRequestId': 'br0001', 'auctionId': 'auction0001', 'userIdAsEids': {}, - timeout: 435 + timeout: 435, + + refererInfo: { + page: 'page' + } + } ]; From 27917d05e6e31b410909f80d0423539f5664c213 Mon Sep 17 00:00:00 2001 From: Rupesh Lakhani <35333377+AskRupert-DM@users.noreply.github.com> Date: Wed, 19 Jul 2023 20:21:25 +0100 Subject: [PATCH 60/92] Ozone Bid Adapter : updates 2.9.0 and tid (#10238) * Ozone: Adapter Updates - 2.9.0 - changes required for PB 8 https://github.com/prebid/Prebid.js/issues/8573 - removed transaction IDs - added support for batching/limiting to 10 impression objects in a single request with a config switch - onpage and also in the adapter. Off by default. In the config use ozone.batchRequests: true - added our own id as required by ozone server & also openrtb spec - adding newspass as an alias - update the spec tests - adding support for gptPreAuction adapter (this is on by default when included in the build) * Update ozoneBidAdapter.js fixes per feedback * Add files via upload removing alias support (added in by accident) * Add files via upload updated tests following removal of aliases --- modules/ozoneBidAdapter.js | 83 ++++++++++++++++++----- modules/ozoneBidAdapter.md | 4 +- test/spec/modules/ozoneBidAdapter_spec.js | 28 +++++++- 3 files changed, 95 insertions(+), 20 deletions(-) diff --git a/modules/ozoneBidAdapter.js b/modules/ozoneBidAdapter.js index 1b725ce3a050..970c7d49fb97 100644 --- a/modules/ozoneBidAdapter.js +++ b/modules/ozoneBidAdapter.js @@ -7,7 +7,8 @@ import { isArray, contains, mergeDeep, - parseUrl + parseUrl, + generateUUID } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; @@ -16,12 +17,12 @@ import {getPriceBucketString} from '../src/cpmBucketManager.js'; import { Renderer } from '../src/Renderer.js'; import {getRefererInfo} from '../src/refererDetection.js'; const BIDDER_CODE = 'ozone'; -const ORIGIN = 'https://elb.the-ozone-project.com'; // applies only to auction & cookie +const ORIGIN = 'https://elb.the-ozone-project.com' // applies only to auction & cookie const AUCTIONURI = '/openrtb2/auction'; const OZONECOOKIESYNC = '/static/load-cookie.html'; const OZONE_RENDERER_URL = 'https://prebid.the-ozone-project.com/ozone-renderer.js'; const ORIGIN_DEV = 'https://test.ozpr.net'; -const OZONEVERSION = '2.8.0'; +const OZONEVERSION = '2.9.0'; export const spec = { gvlid: 524, aliases: [{code: 'lmc', gvlid: 524}], @@ -36,7 +37,8 @@ export const spec = { 'keyPrefix': 'oz', 'auctionUrl': ORIGIN + AUCTIONURI, 'cookieSyncUrl': ORIGIN + OZONECOOKIESYNC, - 'rendererUrl': OZONE_RENDERER_URL + 'rendererUrl': OZONE_RENDERER_URL, + 'batchRequests': false /* you can change this to true OR override it in the config: config.ozone.batchRequests */ }, loadWhitelabelData(bid) { if (this.propertyBag.whitelabel) { return; } @@ -73,6 +75,9 @@ export const spec = { this.propertyBag.whitelabel.auctionUrl = bidderConfig.endpointOverride.auctionUrl; } } + if (bidderConfig.hasOwnProperty('batchRequests')) { + this.propertyBag.whitelabel.batchRequests = bidderConfig.batchRequests; + } try { if (arr.hasOwnProperty('auction') && arr.auction === 'dev') { logInfo('GET: auction=dev'); @@ -94,11 +99,14 @@ export const spec = { getRendererUrl() { return this.propertyBag.whitelabel.rendererUrl; }, + isBatchRequests() { + return this.propertyBag.whitelabel.batchRequests; + }, isBidRequestValid(bid) { this.loadWhitelabelData(bid); logInfo('isBidRequestValid : ', config.getConfig(), bid); let adUnitCode = bid.adUnitCode; // adunit[n].code - let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED'; + let err1 = 'VALIDATION FAILED : missing {param} : siteId, placementId and publisherId are REQUIRED' if (!(bid.params.hasOwnProperty('placementId'))) { logError(err1.replace('{param}', 'placementId'), adUnitCode); return false; @@ -199,7 +207,7 @@ export const spec = { let placementId = placementIdOverrideFromGetParam || this.getPlacementId(ozoneBidRequest); // prefer to use a valid override param, else the bidRequest placement Id obj.id = ozoneBidRequest.bidId; // this causes an error if we change it to something else, even if you update the bidRequest object: "WARNING: Bidder ozone made bid for unknown request ID: mb7953.859498327448. Ignoring." obj.tagid = placementId; - let parsed = parseUrl(getRefererInfo().page); + let parsed = parseUrl(this.getRefererInfo().page); obj.secure = parsed.protocol === 'https' ? 1 : 0; let arrBannerSizes = []; if (!ozoneBidRequest.hasOwnProperty('mediaTypes')) { @@ -262,9 +270,7 @@ export const spec = { obj.placementId = placementId; deepSetValue(obj, 'ext.prebid', {'storedrequest': {'id': placementId}}); obj.ext[whitelabelBidder] = {}; - // TODO: fix auctionId/transactionID leak: https://github.com/prebid/Prebid.js/issues/9781 obj.ext[whitelabelBidder].adUnitCode = ozoneBidRequest.adUnitCode; // eg. 'mpu' - obj.ext[whitelabelBidder].transactionId = ozoneBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit if (ozoneBidRequest.params.hasOwnProperty('customData')) { obj.ext[whitelabelBidder].customData = ozoneBidRequest.params.customData; } @@ -291,6 +297,10 @@ export const spec = { if (!schain && deepAccess(ozoneBidRequest, 'schain')) { schain = ozoneBidRequest.schain; } + let gpid = deepAccess(ozoneBidRequest, 'ortb2Imp.ext.gpid'); + if (gpid) { + deepSetValue(obj, 'ext.gpid', gpid); + } return obj; }); let extObj = {}; @@ -326,7 +336,7 @@ export const spec = { let userExtEids = deepAccess(validBidRequests, '0.userIdAsEids', []); // generate the UserIDs in the correct format for UserId module ozoneRequest.site = { 'publisher': {'id': htmlParams.publisherId}, - 'page': getRefererInfo().page, + 'page': this.getRefererInfo().page, 'id': htmlParams.siteId }; ozoneRequest.test = config.getConfig('debug') ? 1 : 0; @@ -355,13 +365,33 @@ export const spec = { if (config.getConfig('coppa') === true) { deepSetValue(ozoneRequest, 'regs.coppa', 1); } + let ozUuid = generateUUID(); + if (this.isBatchRequests()) { + logInfo('going to batch the requests'); + let arrRet = []; // return an array of objects containing data describing max 10 bids + for (let i = 0; i < tosendtags.length; i += 10) { + ozoneRequest.id = ozUuid; // Unique ID of the bid request, provided by the exchange. (REQUIRED) + ozoneRequest.imp = tosendtags.slice(i, i + 10); + ozoneRequest.ext = extObj; + deepSetValue(ozoneRequest, 'user.ext.eids', userExtEids); + if (ozoneRequest.imp.length > 0) { + arrRet.push({ + method: 'POST', + url: this.getAuctionUrl(), + data: JSON.stringify(ozoneRequest), + bidderRequest: bidderRequest + }); + } + } + logInfo('batch request going to return : ', arrRet); + return arrRet; + } + logInfo('requests will not be batched.'); if (singleRequest) { logInfo('buildRequests starting to generate response for a single request'); - ozoneRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange. - ozoneRequest.auctionId = bidderRequest.auctionId; // not sure if this should be here? + ozoneRequest.id = ozUuid; // Unique ID of the bid request, provided by the exchange. (REQUIRED) ozoneRequest.imp = tosendtags; ozoneRequest.ext = extObj; - deepSetValue(ozoneRequest, 'source.tid', bidderRequest.auctionId);// RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges). deepSetValue(ozoneRequest, 'user.ext.eids', userExtEids); var ret = { method: 'POST', @@ -377,12 +407,9 @@ export const spec = { let arrRet = tosendtags.map(imp => { logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); let ozoneRequestSingle = Object.assign({}, ozoneRequest); - imp.ext[whitelabelBidder].pageAuctionId = bidderRequest['auctionId']; // make a note in the ext object of what the original auctionId was, in the bidderRequest object - ozoneRequestSingle.id = imp.ext[whitelabelBidder].transactionId; // Unique ID of the bid request, provided by the exchange. - ozoneRequestSingle.auctionId = imp.ext[whitelabelBidder].transactionId; // not sure if this should be here? + ozoneRequestSingle.id = generateUUID(); // Unique ID of the bid request, provided by the exchange. (REQUIRED) ozoneRequestSingle.imp = [imp]; ozoneRequestSingle.ext = extObj; - deepSetValue(ozoneRequestSingle, 'source.tid', bidderRequest.auctionId);// RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges). deepSetValue(ozoneRequestSingle, 'user.ext.eids', userExtEids); logInfo('buildRequests RequestSingle (for non-single) = ', ozoneRequestSingle); return { @@ -424,6 +451,7 @@ export const spec = { logInfo(`interpretResponse time: ${startTime} . Time between buildRequests done and interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); logInfo(`serverResponse, request`, JSON.parse(JSON.stringify(serverResponse)), JSON.parse(JSON.stringify(request))); serverResponse = serverResponse.body || {}; + let aucId = serverResponse.id; // this will be correct for single requests and non-single if (!serverResponse.hasOwnProperty('seatbid')) { return []; } @@ -518,7 +546,7 @@ export const spec = { } } let {seat: winningSeat, bid: winningBid} = ozoneGetWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); - adserverTargeting[whitelabelPrefix + '_auc_id'] = String(request.bidderRequest.auctionId); + adserverTargeting[whitelabelPrefix + '_auc_id'] = String(aucId); // was request.bidderRequest.auctionId adserverTargeting[whitelabelPrefix + '_winner'] = String(winningSeat); adserverTargeting[whitelabelPrefix + '_bid'] = 'true'; adserverTargeting[whitelabelPrefix + '_cache_id'] = deepAccess(thisBid, 'ext.prebid.targeting.hb_cache_id', 'no-id'); @@ -686,10 +714,29 @@ export const spec = { return null; }, getGetParametersAsObject() { - let parsed = parseUrl(getRefererInfo().page); + let parsed = parseUrl(this.getRefererInfo().location); logInfo('getGetParametersAsObject found:', parsed.search); return parsed.search; }, + getRefererInfo() { + if (getRefererInfo().hasOwnProperty('location')) { + logInfo('FOUND location on getRefererInfo OK (prebid >= 7); will use getRefererInfo for location & page'); + return getRefererInfo(); + } else { + logInfo('DID NOT FIND location on getRefererInfo (prebid < 7); will use legacy code that ALWAYS worked reliably to get location & page ;-)'); + try { + return { + page: top.location.href, + location: top.location.href + }; + } catch (e) { + return { + page: window.location.href, + location: window.location.href + }; + } + } + }, blockTheRequest() { let ozRequest = this.getWhitelabelConfigItem('ozone.oz_request'); if (typeof ozRequest == 'boolean' && !ozRequest) { diff --git a/modules/ozoneBidAdapter.md b/modules/ozoneBidAdapter.md index 6f4cf752f22d..9787e0692837 100644 --- a/modules/ozoneBidAdapter.md +++ b/modules/ozoneBidAdapter.md @@ -73,6 +73,8 @@ adUnits = [{ }]; ``` + +``` //Instream Video adUnit adUnits = [{ @@ -108,4 +110,4 @@ adUnits = [{ } }] }; -``` +``` \ No newline at end of file diff --git a/test/spec/modules/ozoneBidAdapter_spec.js b/test/spec/modules/ozoneBidAdapter_spec.js index 4c7e330b2374..64b345c5d9c9 100644 --- a/test/spec/modules/ozoneBidAdapter_spec.js +++ b/test/spec/modules/ozoneBidAdapter_spec.js @@ -6,7 +6,6 @@ import {getGranularityKeyName, getGranularityObject} from '../../../modules/ozon import * as utils from '../../../src/utils.js'; const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction'; const BIDDER_CODE = 'ozone'; - var validBidRequests = [ { adUnitCode: 'div-gpt-ad-1460505748561-0', @@ -2049,6 +2048,27 @@ describe('ozone Adapter', function () { expect(data.imp[0].ext.ozone.customData).to.be.an('array'); expect(data.imp[0].ext.ozone.customData[0].targeting.oztestmode).to.equal('mytestvalue_123'); }); + it('should pass gpid to auction if it is present (gptPreAuction adapter sets this)', function () { + var specMock = utils.deepClone(spec); + let br = JSON.parse(JSON.stringify(validBidRequests)); + utils.deepSetValue(br[0], 'ortb2Imp.ext.gpid', '/22037345/projectozone'); + const request = specMock.buildRequests(br, validBidderRequest); + const data = JSON.parse(request.data); + expect(data.imp[0].ext.gpid).to.equal('/22037345/projectozone'); + }); + it('should batch into 10s if config is set', function () { + config.setConfig({ozone: {'batchRequests': true}}); + var specMock = utils.deepClone(spec); + let arrReq = []; + for (let i = 0; i < 25; i++) { + let b = validBidRequests[0]; + b.adUnitCode += i; + arrReq.push(b); + } + const request = specMock.buildRequests(arrReq, validBidderRequest); + expect(request.length).to.equal(3); + config.resetConfig(); + }); it('should use GET values auction=dev & cookiesync=dev if set', function() { var specMock = utils.deepClone(spec); specMock.getGetParametersAsObject = function() { @@ -2429,6 +2449,12 @@ describe('ozone Adapter', function () { const result = spec.interpretResponse(validres, request); expect(utils.deepAccess(result[0].adserverTargeting, 'oz_ozappnexus_sid')).to.equal(result[0].cid); }); + it('should add oz_auc_id (response id value)', function () { + const request = spec.buildRequests(validBidRequestsMulti, validBidderRequest); + let validres = JSON.parse(JSON.stringify(validBidResponse1adWith2Bidders)); + const result = spec.interpretResponse(validres, request); + expect(utils.deepAccess(result[0].adserverTargeting, 'oz_auc_id')).to.equal(validBidResponse1adWith2Bidders.body.id); + }); it('should add unique adId values to each bid', function() { const request = spec.buildRequests(validBidRequests, validBidderRequest); let validres = JSON.parse(JSON.stringify(validResponse2BidsSameAdunit)); From bc260d72faf42f25101fddd9059c481a0f080669 Mon Sep 17 00:00:00 2001 From: AdmixerTech <35560933+AdmixerTech@users.noreply.github.com> Date: Wed, 19 Jul 2023 22:26:09 +0300 Subject: [PATCH 61/92] Geolocation RTD module: initial release (#10012) * Update README.md update * add geolocation rtd provider * move config to prams remove auction delay move geo to ortb2Fragments.global.device.geo * tslint * geolocation cmp allowance * lint fixes * test fixes * test fixes * testing * testing 2 * testing 3 * testing 4 --------- Co-authored-by: Daria Boyko Co-authored-by: dariaboyko <73283727+dariaboyko@users.noreply.github.com> --- modules/geolocationRtdProvider.js | 65 ++++++++++ .../modules/geolocationRtdProvider_spec.js | 119 ++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 modules/geolocationRtdProvider.js create mode 100644 test/spec/modules/geolocationRtdProvider_spec.js diff --git a/modules/geolocationRtdProvider.js b/modules/geolocationRtdProvider.js new file mode 100644 index 000000000000..e2161c7bbfb8 --- /dev/null +++ b/modules/geolocationRtdProvider.js @@ -0,0 +1,65 @@ +import {submodule} from '../src/hook.js'; +import {isFn, logError, deepAccess, deepSetValue, logInfo, logWarn} from '../src/utils.js'; +import { ACTIVITY_TRANSMIT_PRECISE_GEO } from '../src/activities/activities.js'; +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; +import { isActivityAllowed } from '../src/activities/rules.js'; +import { activityParams } from '../src/activities/activityParams.js'; + +let permissionsAvailable = true; +let geolocation; +function getGeolocationData(requestBidsObject, onDone, providerConfig, userConsent) { + let done = false; + if (!permissionsAvailable) { + logWarn('permission for geolocation receiving was denied'); + return complete() + }; + if (!isActivityAllowed(ACTIVITY_TRANSMIT_PRECISE_GEO, activityParams(MODULE_TYPE_RTD, 'geolocation'))) { + logWarn('permission for geolocation receiving was denied by CMP'); + return complete() + }; + const requestPermission = deepAccess(providerConfig, 'params.requestPermission') === true; + const waitForIt = providerConfig.waitForIt; + navigator.permissions.query({ + name: 'geolocation', + }).then(permission => { + if (permission.state !== 'granted' && !requestPermission) return complete(); + navigator.geolocation.getCurrentPosition(geo => { + geolocation = geo; + complete(); + }); + }); + if (!waitForIt) complete(); + function complete() { + if (done) return; + done = true; + if (geolocation) { + deepSetValue(requestBidsObject, 'ortb2Fragments.global.device.geo', { + lat: geolocation.coords.latitude, + lon: geolocation.coords.longitude, + lastfix: geolocation.timestamp, + type: 1 + }); + logInfo('geolocation was successfully received ', requestBidsObject.ortb2Fragments.global.device.geo) + } + onDone(); + } +} +function init(moduleConfig) { + geolocation = void 0; + if (!isFn(navigator?.permissions?.query) || !isFn(navigator?.geolocation?.getCurrentPosition || !navigator?.permissions?.query)) { + logError('geolocation is not defined'); + permissionsAvailable = false; + } else { + permissionsAvailable = true; + } + return permissionsAvailable; +} +export const geolocationSubmodule = { + name: 'geolocation', + getBidRequestData: getGeolocationData, + init: init, +}; +function registerSubModule() { + submodule('realTimeData', geolocationSubmodule); +} +registerSubModule(); diff --git a/test/spec/modules/geolocationRtdProvider_spec.js b/test/spec/modules/geolocationRtdProvider_spec.js new file mode 100644 index 000000000000..4cbfaa35881f --- /dev/null +++ b/test/spec/modules/geolocationRtdProvider_spec.js @@ -0,0 +1,119 @@ +import {config as _config, config} from 'src/config.js'; +import { expect } from 'chai'; +import * as events from 'src/events.js'; +import * as prebidGlobal from 'src/prebidGlobal.js'; +import { geolocationSubmodule } from 'modules/geolocationRtdProvider.js'; +import * as utils from 'src/utils.js'; +import {getGlobal} from 'src/prebidGlobal.js'; +import 'src/prebid.js'; + +describe('Geolocation RTD Provider', function () { + let sandbox; + let placeholder; + const pbjs = getGlobal(); + const adUnit = { + code: 'ad-slot-1', + mediaTypes: { + banner: { + sizes: [ [300, 250] ] + } + }, + bids: [ + { + bidder: 'fake' + } + ] + }; + const providerConfig = {name: 'geolocation', waitForIt: true}; + const rtdConfig = {realTimeData: {auctionDelay: 200, dataProviders: [providerConfig]}} + describe('Geolocation not supported', function() { + beforeEach(function() { + sandbox = sinon.sandbox.create(); + }); + afterEach(function() { + sandbox.restore(); + sandbox = undefined; + }); + it('init should return false', function () { + if (navigator.permissions) { sandbox.stub(navigator.permissions, 'query').value(undefined); expect(geolocationSubmodule.init({})).is.false; } + }); + }); + describe('Geolocation supported', function() { + beforeEach(function() { + sandbox = sinon.sandbox.create(); + // placeholder = createDiv(); + // append(); + const __config = {}; + sandbox.stub(_config, 'getConfig').callsFake(function (path) { + return utils.deepAccess(__config, path); + }); + sandbox.stub(_config, 'setConfig').callsFake(function (obj) { + utils.mergeDeep(__config, obj); + }); + }); + afterEach(function() { + sandbox.restore(); + // remove(); + sandbox = undefined; + placeholder = undefined; + pbjs.removeAdUnit(); + }); + it('init should return true', function () { + navigator.permissions && expect(geolocationSubmodule.init({})).is.true; + }); + it('should set geolocation. (request all)', function(done) { + navigator.permissions && sandbox.stub(navigator.permissions, 'query').value(() => Promise.resolve({ + state: 'granted', + })); + navigator.geolocation && sandbox.stub(navigator.geolocation, 'getCurrentPosition').value((cb) => { + // eslint-disable-next-line standard/no-callback-literal + cb({coords: {latitude: 1, longitude: 1}}); + }); + pbjs.addAdUnits([utils.deepClone(adUnit)]); + config.setConfig(rtdConfig); + const onDone = sandbox.stub(); + const requestBidObject = {}; + geolocationSubmodule.init({}); + geolocationSubmodule.getBidRequestData( + requestBidObject, + onDone, + providerConfig + ); + expect(pbjs.adUnits.length).to.eq(1); + setTimeout(function() { + // expect(requestBidObject?.ortb2Fragments?.global.device.geo?.type).to.eq(1); + done(); + }, 300); + }); + it('should call done due timeout', function(done) { + // sandbox.stub(navigator.permissions, 'query').value(() => new Promise(() => {})); + // sandbox.stub(navigator.geolocation, 'getCurrentPosition').value((cb) => {}); + config.setConfig(rtdConfig); + // remove(); + const onDone = sandbox.stub(); + const requestBidObject = {adUnits: [utils.deepClone(adUnit)]}; + geolocationSubmodule.init({}); + geolocationSubmodule.getBidRequestData( + requestBidObject, + onDone, + {...providerConfig, test: 1} + ); + setTimeout(function() { + sinon.assert.calledOnce(onDone); + expect(requestBidObject).to.not.have.property('ortb2Fragments.global.device.geo'); + done(); + }, 300); + }); + }); + // function createDiv() { + // const div = document.createElement('div'); + // div.id = adUnit.code; + // return div; + // } + // function append() { + // placeholder && document.body.appendChild(placeholder); + // } + // function remove() { + // placeholder && placeholder.parentElement && placeholder.parentElement.removeChild(placeholder); + // } +}); From 115098b318f73e5d12a40a50201ce79166b34eb3 Mon Sep 17 00:00:00 2001 From: Monis Qadri Date: Thu, 20 Jul 2023 00:56:40 +0530 Subject: [PATCH 62/92] Added gpp test cases for medianetBidAdapter_spec.js (#10223) Co-authored-by: monis.q --- test/spec/modules/medianetBidAdapter_spec.js | 117 +++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/test/spec/modules/medianetBidAdapter_spec.js b/test/spec/modules/medianetBidAdapter_spec.js index bb90eded230f..e29e259ebd7a 100644 --- a/test/spec/modules/medianetBidAdapter_spec.js +++ b/test/spec/modules/medianetBidAdapter_spec.js @@ -1311,6 +1311,118 @@ let VALID_BID_REQUEST = [{ } }], 'tmax': 3000, + }, + VALID_BIDDER_REQUEST_WITH_GPP_IN_ORTB2 = { + ortb2: { + regs: { + gpp: 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', + gpp_sid: [5, 7] + } + }, + 'timeout': 3000, + refererInfo: { + referer: 'http://media.net/prebidtest', + stack: ['http://media.net/prebidtest'], + page: 'http://media.net/page', + domain: 'media.net', + topmostLocation: 'http://media.net/topmost', + reachedTop: true + } + }, + VALID_PAYLOAD_FOR_GPP_ORTB2 = { + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest', + 'topMostLocation': 'http://media.net/topmost', + 'isTop': true + }, + 'ext': { + 'customer_id': 'customer_id', + 'prebid_version': $$PREBID_GLOBAL$$.version, + 'gdpr_applies': false, + 'usp_applies': false, + 'coppa_applies': false, + 'screen': { + 'w': 1000, + 'h': 1000 + } + }, + 'id': 'aafabfd0-28c0-4ac0-aa09-99689e88b81d', + 'imp': [{ + 'id': '28f8f8130a583e', + 'transactionId': '277b631f-92f5-4844-8b19-ea13c095d3f1', + ortb2Imp: VALID_BID_REQUEST[0].ortb2Imp, + 'ext': { + 'dfp_id': 'div-gpt-ad-1460505748561-0', + 'visibility': 1, + 'viewability': 1, + 'coordinates': { + 'top_left': { + x: 50, + y: 50 + }, + 'bottom_right': { + x: 100, + y: 100 + } + }, + 'display_count': 1 + }, + 'banner': [{ + 'w': 300, + 'h': 250 + }], + 'all': { + 'cid': 'customer_id', + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest', + 'isTop': true + } + } + }, { + 'id': '3f97ca71b1e5c2', + 'transactionId': 'c52a5c62-3c2b-4b90-9ff8-ec1487754822', + ortb2Imp: VALID_BID_REQUEST[1].ortb2Imp, + 'ext': { + 'dfp_id': 'div-gpt-ad-1460505748561-123', + 'visibility': 1, + 'viewability': 1, + 'coordinates': { + 'top_left': { + x: 50, + y: 50 + }, + 'bottom_right': { + x: 100, + y: 100 + } + }, + 'display_count': 1 + }, + 'banner': [{ + 'w': 300, + 'h': 251 + }], + 'all': { + 'cid': 'customer_id', + 'site': { + 'page': 'http://media.net/prebidtest', + 'domain': 'media.net', + 'ref': 'http://media.net/prebidtest', + 'isTop': true + } + } + }], + 'ortb2': { + 'regs': { + 'gpp': 'DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN', + 'gpp_sid': [5, 7], + } + }, + 'tmax': config.getConfig('bidderTimeout') }; describe('Media.net bid adapter', function () { let sandbox; @@ -1393,6 +1505,11 @@ describe('Media.net bid adapter', function () { expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_FOR_GDPR); }); + it('should have gpp params in ortb2', function () { + let bidReq = spec.buildRequests(VALID_BID_REQUEST, VALID_BIDDER_REQUEST_WITH_GPP_IN_ORTB2); + expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_FOR_GPP_ORTB2); + }); + it('should parse params for native request', function () { let bidReq = spec.buildRequests(VALID_NATIVE_BID_REQUEST, VALID_AUCTIONDATA); expect(JSON.parse(bidReq.data)).to.deep.equal(VALID_PAYLOAD_NATIVE); From 7759968aec79d7e0b9088574aa7d5b6ccef3cd42 Mon Sep 17 00:00:00 2001 From: Sagoru Date: Thu, 20 Jul 2023 04:31:30 +0900 Subject: [PATCH 63/92] A1Media RTD module : initial release (#10141) * Create A1Media RTD Submodule * update description * add import expect property from chai for test * fix fake timestamp for test * Fix for Requested Changes - add reason for load external script to Documentation - fix 1st party id and segment place - update rtdModule by alphabetical * update load script to non-dynamic - add segtax to user.data --------- Co-authored-by: ChangsikChoi <> --- modules/.submodules.json | 1 + modules/a1MediaRtdProvider.js | 92 ++++++++++++++++ modules/a1MediaRtdProvider.md | 46 ++++++++ src/adloader.js | 3 +- test/spec/modules/a1MediaRtdProvider_spec.js | 105 +++++++++++++++++++ 5 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 modules/a1MediaRtdProvider.js create mode 100644 modules/a1MediaRtdProvider.md create mode 100644 test/spec/modules/a1MediaRtdProvider_spec.js diff --git a/modules/.submodules.json b/modules/.submodules.json index b45fb7f23037..b36856580843 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -54,6 +54,7 @@ ], "rtdModule": [ "1plusXRtdProvider", + "a1MediaRtdProvider", "aaxBlockmeterRtdProvider", "airgridRtdProvider", "akamaiDapRtdProvider", diff --git a/modules/a1MediaRtdProvider.js b/modules/a1MediaRtdProvider.js new file mode 100644 index 000000000000..eca6b501ec15 --- /dev/null +++ b/modules/a1MediaRtdProvider.js @@ -0,0 +1,92 @@ +import { MODULE_TYPE_RTD } from '../src/activities/modules.js'; +import { loadExternalScript } from '../src/adloader.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { isEmptyStr, mergeDeep } from '../src/utils.js'; + +const REAL_TIME_MODULE = 'realTimeData'; +const MODULE_NAME = 'a1Media'; +const SCRIPT_URL = 'https://linkback.contentsfeed.com/src'; +export const A1_SEG_KEY = '__a1tg'; +export const A1_AUD_KEY = 'a1gid'; + +export const storage = getStorageManager({moduleType: MODULE_TYPE_RTD, moduleName: MODULE_NAME}); + +/** @type {RtdSubmodule} */ +export const subModuleObj = { + name: MODULE_NAME, + init: init, + getBidRequestData: alterBidRequests, +}; + +export function getStorageData(key) { + let storageValue = ''; + if (storage.getDataFromLocalStorage(key)) { + storageValue = storage.getDataFromLocalStorage(key); + } else if (storage.getCookie(key)) { + storageValue = storage.getCookie(key); + } + return storageValue; +} + +function loadLbScript(tagname) { + const linkback = window.linkback = window.linkback || {}; + if (!linkback.l) { + linkback.l = true; + + const scriptUrl = `${SCRIPT_URL}/${tagname}`; + loadExternalScript(scriptUrl, MODULE_NAME); + } +} + +function init(config, userConsent) { + const tagId = config.params.tagId; + if (tagId && !isEmptyStr(tagId)) { + loadLbScript(config.params.tagId); + return true; + } + if (!isEmptyStr(getStorageData(A1_SEG_KEY))) { + return true; + } + return false; +} + +function alterBidRequests(reqBidsConfigObj, callback, config, userConsent) { + const a1seg = getStorageData(A1_SEG_KEY); + const a1gid = getStorageData(A1_AUD_KEY); + + const a1UserSegData = { + name: 'a1mediagroup.com', + ext: { + segtax: 900 + }, + segment: a1seg.split(',').map(x => ({id: x})) + }; + + const a1UserEid = { + source: 'a1mediagroup.com', + uids: [ + { + id: a1gid, + atype: 1 + } + ] + }; + + const a1Ortb2 = { + user: { + data: [ + a1UserSegData + ], + ext: { + eids: [ + a1UserEid + ] + } + } + }; + mergeDeep(reqBidsConfigObj.ortb2Fragments.global, a1Ortb2); + callback(); +} + +submodule(REAL_TIME_MODULE, subModuleObj); diff --git a/modules/a1MediaRtdProvider.md b/modules/a1MediaRtdProvider.md new file mode 100644 index 000000000000..ab2077ebbbb3 --- /dev/null +++ b/modules/a1MediaRtdProvider.md @@ -0,0 +1,46 @@ +# Overview + +Module Name: A1Media Rtd Provider +Module Type: Rtd Provider +Maintainer: dev@a1mediagroup.co.kr + +# Description + +This module loads external code using the passed parameter (params.tagId). + +The A1Media RTD module loads A1Media script for obtains user segments, and provides user segment data to bid-requests.
+to get user segments, you will need a1media script customized for site. + +To use this module, you’ll need to work with [A1MediaGroup](https://www.a1mediagroup.com/) to get an account and receive instructions on how to set up your pages and ad server. + +Contact dev@a1mediagroup.co.kr for information. + +### Integration + +1) Build the A1Media RTD Module into the Prebid.js package with: + +``` +gulp build --modules=a1MediaRtdProvider,... +``` + +2) Use `setConfig` to instruct Prebid.js to initilaize the A1Media RTD module, as specified below. + +### Configuration + +```javascript +pbjs.setConfig({ + realTimeData: { + auctionDelay: 1000, + dataProviders: [ + { + name: "a1Media", + waitForIt: true, + params: { + // 'tagId' is unique value for each account. + tagId: 'lb4test' + } + } + ] + } +}); +``` diff --git a/src/adloader.js b/src/adloader.js index 3aae348d1060..fb4aa44e8728 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -24,7 +24,8 @@ const _approvedLoadExternalJSList = [ 'confiant', 'arcspan', 'airgrid', - 'clean.io' + 'clean.io', + 'a1Media', ] /** diff --git a/test/spec/modules/a1MediaRtdProvider_spec.js b/test/spec/modules/a1MediaRtdProvider_spec.js new file mode 100644 index 000000000000..2630e83fcf5e --- /dev/null +++ b/test/spec/modules/a1MediaRtdProvider_spec.js @@ -0,0 +1,105 @@ +import { subModuleObj } from 'modules/a1MediaRtdProvider.js'; +import { loadExternalScript } from '../../../src/adloader.js'; +import { A1_AUD_KEY, A1_SEG_KEY, getStorageData, storage } from '../../../modules/a1MediaRtdProvider.js'; +import { expect } from 'chai'; + +const configWithParams = { + name: 'a1Media', + waitForIt: true, + params: { + tagId: 'lb4test.min.js', + }, +}; +const configWithoutParams = { + name: 'a1Media', + waitForIt: true, + params: { + }, +}; + +const reqBidsConfigObj = { + ortb2Fragments: { + global: {} + } +}; +const a1TestOrtbObj = { + user: { + data: [ + { + name: 'a1mediagroup.com', + ext: { + segtax: 900 + }, + segment: [{id: 'test'}] + } + ], + ext: { + eids: [ + { + source: 'a1mediagroup.com', + uids: [ + { + id: 'tester', + atype: 1 + } + ] + } + ] + } + } +}; + +describe('a1MediaRtdProvider', function() { + describe('init', function() { + describe('initialize with expected params', function() { + it('successfully initialize with load script', function() { + expect(subModuleObj.init(configWithParams)).to.be.true; + expect(window.linkback.l).to.be.true; + expect(loadExternalScript.called).to.be.true; + expect(loadExternalScript.args[0][0]).to.deep.equal('https://linkback.contentsfeed.com/src/lb4test.min.js'); + }) + + it('successfully initialize but script is already exist', function() { + const linkback = { l: true }; + + expect(subModuleObj.init(configWithParams)).to.be.true; + expect(loadExternalScript.called).to.be.false; + }) + }); + + describe('initialize without expected params', function() { + afterEach(function() { + storage.setCookie(A1_SEG_KEY, '', 0); + }) + + it('successfully initialize when publisher side segment is exist in cookie', function() { + storage.setCookie(A1_SEG_KEY, 'test'); + expect(subModuleObj.init(configWithoutParams)).to.be.true; + expect(getStorageData(A1_SEG_KEY)).to.not.equal(''); + }) + it('fails initalize publisher sied segment is not exist', function() { + expect(subModuleObj.init(configWithoutParams)).to.be.false; + expect(getStorageData(A1_SEG_KEY)).to.equal(''); + }) + }) + }); + + describe('alterBidRequests', function() { + const callback = sinon.stub(); + + before(function() { + storage.setCookie(A1_SEG_KEY, 'test'); + storage.setDataInLocalStorage(A1_AUD_KEY, 'tester'); + }) + after(function() { + storage.setCookie(A1_SEG_KEY, '', 0); + storage.removeDataFromLocalStorage(A1_AUD_KEY); + }) + + it('alterBidRequests', function() { + subModuleObj.getBidRequestData(reqBidsConfigObj, callback); + expect(reqBidsConfigObj.ortb2Fragments.global).to.deep.include(a1TestOrtbObj); + expect(callback.calledOnce).to.be.true; + }) + }); +}) From 7a2a6e8cbddb8fb5908a9c8ff4bfb171113277b5 Mon Sep 17 00:00:00 2001 From: abazylewicz-id5 <106807984+abazylewicz-id5@users.noreply.github.com> Date: Wed, 19 Jul 2023 21:32:16 +0200 Subject: [PATCH 64/92] ID5 User Id module - accept partner in a string format (#10233) --- modules/id5IdSystem.js | 26 +++++++++++++++++++------- test/spec/modules/id5IdSystem_spec.js | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index eff1f1366494..e12aea5f8d1a 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -108,7 +108,7 @@ export const id5IdSubmodule = { * @returns {IdResponse|undefined} */ getId(submoduleConfig, consentData, cacheIdObj) { - if (!hasRequiredConfig(submoduleConfig)) { + if (!validateConfig(submoduleConfig)) { return undefined; } @@ -142,14 +142,12 @@ export const id5IdSubmodule = { * @return {(IdResponse|function(callback:function))} A response object that contains id and/or callback. */ extendId(config, consentData, cacheIdObj) { - hasRequiredConfig(config); - if (!hasWriteConsentToLocalStorage(consentData)) { logInfo(LOG_PREFIX + 'No consent given for ID5 local storage writing, skipping nb increment.') return cacheIdObj; } - const partnerId = (config && config.params && config.params.partner) || 0; + const partnerId = validateConfig(config) ? config.params.partner : 0; incrementNb(partnerId); logInfo(LOG_PREFIX + 'using cached ID', cacheIdObj); @@ -307,9 +305,23 @@ class IdFetchFlow { } } -function hasRequiredConfig(config) { - if (!config || !config.params || !config.params.partner || typeof config.params.partner !== 'number') { - logError(LOG_PREFIX + 'partner required to be defined as a number'); +function validateConfig(config) { + if (!config || !config.params || !config.params.partner) { + logError(LOG_PREFIX + 'partner required to be defined'); + return false; + } + + const partner = config.params.partner + if (typeof partner === 'string' || partner instanceof String) { + let parsedPartnerId = parseInt(partner); + if (isNaN(parsedPartnerId) || parsedPartnerId < 0) { + logError(LOG_PREFIX + 'partner required to be a number or a String parsable to a positive integer'); + return false; + } else { + config.params.partner = parsedPartnerId; + } + } else if (typeof partner !== 'number') { + logError(LOG_PREFIX + 'partner required to be a number or a String parsable to a positive integer'); return false; } diff --git a/test/spec/modules/id5IdSystem_spec.js b/test/spec/modules/id5IdSystem_spec.js index 9db26ed91cea..2d81c9b7b8d0 100644 --- a/test/spec/modules/id5IdSystem_spec.js +++ b/test/spec/modules/id5IdSystem_spec.js @@ -407,6 +407,24 @@ describe('ID5 ID System', function () { }) }); + it('should call the ID5 server for config with partner id being a string', function () { + let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) + let id5FetchConfig = getId5FetchConfig(); + id5FetchConfig.params.partner = '173'; + let submoduleResponse = callSubmoduleGetId(id5FetchConfig, undefined, undefined); + + return xhrServerMock.expectConfigRequest() + .then(configRequest => { + let requestBody = JSON.parse(configRequest.requestBody) + expect(requestBody.params.partner).is.eq(173) + return xhrServerMock.respondWithConfigAndExpectNext(configRequest) + }) + .then(fetchRequest => { + fetchRequest.respond(200, responseHeader, JSON.stringify(ID5_JSON_RESPONSE)); + return submoduleResponse + }) + }); + it('should call the ID5 server for config with overridden url', function () { let xhrServerMock = new XhrServerMock(sinon.createFakeServer()) let id5FetchConfig = getId5FetchConfig(); From dd4b25e9a4ad329ed66f00c3c791236b898a7233 Mon Sep 17 00:00:00 2001 From: Justas Pupelis Date: Thu, 20 Jul 2023 14:41:02 +0300 Subject: [PATCH 65/92] Adf Bid Adapter getFloor update (#10211) * adfBidAdapter getFloor update * syntax fix * Better manner for tests --- modules/adfBidAdapter.js | 5 ++- test/spec/modules/adfBidAdapter_spec.js | 47 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index 5c4b03c3bb2e..e5b40f661763 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -69,8 +69,11 @@ export const spec = { bid.netRevenue = pt; const floorInfo = bid.getFloor ? bid.getFloor({ - currency: currency || 'USD' + currency: currency || 'USD', + size: '*', + mediaType: '*' }) : {}; + const bidfloor = floorInfo.floor; const bidfloorcur = floorInfo.currency; const { mid, inv, mname } = bid.params; diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index 88f595b6c005..c1acff522c0d 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -1,4 +1,5 @@ // jshint esversion: 6, es3: false, node: true +/* eslint-disable no-console */ import { assert } from 'chai'; import { spec } from 'modules/adfBidAdapter.js'; import { config } from 'src/config.js'; @@ -446,6 +447,52 @@ describe('Adf adapter', function () { }); }); + it('should add correct params to getFloor', function () { + let result; + let mediaTypes = { video: { + playerSize: [ 100, 200 ] + } }; + const expectedFloors = [ 1, 1.3, 0.5 ]; + config.setConfig({ currency: { adServerCurrency: 'DKK' } }); + let validBidRequests = expectedFloors.map(getBidWithFloorTest); + getRequestImps(validBidRequests); + assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' }); + + mediaTypes = { banner: { + sizes: [ [100, 200], [300, 400] ] + }}; + validBidRequests = expectedFloors.map(getBidWithFloorTest); + getRequestImps(validBidRequests); + + assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' }); + + mediaTypes = { native: {} }; + validBidRequests = expectedFloors.map(getBidWithFloorTest); + getRequestImps(validBidRequests); + + assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' }); + + mediaTypes = {}; + validBidRequests = expectedFloors.map(getBidWithFloorTest); + getRequestImps(validBidRequests); + + assert.deepEqual(result, { currency: 'DKK', size: '*', mediaType: '*' }); + + function getBidWithFloorTest(floor) { + return { + params: { mid: 1 }, + mediaTypes: mediaTypes, + getFloor: (args) => { + result = args; + return { + currency: 'DKK', + floor + }; + } + }; + } + }); + function getBidWithFloor(floor) { return { params: { mid: 1 }, From b9f12a4b4ab29f49370baa8b01b344fcb1b0fcb7 Mon Sep 17 00:00:00 2001 From: Patrick Loughrey Date: Thu, 20 Jul 2023 09:00:17 -0400 Subject: [PATCH 66/92] Triplelift Bid Adapter: update `ext.ortb2` (#10159) * Add ortb2 changes * Added unit test * Linting requirements --- modules/tripleliftBidAdapter.js | 5 +++ .../spec/modules/tripleliftBidAdapter_spec.js | 40 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/modules/tripleliftBidAdapter.js b/modules/tripleliftBidAdapter.js index 2072bd5ff496..a68bef5584c4 100644 --- a/modules/tripleliftBidAdapter.js +++ b/modules/tripleliftBidAdapter.js @@ -189,6 +189,11 @@ function _buildPostBody(bidRequests, bidderRequest) { if (bidderRequest?.ortb2?.regs?.gpp) { data.regs = Object.assign({}, bidderRequest.ortb2.regs); } + + if (bidderRequest?.ortb2) { + data.ext.ortb2 = Object.assign({}, bidderRequest.ortb2); + } + return data; } diff --git a/test/spec/modules/tripleliftBidAdapter_spec.js b/test/spec/modules/tripleliftBidAdapter_spec.js index 70f6b43eef6c..3874e1f93286 100644 --- a/test/spec/modules/tripleliftBidAdapter_spec.js +++ b/test/spec/modules/tripleliftBidAdapter_spec.js @@ -1130,6 +1130,46 @@ describe('triplelift adapter', function () { expect(logErrorSpy.calledOnce).to.equal(true); }); + it('should add ortb2 ext object if global fpd is available', function() { + const ortb2 = { + site: { + domain: 'page.example.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'], + page: 'https://page.example.com/here.html', + }, + user: { + yob: 1985, + gender: 'm', + keywords: 'a,b', + data: [ + { + name: 'dataprovider.com', + ext: { segtax: 4 }, + segment: [{ id: '1' }] + } + ], + ext: { + data: { + registered: true, + interests: ['cars'] + } + } + } + }; + + const request = tripleliftAdapterSpec.buildRequests(bidRequests, {...bidderRequest, ortb2}); + const { data: payload } = request; + expect(payload.ext.ortb2).to.exist; + expect(payload.ext.ortb2.site).to.deep.equal({ + domain: 'page.example.com', + cat: ['IAB2'], + sectioncat: ['IAB2-2'], + pagecat: ['IAB2-2'], + page: 'https://page.example.com/here.html', + }); + }); it('should send global config fpd if kvps are available', function() { const sens = null; const category = ['news', 'weather', 'hurricane']; From 84c631f17f39b46a911f3f2b66e38d07fbf5ec6f Mon Sep 17 00:00:00 2001 From: Timothy Ace Date: Thu, 20 Jul 2023 09:25:38 -0400 Subject: [PATCH 67/92] iMDS Bid Adapter: support gpp (#10150) * Backport of pull request #8526 that was missing from rename of synacormedia adapter to imds * CAPT-787: Add gpp support to imds adapter. * CAPT-787: Change src= to pbjs/x.y.z to be consistent with prebid-server format. * CAPT-787: Add gppsid parameter to usersync to mirror the prebid-server instance * Revert "Backport of pull request #8526 that was missing from rename of synacormedia adapter to imds" This reverts commit 6619ed90aee942d6a085a6a99c177574428ab586. * CAPT-787: imds adapter: Resolve "undefined" passed in gpp string for usersync --------- Co-authored-by: Timothy M. Ace --- modules/imdsBidAdapter.js | 52 +++++++--- test/spec/modules/imdsBidAdapter_spec.js | 115 +++++++++++++++++++---- 2 files changed, 136 insertions(+), 31 deletions(-) diff --git a/modules/imdsBidAdapter.js b/modules/imdsBidAdapter.js index 545a0bd1ac30..d6f3df944091 100644 --- a/modules/imdsBidAdapter.js +++ b/modules/imdsBidAdapter.js @@ -1,6 +1,6 @@ 'use strict'; -import {deepAccess, deepSetValue, getAdUnitSizes, isFn, isPlainObject, logWarn} from '../src/utils.js'; +import {deepAccess, deepSetValue, getAdUnitSizes, isFn, isPlainObject, logWarn, mergeDeep} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; import {includes} from '../src/polyfill.js'; @@ -8,7 +8,8 @@ import {config} from '../src/config.js'; const BID_SCHEME = 'https://'; const BID_DOMAIN = 'technoratimedia.com'; -const USER_SYNC_HOST = 'https://ad-cdn.technoratimedia.com'; +const USER_SYNC_IFRAME_URL = 'https://ad-cdn.technoratimedia.com/html/usersync.html'; +const USER_SYNC_PIXEL_URL = 'https://sync.technoratimedia.com/services'; const VIDEO_PARAMS = [ 'minduration', 'maxduration', 'startdelay', 'placement', 'linearity', 'mimes', 'protocols', 'api' ]; const BLOCKED_AD_SIZES = [ '1x1', @@ -38,11 +39,11 @@ export const spec = { return; } const refererInfo = bidderRequest.refererInfo; - const openRtbBidRequest = { + // start with some defaults, overridden by anything set in ortb2, if provided. + const openRtbBidRequest = mergeDeep({ id: bidderRequest.bidderRequestId, site: { - // TODO: does the fallback make sense here? - domain: refererInfo.domain || location.hostname, + domain: refererInfo.domain, page: refererInfo.page, ref: refererInfo.ref }, @@ -50,7 +51,7 @@ export const spec = { ua: navigator.userAgent }, imp: [] - }; + }, bidderRequest.ortb2 || {}); const tmax = bidderRequest.timeout; if (tmax) { @@ -101,9 +102,17 @@ export const spec = { } }); - // CCPA - if (bidderRequest.uspConsent) { - deepSetValue(openRtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent); + // Move us_privacy from regs.ext to regs if there isn't already a us_privacy in regs + if (openRtbBidRequest.regs?.ext?.us_privacy && !openRtbBidRequest.regs?.us_privacy) { + deepSetValue(openRtbBidRequest, 'regs.us_privacy', openRtbBidRequest.regs.ext.us_privacy); + } + + // Remove regs.ext.us_privacy + if (openRtbBidRequest.regs?.ext?.us_privacy) { + delete openRtbBidRequest.regs.ext.us_privacy; + if (Object.keys(openRtbBidRequest.regs.ext).length < 1) { + delete openRtbBidRequest.regs.ext; + } } // User ID @@ -117,7 +126,7 @@ export const spec = { if (openRtbBidRequest.imp.length && seatId) { return { method: 'POST', - url: `${BID_SCHEME}${seatId}.${BID_DOMAIN}/openrtb/bids/${seatId}?src=$$REPO_AND_VERSION$$`, + url: `${BID_SCHEME}${seatId}.${BID_DOMAIN}/openrtb/bids/${seatId}?src=pbjs%2F$prebid.version$`, data: openRtbBidRequest, options: { contentType: 'application/json', @@ -294,16 +303,31 @@ export const spec = { } return bids; }, - getUserSyncs: function (syncOptions, serverResponses) { + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { const syncs = []; + const queryParams = ['src=pbjs%2F$prebid.version$']; + if (gdprConsent) { + queryParams.push(`gdpr=${Number(gdprConsent.gdprApplies && 1)}&consent=${encodeURIComponent(gdprConsent.consentString || '')}`); + } + if (uspConsent) { + queryParams.push('us_privacy=' + encodeURIComponent(uspConsent)); + } + if (gppConsent) { + queryParams.push('gpp=' + encodeURIComponent(gppConsent.gppString || '') + '&gppsid=' + encodeURIComponent((gppConsent.applicableSections || []).join(','))); + } + if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: `${USER_SYNC_HOST}/html/usersync.html?src=$$REPO_AND_VERSION$$` + url: `${USER_SYNC_IFRAME_URL}?${queryParams.join('&')}` + }); + } else if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'pixel', + url: `${USER_SYNC_PIXEL_URL}?srv=cs&${queryParams.join('&')}` }); - } else { - logWarn('IMDS: Please enable iframe based user sync.'); } + return syncs; } }; diff --git a/test/spec/modules/imdsBidAdapter_spec.js b/test/spec/modules/imdsBidAdapter_spec.js index ce04fabe02b0..7d808a2528f2 100644 --- a/test/spec/modules/imdsBidAdapter_spec.js +++ b/test/spec/modules/imdsBidAdapter_spec.js @@ -192,12 +192,45 @@ describe('imdsBidAdapter ', function () { timeout: 3000 }; - let bidderRequestWithCCPA = { + let bidderRequestWithUSPInExt = { bidderRequestId: 'xyz123', refererInfo: { referer: 'https://test.com/foo/bar' }, - uspConsent: '1YYY' + ortb2: { + regs: { + ext: { + us_privacy: '1YYY' + } + } + } + }; + + let bidderRequestWithUSPInRegs = { + bidderRequestId: 'xyz123', + refererInfo: { + referer: 'https://test.com/foo/bar' + }, + ortb2: { + regs: { + us_privacy: '1YYY' + } + } + }; + + let bidderRequestWithUSPAndOthersInExt = { + bidderRequestId: 'xyz123', + refererInfo: { + referer: 'https://test.com/foo/bar' + }, + ortb2: { + regs: { + ext: { + extra: 'extra item', + us_privacy: '1YYY' + } + } + } }; let validBidRequestWithUserIds = { @@ -391,7 +424,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([badFloorBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -422,7 +455,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([badFloorBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -454,7 +487,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([newPosBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -485,7 +518,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([newPosBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -577,7 +610,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([validBidRequestVideo], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -635,7 +668,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([validBidRequestVideo], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data.id).to.equal('xyz123'); expect(req.data.imp).to.eql([ { @@ -701,16 +734,42 @@ describe('imdsBidAdapter ', function () { } ]); }); - it('should contain the CCPA privacy string when UspConsent is in bidder request', function () { + it('should have us_privacy string in regs instead of regs.ext bidder request', function () { + let req = spec.buildRequests([validBidRequest], bidderRequestWithUSPInExt); + expect(req).be.an('object'); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?'); + expect(req.data).to.exist.and.to.be.an('object'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.regs.us_privacy).to.equal('1YYY'); + expect(req.data.regs.ext).to.not.exist; + expect(req.data.imp).to.eql([expectedDataImp1]); + }); + it('should accept us_privacy string in regs', function () { + // banner test + let req = spec.buildRequests([validBidRequest], bidderRequestWithUSPInRegs); + expect(req).be.an('object'); + expect(req).to.have.property('method', 'POST'); + expect(req).to.have.property('url'); + expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?'); + expect(req.data).to.exist.and.to.be.an('object'); + expect(req.data.id).to.equal('xyz123'); + expect(req.data.regs.us_privacy).to.equal('1YYY'); + expect(req.data.regs.ext).to.not.exist; + expect(req.data.imp).to.eql([expectedDataImp1]); + }); + it('should not remove regs.ext when moving us_privacy if there are other things in regs.ext', function () { // banner test - let req = spec.buildRequests([validBidRequest], bidderRequestWithCCPA); + let req = spec.buildRequests([validBidRequest], bidderRequestWithUSPAndOthersInExt); expect(req).be.an('object'); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); expect(req.url).to.contain('https://prebid.technoratimedia.com/openrtb/bids/prebid?'); expect(req.data).to.exist.and.to.be.an('object'); expect(req.data.id).to.equal('xyz123'); - expect(req.data.regs.ext.us_privacy).to.equal('1YYY'); + expect(req.data.regs.us_privacy).to.equal('1YYY'); + expect(req.data.regs.ext.extra).to.equal('extra item'); expect(req.data.imp).to.eql([expectedDataImp1]); }); it('should contain user object when user ids are present in the bidder request', function () { @@ -780,14 +839,14 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([validBannerBidRequest], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); }); it('should return valid bid request for video impression', function () { let req = spec.buildRequests([validVideoBidReq], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); }); }); @@ -887,7 +946,7 @@ describe('imdsBidAdapter ', function () { let req = spec.buildRequests([validBidReq], bidderRequest); expect(req).to.have.property('method', 'POST'); expect(req).to.have.property('url'); - expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=$$REPO_AND_VERSION$$'); + expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?src=pbjs%2F$prebid.version$'); expect(req.data).to.have.property('source'); expect(req.data.source).to.have.property('ext'); expect(req.data.source.ext).to.have.property('schain'); @@ -1293,20 +1352,42 @@ describe('imdsBidAdapter ', function () { }); }); describe('getUserSyncs', function () { - it('should return a usersync when iframes is enabled', function () { + it('should return an iframe usersync when iframes is enabled', function () { let usersyncs = spec.getUserSyncs({ iframeEnabled: true }, null); - expect(usersyncs).to.be.an('array').that.is.not.empty; + expect(usersyncs).to.be.an('array').with.lengthOf(1); expect(usersyncs[0]).to.have.property('type', 'iframe'); expect(usersyncs[0]).to.have.property('url'); expect(usersyncs[0].url).to.contain('https://ad-cdn.technoratimedia.com/html/usersync.html'); }); - it('should not return a usersync when iframes are not enabled', function () { + it('should return a pixel usersync when pixels is enabled', function () { let usersyncs = spec.getUserSyncs({ pixelEnabled: true }, null); + expect(usersyncs).to.be.an('array').with.lengthOf(1); + expect(usersyncs[0]).to.have.property('type', 'pixel'); + expect(usersyncs[0]).to.have.property('url'); + expect(usersyncs[0].url).to.contain('https://sync.technoratimedia.com/services'); + }); + + it('should return an iframe usersync when both iframe and pixels is enabled', function () { + let usersyncs = spec.getUserSyncs({ + iframeEnabled: true, + pixelEnabled: true + }, null); + expect(usersyncs).to.be.an('array').with.lengthOf(1); + expect(usersyncs[0]).to.have.property('type', 'iframe'); + expect(usersyncs[0]).to.have.property('url'); + expect(usersyncs[0].url).to.contain('https://ad-cdn.technoratimedia.com/html/usersync.html'); + }); + + it('should not return a usersync when neither iframes nor pixel are enabled', function () { + let usersyncs = spec.getUserSyncs({ + iframeEnabled: false, + pixelEnabled: false + }, null); expect(usersyncs).to.be.an('array').that.is.empty; }); }); From b4c9091275ab81b7e5627c5292f60c668dc10e45 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 20 Jul 2023 15:36:09 +0200 Subject: [PATCH 68/92] Adyoulike Bid Adapter: fix eids retrieve (#10102) * Adyoulike adapter: fix eids retrieve * refacto * rename userId to eids --- modules/adyoulikeBidAdapter.js | 9 +++-- test/spec/modules/adyoulikeBidAdapter_spec.js | 37 +++++++------------ 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/modules/adyoulikeBidAdapter.js b/modules/adyoulikeBidAdapter.js index 4080d9f25cd5..6eb897d334e0 100644 --- a/modules/adyoulikeBidAdapter.js +++ b/modules/adyoulikeBidAdapter.js @@ -63,6 +63,7 @@ export const spec = { // convert Native ORTB definition to old-style prebid native definition bidRequests = convertOrtbRequestToProprietaryNative(bidRequests); let hasVideo = false; + let eids; const payload = { Version: VERSION, Bids: bidRequests.reduce((accumulator, bidReq) => { @@ -81,6 +82,9 @@ export const spec = { if (bidReq.schain) { accumulator[bidReq.bidId].SChain = bidReq.schain; } + if (!eids && bidReq.userIdAsEids && bidReq.userIdAsEids.length) { + eids = bidReq.userIdAsEids; + } if (mediatype === NATIVE) { let nativeReq = bidReq.mediaTypes.native; if (nativeReq.type === 'image') { @@ -120,9 +124,8 @@ export const spec = { if (bidderRequest.ortb2) { payload.ortb2 = bidderRequest.ortb2; } - - if (deepAccess(bidderRequest, 'userIdAsEids')) { - payload.userId = bidderRequest.userIdAsEids; + if (eids) { + payload.eids = eids; } payload.pbjs_version = '$prebid.version$'; diff --git a/test/spec/modules/adyoulikeBidAdapter_spec.js b/test/spec/modules/adyoulikeBidAdapter_spec.js index 7310f736f7ec..de77c7413647 100644 --- a/test/spec/modules/adyoulikeBidAdapter_spec.js +++ b/test/spec/modules/adyoulikeBidAdapter_spec.js @@ -707,32 +707,21 @@ describe('Adyoulike Adapter', function () { expect(payload.gdprConsent.consentRequired).to.be.null; }); - it('should add userid eids information to the request', function () { - let bidderRequest = { - 'auctionId': '1d1a030790a475', - 'bidderRequestId': '22edbae2733bf6', - 'timeout': 3000, - 'userIdAsEids': - [ - { - 'source': 'pubcid.org', - 'uids': [ - { - 'atype': 1, - 'id': '01EAJWWNEPN3CYMM5N8M5VXY22' - } - ] - } - ] - }; - - bidderRequest.bids = bidRequestWithSinglePlacement; - - const request = spec.buildRequests(bidRequestWithSinglePlacement, bidderRequest); + it('should add eids eids information to the request', function () { + let bidRequest = bidRequestWithSinglePlacement; + bidRequest[0].userIdAsEids = [{ + 'source': 'pubcid.org', + 'uids': [{ + 'atype': 1, + 'id': '01EAJWWNEPN3CYMM5N8M5VXY22' + }] + }] + + const request = spec.buildRequests(bidRequest, bidderRequest); const payload = JSON.parse(request.data); - expect(payload.userId).to.exist; - expect(payload.userId).to.deep.equal(bidderRequest.userIdAsEids); + expect(payload.eids).to.exist; + expect(payload.eids).to.deep.equal(bidRequest[0].userIdAsEids); }); it('sends bid request to endpoint with single placement', function () { From 0f4a2fe0f7da0ffcf61f7f687504538b4b07cc2d Mon Sep 17 00:00:00 2001 From: ccorbo Date: Thu, 20 Jul 2023 10:38:55 -0400 Subject: [PATCH 69/92] IX bid adapter: add screen resolution in device object (#10246) Co-authored-by: Chris Corbo --- modules/ixBidAdapter.js | 16 ++++++++++++++++ test/spec/modules/ixBidAdapter_spec.js | 23 +++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 5e39c43367f1..d6ac3f3f9a46 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -736,6 +736,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { const isLastAdUnit = adUnitIndex === impKeys.length - 1; + r = addDeviceInfo(r); r = deduplicateImpExtFields(r); r = removeSiteIDs(r); @@ -2056,4 +2057,19 @@ function getFormatCount(imp) { return formatCount; } +/** + * Adds device.w / device.h info + * @param {object} r + * @returns object + */ +export function addDeviceInfo(r) { + if (r.device == undefined) { + r.device = {}; + } + r.device.h = window.screen.height; + r.device.w = window.screen.width; + + return r; +} + registerBidder(spec); diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 3bfbf706d723..7f5b882fed27 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -2,7 +2,7 @@ import * as utils from 'src/utils.js'; import { config } from 'src/config.js'; import { expect } from 'chai'; import { newBidder } from 'src/adapters/bidderFactory.js'; -import { spec, storage, ERROR_CODES, FEATURE_TOGGLES, LOCAL_STORAGE_FEATURE_TOGGLES_KEY, REQUESTED_FEATURE_TOGGLES, combineImps, bidToVideoImp, bidToNativeImp, deduplicateImpExtFields, removeSiteIDs } from '../../../modules/ixBidAdapter.js'; +import { spec, storage, ERROR_CODES, FEATURE_TOGGLES, LOCAL_STORAGE_FEATURE_TOGGLES_KEY, REQUESTED_FEATURE_TOGGLES, combineImps, bidToVideoImp, bidToNativeImp, deduplicateImpExtFields, removeSiteIDs, addDeviceInfo } from '../../../modules/ixBidAdapter.js'; import { deepAccess, deepClone } from '../../../src/utils.js'; describe('IndexexchangeAdapter', function () { @@ -2202,7 +2202,8 @@ describe('IndexexchangeAdapter', function () { const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, { ortb2 })[0]; const payload = extractPayload(request); - expect(payload.device).to.be.undefined + expect(payload.device.h).to.exist; + expect(payload.device.w).to.exist; }); it('should not set first party data if it is not an object', function () { @@ -4906,4 +4907,22 @@ describe('IndexexchangeAdapter', function () { expect(removeSiteIDs(request)).to.deep.equal(expected); }); }); + describe('addDeviceInfo', () => { + it('should add device to request when device already exists', () => { + let r = { + device: { + ip: '127.0.0.1' + } + } + r = addDeviceInfo(r); + expect(r.device.w).to.exist; + expect(r.device.h).to.exist; + }); + it('should add device to request when device doesnt exist', () => { + let r = {} + r = addDeviceInfo(r); + expect(r.device.w).to.exist; + expect(r.device.h).to.exist; + }); + }); }); From 1b8866f501e1cf963c458eca7013c68676518bf1 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Date: Thu, 20 Jul 2023 19:19:57 +0200 Subject: [PATCH 70/92] feat(greenbidsRtdProvider): fix throttling endpoint (#10258) --- modules/greenbidsRtdProvider.js | 2 +- test/spec/modules/greenbidsRtdProvider_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/greenbidsRtdProvider.js b/modules/greenbidsRtdProvider.js index ef12326cf185..b3d79f059968 100644 --- a/modules/greenbidsRtdProvider.js +++ b/modules/greenbidsRtdProvider.js @@ -4,7 +4,7 @@ import { submodule } from '../src/hook.js'; const MODULE_NAME = 'greenbidsRtdProvider'; const MODULE_VERSION = '1.0.0'; -const ENDPOINT = 'https://europe-west1-greenbids-357713.cloudfunctions.net/partner-selection'; +const ENDPOINT = 'https://t.greenbids.ai'; const auctionInfo = {}; const rtdOptions = {}; diff --git a/test/spec/modules/greenbidsRtdProvider_spec.js b/test/spec/modules/greenbidsRtdProvider_spec.js index 6b7d826ab06e..7cb6c10ce482 100644 --- a/test/spec/modules/greenbidsRtdProvider_spec.js +++ b/test/spec/modules/greenbidsRtdProvider_spec.js @@ -18,7 +18,7 @@ describe('greenbidsRtdProvider', () => { server.restore(); }); - const endPoint = 'europe-west1-greenbids-357713.cloudfunctions.net'; + const endPoint = 't.greenbids.ai'; const SAMPLE_MODULE_CONFIG = { params: { From 5aefdec842c8f518d4ab00c0cdc14db4b05628ac Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 20 Jul 2023 10:47:01 -0700 Subject: [PATCH 71/92] priceFloors: fix bug where floors are not set when TIDs are disabled (#10257) --- integrationExamples/gpt/adUnitFloors.html | 1 - src/adapters/bidderFactory.js | 14 ++++-- test/spec/modules/priceFloors_spec.js | 14 ++++++ test/spec/unit/core/bidderFactory_spec.js | 56 +++++++++++++++++------ 4 files changed, 67 insertions(+), 18 deletions(-) diff --git a/integrationExamples/gpt/adUnitFloors.html b/integrationExamples/gpt/adUnitFloors.html index bb48a20ef788..a80e1b2380b6 100644 --- a/integrationExamples/gpt/adUnitFloors.html +++ b/integrationExamples/gpt/adUnitFloors.html @@ -109,4 +109,3 @@
Div-1
- diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index c8f03bcfc94b..0d51ab9c6633 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -184,7 +184,7 @@ export function registerBidder(spec) { } } -function guardTids(bidderCode) { +export function guardTids(bidderCode) { if (isActivityAllowed(ACTIVITY_TRANSMIT_TID, activityParams(MODULE_TYPE_BIDDER, bidderCode))) { return { bidRequest: (br) => br, @@ -197,7 +197,15 @@ function guardTids(bidderCode) { } return Reflect.get(target, prop, receiver); } - const bidRequest = memoize((br) => new Proxy(br, {get}), (arg) => arg.bidId) + function privateAccessProxy(target, handler) { + const proxy = new Proxy(target, handler); + // always allow methods (such as getFloor) private access to TIDs + Object.entries(target) + .filter(([_, v]) => typeof v === 'function') + .forEach(([prop, fn]) => proxy[prop] = fn.bind(target)); + return proxy; + } + const bidRequest = memoize((br) => privateAccessProxy(br, {get}), (arg) => arg.bidId); /** * Return a view on bidd(er) requests where auctionId/transactionId are nulled if the bidder is not allowed `transmitTid`. * @@ -207,7 +215,7 @@ function guardTids(bidderCode) { */ return { bidRequest, - bidderRequest: (br) => new Proxy(br, { + bidderRequest: (br) => privateAccessProxy(br, { get(target, prop, receiver) { if (prop === 'bids') return br.bids.map(bidRequest); return get(target, prop, receiver); diff --git a/test/spec/modules/priceFloors_spec.js b/test/spec/modules/priceFloors_spec.js index e9ce4c8ccd3d..64c871308a9c 100644 --- a/test/spec/modules/priceFloors_spec.js +++ b/test/spec/modules/priceFloors_spec.js @@ -20,6 +20,8 @@ import 'src/prebid.js'; import {createBid} from '../../../src/bidfactory.js'; import {auctionManager} from '../../../src/auctionManager.js'; import {stubAuctionIndex} from '../../helpers/indexStub.js'; +import {guardTids} from '../../../src/adapters/bidderFactory.js'; +import * as activities from '../../../src/activities/rules.js'; describe('the price floors module', function () { let logErrorSpy; @@ -1369,6 +1371,18 @@ describe('the price floors module', function () { floor: 2.5 }); }); + + it('works when TIDs are disabled', () => { + sandbox.stub(activities, 'isActivityAllowed').returns(false); + const req = utils.deepClone(bidRequest); + _floorDataForAuction[req.auctionId] = utils.deepClone(basicFloorConfig); + + expect(guardTids('mock-bidder').bidRequest(req).getFloor({})).to.deep.equal({ + currency: 'USD', + floor: 1.0 + }); + }); + it('picks the right rule with more complex rules', function () { _floorDataForAuction[bidRequest.auctionId] = { ...basicFloorConfig, diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index 751c90af50f0..c94d349b8691 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -13,7 +13,6 @@ import {stubAuctionIndex} from '../../../helpers/indexStub.js'; import {bidderSettings} from '../../../../src/bidderSettings.js'; import {decorateAdUnitsWithNativeParams} from '../../../../src/native.js'; import * as activityRules from 'src/activities/rules.js'; -import {sandbox} from 'sinon'; import {MODULE_TYPE_BIDDER} from '../../../../src/activities/modules.js'; import {ACTIVITY_TRANSMIT_TID} from '../../../../src/activities/activities.js'; @@ -141,6 +140,21 @@ describe('bidders created by newBidder', function () { }); describe('transaction IDs', () => { + beforeEach(() => { + activityRules.isActivityAllowed.reset(); + ajaxStub.callsFake((_, callback) => callback.success(null, {getResponseHeader: sinon.stub()})); + spec.interpretResponse.callsFake(() => [ + { + requestId: 'bid', + cpm: 123, + ttl: 300, + creativeId: 'crid', + netRevenue: true, + currency: 'USD' + } + ]) + }); + Object.entries({ 'be hidden': false, 'not be hidden': true, @@ -166,21 +180,8 @@ describe('bidders created by newBidder', function () { bidReqs.forEach(checkBidRequest); return {method: 'POST'}; }); - spec.interpretResponse.callsFake(() => [ - { - requestId: 'bid', - cpm: 123, - ttl: 300, - creativeId: 'crid', - netRevenue: true, - currency: 'USD' - } - ]) - activityRules.isActivityAllowed.reset(); activityRules.isActivityAllowed.callsFake(() => allowed); - ajaxStub.callsFake((_, callback) => callback.success(null, {getResponseHeader: sinon.stub()})); - const bidder = newBidder(spec); bidder.callBids({ @@ -206,6 +207,33 @@ describe('bidders created by newBidder', function () { }) }); }); + + it('should not be hidden from request methods', (done) => { + const bidderRequest = { + bidderCode: 'mockBidder', + auctionId: 'aid', + getAID() { return this.auctionId }, + bids: [ + { + adUnitCode: 'mockAU', + bidId: 'bid', + transactionId: 'tid', + auctionId: 'aid', + getTIDs() { + return [this.auctionId, this.transactionId] + } + } + ] + }; + activityRules.isActivityAllowed.callsFake(() => false); + spec.isBidRequestValid.returns(true); + spec.buildRequests.callsFake((reqs, bidderReq) => { + expect(bidderReq.getAID()).to.eql('aid'); + expect(reqs[0].getTIDs()).to.eql(['aid', 'tid']); + done(); + }); + newBidder(spec).callBids(bidderRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + }) }); it('should handle bad bid requests gracefully', function () { From c98ccdbe22b3ac4a74e85b351cc3e26bc35a213c Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Thu, 20 Jul 2023 12:38:54 -0700 Subject: [PATCH 72/92] Core: disregard `transactionId`/`auctionId` set by bid adapters (#10262) --- src/adapters/bidderFactory.js | 7 ++++--- test/spec/unit/core/bidderFactory_spec.js | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/adapters/bidderFactory.js b/src/adapters/bidderFactory.js index 0d51ab9c6633..d316ad5924c0 100644 --- a/src/adapters/bidderFactory.js +++ b/src/adapters/bidderFactory.js @@ -15,7 +15,7 @@ import { logError, logWarn, memoize, parseQueryStringParameters, - parseSizesInput, + parseSizesInput, pick, uniques } from '../utils.js'; import {hook} from '../hook.js'; @@ -150,6 +150,7 @@ import {ACTIVITY_TRANSMIT_TID} from '../activities/activities.js'; // common params for all mediaTypes const COMMON_BID_RESPONSE_KEYS = ['cpm', 'ttl', 'creativeId', 'netRevenue', 'currency']; +const TIDS = ['auctionId', 'transactionId']; /** * Register a bidder with prebid, using the given spec. @@ -192,7 +193,7 @@ export function guardTids(bidderCode) { }; } function get(target, prop, receiver) { - if (['transactionId', 'auctionId'].includes(prop)) { + if (TIDS.includes(prop)) { return null; } return Reflect.get(target, prop, receiver); @@ -320,7 +321,7 @@ export function newBidder(spec) { bid.originalCpm = bid.cpm; bid.originalCurrency = bid.currency; bid.meta = bid.meta || Object.assign({}, bid[bidRequest.bidder]); - const prebidBid = Object.assign(createBid(CONSTANTS.STATUS.GOOD, bidRequest), bid); + const prebidBid = Object.assign(createBid(CONSTANTS.STATUS.GOOD, bidRequest), bid, pick(bidRequest, TIDS)); addBidWithCode(bidRequest.adUnitCode, prebidBid); } else { logWarn(`Bidder ${spec.code} made bid for unknown request ID: ${bid.requestId}. Ignoring.`); diff --git a/test/spec/unit/core/bidderFactory_spec.js b/test/spec/unit/core/bidderFactory_spec.js index c94d349b8691..138ffcb608db 100644 --- a/test/spec/unit/core/bidderFactory_spec.js +++ b/test/spec/unit/core/bidderFactory_spec.js @@ -1135,6 +1135,25 @@ describe('validate bid response: ', function () { expect(logErrorSpy.callCount).to.equal(0); }); + it('should disregard auctionId/transactionId set by the adapter', () => { + let bidderRequest = { + bids: [{ + bidder: CODE, + bidId: '1', + auctionId: 'aid', + transactionId: 'tid', + adUnitCode: 'au', + }] + }; + const bidder = newBidder(spec); + spec.interpretResponse.returns(Object.assign({}, bids[0], {transactionId: 'ignored', auctionId: 'ignored'})); + bidder.callBids(bidderRequest, addBidResponseStub, doneStub, ajaxStub, onTimelyResponseStub, wrappedCallback); + sinon.assert.calledWith(addBidResponseStub, sinon.match.any, sinon.match({ + transactionId: 'tid', + auctionId: 'aid' + })); + }) + describe(' Check for alternateBiddersList ', function() { let bidRequest; let bids1; From 665b55f8b43f6991ca7c9645028ac46705fa632f Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 20 Jul 2023 20:06:59 +0000 Subject: [PATCH 73/92] Prebid 8.5.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index a043b290499b..b6b2b34c6281 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.5.0-pre", + "version": "8.5.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index f72547206f1f..9fcd2da5d639 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.5.0-pre", + "version": "8.5.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From 402d30bbd9149bd4047e9a48b05ff3f9336f1e63 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 20 Jul 2023 20:06:59 +0000 Subject: [PATCH 74/92] Increment version to 8.6.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b6b2b34c6281..f8dc7ad289f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.5.0", + "version": "8.6.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 9fcd2da5d639..8bbbe9a6d789 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.5.0", + "version": "8.6.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From af0fac308de33849d1ec12714b8c94a40a6c81bf Mon Sep 17 00:00:00 2001 From: hzchen98 Date: Fri, 21 Jul 2023 18:02:04 +0200 Subject: [PATCH 75/92] change to https (#10267) --- modules/ssmasBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ssmasBidAdapter.js b/modules/ssmasBidAdapter.js index 9ed1ec90dd82..3005910789be 100644 --- a/modules/ssmasBidAdapter.js +++ b/modules/ssmasBidAdapter.js @@ -6,8 +6,8 @@ import {config} from '../src/config.js'; export const SSMAS_CODE = 'ssmas'; const SSMAS_SERVER = 'ads.ssmas.com'; -export const SSMAS_ENDPOINT = `http://${SSMAS_SERVER}/ortb`; -const SYNC_URL = `http://sync.ssmas.com/user_sync`; +export const SSMAS_ENDPOINT = `https://${SSMAS_SERVER}/ortb`; +const SYNC_URL = `https://sync.ssmas.com/user_sync`; export const SSMAS_REQUEST_METHOD = 'POST'; const GDPR_VENDOR_ID = 1183; From bb24bdb7e335c2e2436d815f56d806dbdb536ba4 Mon Sep 17 00:00:00 2001 From: tkrishnaviant Date: Mon, 24 Jul 2023 06:21:11 -0700 Subject: [PATCH 76/92] Viant Bid Adapter: Initial Release (#10149) * First Draft * Second Draft with Unit tests * THird Draft with more Unit tests * fix test when all features are disabled. * added gdpr support and unit test * added us privacy support and unit test * added video interpret response unit test --- modules/viantOrtbBidAdapter.js | 113 +++++ modules/viantOrtbBidAdapter.md | 54 +++ test/spec/modules/viantOrtbBidAdapter_spec.js | 432 ++++++++++++++++++ 3 files changed, 599 insertions(+) create mode 100644 modules/viantOrtbBidAdapter.js create mode 100644 modules/viantOrtbBidAdapter.md create mode 100644 test/spec/modules/viantOrtbBidAdapter_spec.js diff --git a/modules/viantOrtbBidAdapter.js b/modules/viantOrtbBidAdapter.js new file mode 100644 index 000000000000..e7bf9129a621 --- /dev/null +++ b/modules/viantOrtbBidAdapter.js @@ -0,0 +1,113 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; +import {ortbConverter} from '../libraries/ortbConverter/converter.js' +import {deepAccess, getBidIdParameter, logError} from '../src/utils.js'; + +const BIDDER_CODE = 'viant'; +const ENDPOINT = 'https://bidders-us-east-1.adelphic.net/d/rtb/v25/prebid/bidder_test' + +const DEFAULT_BID_TTL = 300; +const DEFAULT_CURRENCY = 'USD'; +const DEFAULT_NET_REVENUE = true; + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: function (bid) { + if (bid && typeof bid.params !== 'object') { + logError(BIDDER_CODE + ': params is not defined or is incorrect in the bidder settings.'); + return false; + } + if (!getBidIdParameter('publisherId', bid.params)) { + logError(BIDDER_CODE + ': publisherId is not present in bidder params.'); + return false; + } + const mediaTypesBanner = deepAccess(bid, 'mediaTypes.banner'); + const mediaTypesVideo = deepAccess(bid, 'mediaTypes.video'); + const mediaTypesNative = deepAccess(bid, 'mediaTypes.native'); + if (!mediaTypesBanner && !mediaTypesVideo && !mediaTypesNative) { + utils.logWarn(BIDDER_CODE + ': one of mediaTypes.banner or mediaTypes.video or mediaTypes.native must be passed'); + return false; + } + return true; + }, + + buildRequests, + + interpretResponse(response, request) { + if (!response.body) { + response.body = {nbr: 0}; + } + const bids = converter.fromORTB({request: request.data, response: response.body}).bids; + return bids; + }, + + /** + * Register bidder specific code, which will execute if a bid from this bidder won the auction + * @param {Bid} bid The bid that won the auction + */ + onBidWon: function (bid) { + if (bid.burl) { + utils.triggerPixel(bid.burl); + } else if (bid.nurl) { + utils.triggerPixel(bid.nurl); + } + } +} + +function buildRequests(bids, bidderRequest) { + let videoBids = bids.filter(bid => isVideoBid(bid)); + let bannerBids = bids.filter(bid => isBannerBid(bid)); + let nativeBids = bids.filter(bid => isNativeBid(bid)); + let requests = bannerBids.length ? [createRequest(bannerBids, bidderRequest, BANNER)] : []; + videoBids.forEach(bid => { + requests.push(createRequest([bid], bidderRequest, VIDEO)); + }); + nativeBids.forEach(bid => { + requests.push(createRequest([bid], bidderRequest, NATIVE)); + }); + return requests; +} + +function createRequest(bidRequests, bidderRequest, mediaType) { + const data = converter.toORTB({bidRequests, bidderRequest, context: {mediaType}}); + if (bidderRequest.gdprConsent && typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { + if (!data.regs) data.regs = {}; + if (!data.regs.ext) data.regs.ext = {}; + data.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + } + if (bidderRequest.uspConsent) { + if (!data.regs) data.regs = {}; + if (!data.regs.ext) data.regs.ext = {}; + data.regs.ext.us_privacy = bidderRequest.uspConsent; + } + return { + method: 'POST', + url: ENDPOINT, + data: data + } +} + +function isVideoBid(bid) { + return deepAccess(bid, 'mediaTypes.video'); +} + +function isBannerBid(bid) { + return deepAccess(bid, 'mediaTypes.banner'); +} + +function isNativeBid(bid) { + return deepAccess(bid, 'mediaTypes.native'); +} + +const converter = ortbConverter({ + context: { + netRevenue: DEFAULT_NET_REVENUE, + ttl: DEFAULT_BID_TTL, + currency: DEFAULT_CURRENCY + } +}); + +registerBidder(spec); diff --git a/modules/viantOrtbBidAdapter.md b/modules/viantOrtbBidAdapter.md new file mode 100644 index 000000000000..178c2c18a0f7 --- /dev/null +++ b/modules/viantOrtbBidAdapter.md @@ -0,0 +1,54 @@ +# Overview + +Module Name: VIANT Bidder Adapter +Module Type: Bidder Adapter +Maintainer: dist-runtime@viantinc.com + +# Description + +An adapter to get a bid from VIANT DSP. + +# Test Parameters +```javascript + var adUnits = [ // Banner adUnit with only required parameters + { + code: 'test-div-minimal', + mediaTypes: { + banner: { + sizes: [[728, 90]] + } + }, + bids: [ + { + bidder: 'viant', + params: { + supplySourceId: 'supplier', + publisherId: '464' + } + } + ] + }, + { + code: 'test-div-minimal-video', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'outstream' + } + }, + bids: [ + { + bidder: 'viant', + params: { + supplySourceId: 'supplier', + publisherId: '464' // required + } + } + ] + } + ]; +``` + +Where: + +* placementId - Placement ID of the ad unit (required) diff --git a/test/spec/modules/viantOrtbBidAdapter_spec.js b/test/spec/modules/viantOrtbBidAdapter_spec.js new file mode 100644 index 000000000000..ef537d50986d --- /dev/null +++ b/test/spec/modules/viantOrtbBidAdapter_spec.js @@ -0,0 +1,432 @@ +import { spec, converter } from 'modules/viantOrtbBidAdapter.js'; +import {assert, expect} from 'chai'; +import { deepClone } from '../../../src/utils'; +import {buildWindowTree} from '../../helpers/refererDetectionHelper'; +import {detectReferer} from '../../../src/refererDetection'; +describe('viantOrtbBidAdapter', function () { + function testBuildRequests(bidRequests, bidderRequestBase) { + let clonedBidderRequest = deepClone(bidderRequestBase); + clonedBidderRequest.bids = bidRequests; + let requests = spec.buildRequests(bidRequests, clonedBidderRequest); + return requests + } + describe('isBidRequestValid', function() { + function makeBid() { + return { + 'bidder': 'viant', + 'params': { + 'publisherId': '464', + 'placementId': 'some-PlacementId_1' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [728, 90] + ] + } + }, + 'adUnitCode': 'adunit-code', + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + } + + describe('core', function () { + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(makeBid())).to.equal(true); + }); + + it('should return false when publisherId not passed', function () { + let bid = makeBid(); + delete bid.params.publisherId; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return true if placementId is not passed ', function () { + let bid = makeBid(); + delete bid.params.placementId; + bid.ortb2Imp = { + + } + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false if mediaTypes.banner is Not passed', function () { + let bid = makeBid(); + delete bid.mediaTypes + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('banner', function () { + it('should return true if banner.pos is passed correctly', function () { + let bid = makeBid(); + bid.mediaTypes.banner.pos = 1; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + + describe('video', function () { + describe('and request config uses mediaTypes', () => { + function makeBid() { + return { + 'bidder': 'viant', + 'params': { + 'unit': '12345678', + 'delDomain': 'test-del-domain', + 'publisherId': '464', + 'placementId': 'some-PlacementId_2' + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [[640, 480]], + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], + 'api': [1, 3], + 'skip': 1, + 'skipafter': 5, + 'minduration': 10, + 'maxduration': 30 + } + }, + 'adUnitCode': 'adunit-code', + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' + } + } + it('should return true when required params found', function () { + expect(spec.isBidRequestValid(makeBid())).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let videoBidWithMediaTypes = Object.assign({}, makeBid()); + videoBidWithMediaTypes.params = {}; + expect(spec.isBidRequestValid(videoBidWithMediaTypes)).to.equal(false); + }); + }); + }); + }); + + describe('buildRequests-banner', function () { + const baseBannerBidRequests = [{ + 'bidder': 'viant', + 'params': { + 'publisherId': '464', + 'placementId': '1' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[728, 90]] + } + }, + 'gdprConsent': { + 'consentString': 'consentString', + 'gdprApplies': true, + }, + 'uspConsent': '1YYY', + 'sizes': [[728, 90]], + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'bidId': '243310435309b5', + 'bidderRequestId': '18084284054531', + 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + 'src': 'client', + 'bidRequestsCount': 1 + }]; + + const testWindow = buildWindowTree(['https://www.example.com/test', 'https://www.example.com/other/page', 'https://www.example.com/third/page'], 'https://othersite.com/', 'https://example.com/canonical/page'); + const baseBidderRequestReferer = detectReferer(testWindow)(); + const baseBidderRequest = { + 'bidderCode': 'viant', + 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + 'bidderRequestId': '18084284054531', + 'auctionStart': 1540945362095, + 'timeout': 3000, + 'refererInfo': baseBidderRequestReferer, + 'start': 1540945362099, + 'doneCbCallCount': 0 + }; + + it('test regs', function () { + const gdprBaseBidderRequest = Object.assign({}, baseBidderRequest, { + gdprConsent: { + consentString: 'consentString', + gdprApplies: true, + }, + uspConsent: '1YYN' + }); + const request = testBuildRequests(baseBannerBidRequests, gdprBaseBidderRequest)[0]; + expect(request.data.regs.ext).to.have.property('gdpr', 1); + expect(request.data.regs.ext).to.have.property('us_privacy', '1YYN'); + }); + + it('sends bid request to our endpoint that makes sense', function () { + const request = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0]; + expect(request.method).to.equal('POST'); + expect(request.url).to.be.not.empty; + expect(request.data).to.be.not.null; + }); + it('sends bid requests to the correct endpoint', function () { + const url = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0].url; + expect(url).to.equal('https://bidders-us-east-1.adelphic.net/d/rtb/v25/prebid/bidder_test'); + }); + + it('sends site', function () { + const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0].data; + expect(requestBody.site).to.be.not.null; + }); + + it('includes the ad size in the bid request', function () { + const requestBody = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0].data; + expect(requestBody.imp[0].banner.format[0].w).to.equal(728); + expect(requestBody.imp[0].banner.format[0].h).to.equal(90); + }); + + it('sets the banner pos correctly if sent', function () { + let clonedBannerRequests = deepClone(baseBannerBidRequests); + clonedBannerRequests[0].mediaTypes.banner.pos = 1; + + const requestBody = testBuildRequests(clonedBannerRequests, baseBidderRequest)[0].data; + expect(requestBody.imp[0].banner.pos).to.equal(1); + }); + }); + + if (FEATURES.VIDEO) { + describe('buildRequests-video', function () { + function makeBid() { + return { + 'bidder': 'viant', + 'params': { + 'unit': '12345678', + 'delDomain': 'test-del-domain', + 'publisherId': '464', + 'placementId': 'some-PlacementId_2' + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [[640, 480]], + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], + 'api': [1, 3], + 'skip': 1, + 'skipafter': 5, + 'minduration': 10, + 'maxduration': 31 + } + }, + 'adUnitCode': 'adunit-code', + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e' + } + } + + it('assert video and its fields is present in imp ', function () { + let requests = spec.buildRequests([makeBid()], {referrerInfo: {}}); + let clonedRequests = deepClone(requests) + assert.equal(clonedRequests[0].data.imp[0].video.mimes[0], 'video/mp4') + assert.equal(clonedRequests[0].data.imp[0].video.maxduration, 31) + assert.equal(clonedRequests[0].data.imp[0].video.placement, 1) + assert.equal(clonedRequests[0].method, 'POST') + }); + }); + } + + describe('interpretResponse', function () { + const baseBannerBidRequests = [{ + 'bidder': 'viant', + 'params': { + 'publisherId': '464', + 'placementId': '1' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [[728, 90]] + } + }, + 'sizes': [[728, 90]], + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'bidId': '243310435309b5', + 'bidderRequestId': '18084284054531', + 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + 'src': 'client', + 'bidRequestsCount': 1 + }]; + + const testWindow = buildWindowTree(['https://www.example.com/test', 'https://www.example.com/other/page', 'https://www.example.com/third/page'], 'https://othersite.com/', 'https://example.com/canonical/page'); + const baseBidderRequestReferer = detectReferer(testWindow)(); + const baseBidderRequest = { + 'bidderCode': 'viant', + 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + 'bidderRequestId': '18084284054531', + 'auctionStart': 1540945362095, + 'timeout': 3000, + 'refererInfo': baseBidderRequestReferer, + 'start': 1540945362099, + 'doneCbCallCount': 0 + }; + + it('empty bid response test', function () { + const request = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0]; + let bidResponse = {nbr: 0}; // Unknown error + let bids = spec.interpretResponse({body: bidResponse}, request); + expect(bids.length).to.equal(0); + }); + + it('bid response is a banner', function () { + const request = testBuildRequests(baseBannerBidRequests, baseBidderRequest)[0]; + let bidResponse = { + seatbid: [{ + bid: [{ + impid: '243310435309b5', + price: 2, + w: 728, + h: 90, + crid: 'test-creative-id', + dealid: 'test-deal-id', + adm: 'test-ad-markup', + }] + }], + cur: 'USD' + }; + let bids = spec.interpretResponse({body: bidResponse}, request); + expect(bids.length).to.equal(1); + let bid = bids[0]; + it('should return the proper mediaType', function () { + it('should return a creativeId', function () { + expect(bid.mediaType).to.equal('banner'); + }); + }); + it('should return a price', function () { + expect(bid.cpm).to.equal(bidResponse.seatbid[0].bid[0].price); + }); + + it('should return a request id', function () { + expect(bid.requestId).to.equal(bidResponse.seatbid[0].bid[0].impid); + }); + + it('should return width and height for the creative', function () { + expect(bid.width).to.equal(bidResponse.seatbid[0].bid[0].w); + expect(bid.height).to.equal(bidResponse.seatbid[0].bid[0].h); + }); + it('should return a creativeId', function () { + expect(bid.creativeId).to.equal(bidResponse.seatbid[0].bid[0].crid); + }); + it('should return an ad', function () { + expect(bid.ad).to.equal(bidResponse.seatbid[0].bid[0].adm); + }); + + it('should return a deal id if it exists', function () { + expect(bid.dealId).to.equal(bidResponse.seatbid[0].bid[0].dealid); + }); + + it('should have a time-to-live of 5 minutes', function () { + expect(bid.ttl).to.equal(300); + }); + + it('should always return net revenue', function () { + expect(bid.netRevenue).to.equal(true); + }); + it('should return a currency', function () { + expect(bid.currency).to.equal(bidResponse.cur); + }); + }); + }); + describe('interpretResponse-Video', function () { + const baseVideoBidRequests = [{ + 'bidder': 'viant', + 'params': { + 'publisherId': '464', + 'placementId': '1' + }, + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [[640, 480]], + 'mimes': ['video/mp4'], + 'protocols': [1, 2, 3, 4, 5, 6, 7, 8], + 'api': [1, 3], + 'skip': 1, + 'skipafter': 5, + 'minduration': 10, + 'maxduration': 31 + } + }, + 'sizes': [[640, 480]], + 'transactionId': '1111474f-58b1-4368-b812-84f8c937a099', + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'bidId': '243310435309b5', + 'bidderRequestId': '18084284054531', + 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + 'src': 'client', + 'bidRequestsCount': 1 + }]; + + const testWindow = buildWindowTree(['https://www.example.com/test', 'https://www.example.com/other/page', 'https://www.example.com/third/page'], 'https://othersite.com/', 'https://example.com/canonical/page'); + const baseBidderRequestReferer = detectReferer(testWindow)(); + const baseBidderRequest = { + 'bidderCode': 'viant', + 'auctionId': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + 'bidderRequestId': '18084284054531', + 'auctionStart': 1540945362095, + 'timeout': 3000, + 'refererInfo': baseBidderRequestReferer, + 'start': 1540945362099, + 'doneCbCallCount': 0 + }; + + it('bid response is a video', function () { + const request = testBuildRequests(baseVideoBidRequests, baseBidderRequest)[0]; + const VIDEO_BID_RESPONSE = { + 'id': 'bidderRequestId', + 'bidid': 'e7b34fa3-8654-424e-8c49-03e509e53d8c', + 'seatbid': [ + { + 'bid': [ + { + 'id': '1', + 'impid': '243310435309b5', + 'price': 1.09, + 'adid': '144762342', + 'nurl': 'http://0.0.0.0:8181/nurl', + 'adm': '', + 'adomain': [ + 'https://dummydomain.com' + ], + 'cid': 'cid', + 'crid': 'crid', + 'iurl': 'iurl', + 'cat': [], + 'h': 480, + 'w': 640 + } + ] + } + ], + 'cur': 'USD' + }; + let bids = spec.interpretResponse({body: VIDEO_BID_RESPONSE}, request); + expect(bids.length).to.equal(1); + let bid = bids[0]; + it('should return the proper mediaType', function () { + expect(bid.mediaType).to.equal('video'); + }); + it('should return correct Ad Markup', function () { + expect(bid.vastXml).to.equal(''); + }); + it('should return correct Notification', function () { + expect(bid.vastUrl).to.equal('http://0.0.0.0:8181/nurl'); + }); + it('should return correct Cpm', function () { + expect(bid.cpm).to.equal(1.09); + }); + }); + }); +}); From c281fc7cb022be04092c4698c2ec5643925bb039 Mon Sep 17 00:00:00 2001 From: dzhang-criteo <87757739+dzhang-criteo@users.noreply.github.com> Date: Mon, 24 Jul 2023 17:08:25 +0200 Subject: [PATCH 77/92] Criteo adapter: add onDataDeletionRequest method (#10236) Add support of US Privacy data deletion request method. --- modules/criteoBidAdapter.js | 18 ++++++++++++ test/spec/modules/criteoBidAdapter_spec.js | 34 ++++++++++++++++++++-- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/modules/criteoBidAdapter.js b/modules/criteoBidAdapter.js index 993346df8499..046c17ef5eca 100644 --- a/modules/criteoBidAdapter.js +++ b/modules/criteoBidAdapter.js @@ -10,6 +10,7 @@ import { getRefererInfo } from '../src/refererDetection.js'; import { hasPurpose1Consent } from '../src/utils/gpdr.js'; import { Renderer } from '../src/Renderer.js'; import { OUTSTREAM } from '../src/video.js'; +import { ajax } from '../src/ajax.js'; const GVLID = 91; export const ADAPTER_VERSION = 36; @@ -306,6 +307,23 @@ export const spec = { adapter.handleSetTargeting(bid); } }, + + /** + * @param {BidRequest[]} bidRequests + */ + onDataDeletionRequest: (bidRequests) => { + const id = readFromAllStorages(BUNDLE_COOKIE_NAME); + if (id) { + deleteFromAllStorages(BUNDLE_COOKIE_NAME); + } + ajax('https://privacy.criteo.com/api/privacy/datadeletionrequest', + null, + JSON.stringify({ publisherUserId: id }), + { + contentType: 'application/json', + method: 'POST' + }); + } }; function readFromAllStorages(name) { diff --git a/test/spec/modules/criteoBidAdapter_spec.js b/test/spec/modules/criteoBidAdapter_spec.js index b2f3d64a1567..f28c65156513 100755 --- a/test/spec/modules/criteoBidAdapter_spec.js +++ b/test/spec/modules/criteoBidAdapter_spec.js @@ -9,11 +9,12 @@ import { } from 'modules/criteoBidAdapter.js'; import * as utils from 'src/utils.js'; import * as refererDetection from 'src/refererDetection.js'; +import * as ajax from 'src/ajax.js'; import { config } from '../../../src/config.js'; import { BANNER, NATIVE, VIDEO } from '../../../src/mediaTypes.js'; describe('The Criteo bidding adapter', function () { - let utilsMock, sandbox; + let utilsMock, sandbox, ajaxStub; beforeEach(function () { $$PREBID_GLOBAL$$.bidderSettings = { @@ -26,6 +27,7 @@ describe('The Criteo bidding adapter', function () { utilsMock = sinon.mock(utils); sandbox = sinon.sandbox.create(); + ajaxStub = sandbox.stub(ajax, 'ajax'); }); afterEach(function () { @@ -33,6 +35,7 @@ describe('The Criteo bidding adapter', function () { global.Criteo = undefined; utilsMock.restore(); sandbox.restore(); + ajaxStub.restore(); }); describe('getUserSyncs', function () { @@ -56,7 +59,9 @@ describe('The Criteo bidding adapter', function () { cookiesAreEnabledStub, localStorageIsEnabledStub, getCookieStub, - getDataFromLocalStorageStub; + setCookieStub, + getDataFromLocalStorageStub, + removeDataFromLocalStorageStub; beforeEach(function () { getConfigStub = sinon.stub(config, 'getConfig'); @@ -75,8 +80,10 @@ describe('The Criteo bidding adapter', function () { localStorageIsEnabledStub = sinon.stub(storage, 'localStorageIsEnabled'); localStorageIsEnabledStub.returns(true); - getCookieStub = sinon.stub(storage, 'getCookie') + getCookieStub = sinon.stub(storage, 'getCookie'); + setCookieStub = sinon.stub(storage, 'setCookie'); getDataFromLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + removeDataFromLocalStorageStub = sinon.stub(storage, 'removeDataFromLocalStorage'); }); afterEach(function () { @@ -86,7 +93,9 @@ describe('The Criteo bidding adapter', function () { cookiesAreEnabledStub.restore(); localStorageIsEnabledStub.restore(); getCookieStub.restore(); + setCookieStub.restore(); getDataFromLocalStorageStub.restore(); + removeDataFromLocalStorageStub.restore(); }); it('should not trigger sync if publisher is using fast bid', function () { @@ -198,6 +207,25 @@ describe('The Criteo bidding adapter', function () { url: `https://gum.criteo.com/syncframe?origin=criteoPrebidAdapter&topUrl=www.abc.com&us_privacy=ABC#${JSON.stringify(expectedHash).replace(/"/g, '%22')}` }]); }); + + it('should delete user data when calling onDataDeletionRequest', () => { + const cookieData = { + 'cto_bundle': 'a' + }; + const lsData = { + 'cto_bundle': 'a' + } + getCookieStub.callsFake(cookieName => cookieData[cookieName]); + setCookieStub.callsFake((cookieName, value, expires) => cookieData[cookieName] = value); + removeDataFromLocalStorageStub.callsFake(name => lsData[name] = ''); + spec.onDataDeletionRequest([]); + expect(getCookieStub.calledOnce).to.equal(true); + expect(setCookieStub.calledOnce).to.equal(true); + expect(removeDataFromLocalStorageStub.calledOnce).to.equal(true); + expect(cookieData.cto_bundle).to.equal(''); + expect(lsData.cto_bundle).to.equal(''); + expect(ajaxStub.calledOnce).to.equal(true); + }); }); describe('isBidRequestValid', function () { From 9d7c79b9f52d6ac62a8eace27f10b169eb792dfd Mon Sep 17 00:00:00 2001 From: Saar Amrani Date: Tue, 25 Jul 2023 17:29:40 +0300 Subject: [PATCH 78/92] Vidazoo Bid Adapter: pass gpp to user sync (#10274) * feat(module): multi size request * fix getUserSyncs added tests * update(module): package-lock.json from master * feat(module): VidazooBidAdapter - send top query params to server * multi poc * feat: added support for single request. * feat: change to bids. * change to if else * Fixed test * feat(client): added gpp to userSync --------- Co-authored-by: Udi Talias Co-authored-by: roman --- modules/vidazooBidAdapter.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/vidazooBidAdapter.js b/modules/vidazooBidAdapter.js index 71b9eae29a52..ba21a9c82d2b 100644 --- a/modules/vidazooBidAdapter.js +++ b/modules/vidazooBidAdapter.js @@ -320,13 +320,20 @@ function interpretResponse(serverResponse, request) { } } -function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '') { +function getUserSyncs(syncOptions, responses, gdprConsent = {}, uspConsent = '', gppConsent = {}) { let syncs = []; const {iframeEnabled, pixelEnabled} = syncOptions; const {gdprApplies, consentString = ''} = gdprConsent; + const {gppString, applicableSections} = gppConsent; const cidArr = responses.filter(resp => deepAccess(resp, 'body.cid')).map(resp => resp.body.cid).filter(uniques); - const params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}` + let params = `?cid=${encodeURIComponent(cidArr.join(','))}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${encodeURIComponent(consentString || '')}&us_privacy=${encodeURIComponent(uspConsent || '')}`; + + if (gppString && applicableSections?.length) { + params += '&gpp=' + encodeURIComponent(gppString); + params += '&gpp_sid=' + encodeURIComponent(applicableSections.join(',')); + } + if (iframeEnabled) { syncs.push({ type: 'iframe', From b12aa0e5a2a28869d609bee324a7db13c50eeb6f Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 25 Jul 2023 07:47:33 -0700 Subject: [PATCH 79/92] gppControl_usnat: update default rules for usnat consent interpretation (#10241) --- libraries/mspa/activityControls.js | 83 ++-- .../libraries/mspa/activityControls_spec.js | 357 +++++++----------- 2 files changed, 196 insertions(+), 244 deletions(-) diff --git a/libraries/mspa/activityControls.js b/libraries/mspa/activityControls.js index 3359862a5b3b..9c8e393f0641 100644 --- a/libraries/mspa/activityControls.js +++ b/libraries/mspa/activityControls.js @@ -8,7 +8,13 @@ import { import {gppDataHandler} from '../../src/adapterManager.js'; // default interpretation for MSPA consent(s): -// https://docs.google.com/document/d/1yPEVx3bBdSkIw-QNkQR5qi1ztmn9zoXk-LaGQB6iw7Q +// https://docs.prebid.org/features/mspa-usnat.html + +const SENSITIVE_DATA_GEO = 7; + +function isApplicable(val) { + return val != null && val !== 0 +} export function isBasicConsentDenied(cd) { // service provider mode is always consent denied @@ -18,47 +24,60 @@ export function isBasicConsentDenied(cd) { // minors 13+ who have not given consent cd.KnownChildSensitiveDataConsents[0] === 1 || // minors under 13 cannot consent - cd.KnownChildSensitiveDataConsents[1] !== 0 || - // sensitive category consent impossible without notice - (cd.SensitiveDataProcessing.some(element => element === 2) && (cd.SensitiveDataLimitUseNotice !== 1 || cd.SensitiveDataProcessingOptOutNotice !== 1)); + isApplicable(cd.KnownChildSensitiveDataConsents[1]); } -export function isSensitiveNoticeMissing(cd) { - return ['SensitiveDataProcessingOptOutNotice', 'SensitiveDataLimitUseNotice'].some(prop => cd[prop] === 2) +export function sensitiveNoticeIs(cd, value) { + return ['SensitiveDataProcessingOptOutNotice', 'SensitiveDataLimitUseNotice'].some(prop => cd[prop] === value) } export function isConsentDenied(cd) { return isBasicConsentDenied(cd) || - // user opts out - (['SaleOptOut', 'SharingOptOut', 'TargetedAdvertisingOptOut'].some(prop => cd[prop] === 1)) || - // notice not given - (['SaleOptOutNotice', 'SharingNotice', 'SharingOptOutNotice', 'TargetedAdvertisingOptOutNotice'].some(prop => cd[prop] === 2)) || - // sale opted in but notice does not apply - ((cd.SaleOptOut === 2 && cd.SaleOptOutNotice === 0)) || - // targeted opted in but notice does not apply - ((cd.TargetedAdvertisingOptOut === 2 && cd.TargetedAdvertisingOptOutNotice === 0)) || - // sharing opted in but notices do not apply - ((cd.SharingOptOut === 2 && (cd.SharingNotice === 0 || cd.SharingOptOutNotice === 0))); + ['Sale', 'Sharing', 'TargetedAdvertising'].some(scope => { + const oo = cd[`${scope}OptOut`]; + const notice = cd[`${scope}OptOutNotice`]; + // user opted out + return oo === 1 || + // opt-out notice was not given + notice === 2 || + // do not trust CMP if it signals opt-in but no opt-out notice was given + (oo === 2 && notice === 0); + }) || + // no sharing notice was given ... + cd.SharingNotice === 2 || + // ... or the CMP says it was not applicable, while also claiming it got consent + (cd.SharingOptOut === 2 && cd.SharingNotice === 0); } -export function isTransmitUfpdConsentDenied(cd) { - // SensitiveDataProcessing[1-5,11]=1 OR SensitiveDataProcessing[6,7,9,10,12]<>0 OR - const mustBeZero = [6, 7, 9, 10, 12]; - const mustBeZeroSubtractedVector = mustBeZero.map((number) => number - 1); - const SensitiveDataProcessingMustBeZero = cd.SensitiveDataProcessing.filter(index => mustBeZeroSubtractedVector.includes(index)); - const cannotBeOne = [1, 2, 3, 4, 5, 11]; - const cannotBeOneSubtractedVector = cannotBeOne.map((number) => number - 1); - const SensitiveDataProcessingCannotBeOne = cd.SensitiveDataProcessing.filter(index => cannotBeOneSubtractedVector.includes(index)); - return isConsentDenied(cd) || - isSensitiveNoticeMissing(cd) || - SensitiveDataProcessingCannotBeOne.some(val => val === 1) || - SensitiveDataProcessingMustBeZero.some(val => val !== 0); -} +export const isTransmitUfpdConsentDenied = (() => { + // deny anything that smells like: genetic, biometric, state/national ID, financial, union membership, + // or personal communication data + const cannotBeInScope = [6, 7, 9, 10, 12].map(el => --el); + // require consent for everything else (except geo, which is treated separately) + const allExceptGeo = Array.from(Array(12).keys()).filter((el) => el !== SENSITIVE_DATA_GEO) + const mustHaveConsent = allExceptGeo.filter(el => !cannotBeInScope.includes(el)); + + return function (cd) { + return isConsentDenied(cd) || + // no notice about sensitive data was given + sensitiveNoticeIs(cd, 2) || + // extra-sensitive data is applicable + cannotBeInScope.some(i => isApplicable(cd.SensitiveDataProcessing[i])) || + // user opted out for not-as-sensitive data + mustHaveConsent.some(i => cd.SensitiveDataProcessing[i] === 1) || + // CMP says it has consent, but did not give notice about it + (sensitiveNoticeIs(cd, 0) && allExceptGeo.some(i => cd.SensitiveDataProcessing[i] === 2)) + } +})(); export function isTransmitGeoConsentDenied(cd) { - return isBasicConsentDenied(cd) || - isSensitiveNoticeMissing(cd) || - cd.SensitiveDataProcessing[7] === 1 + const geoConsent = cd.SensitiveDataProcessing[SENSITIVE_DATA_GEO]; + return geoConsent === 1 || + isBasicConsentDenied(cd) || + // no sensitive data notice was given + sensitiveNoticeIs(cd, 2) || + // do not trust CMP if it says it has consent for geo but didn't show a sensitive data notice + (sensitiveNoticeIs(cd, 0) && geoConsent === 2) } const CONSENT_RULES = { diff --git a/test/spec/libraries/mspa/activityControls_spec.js b/test/spec/libraries/mspa/activityControls_spec.js index 5286f1d47f03..eab99dc43ee5 100644 --- a/test/spec/libraries/mspa/activityControls_spec.js +++ b/test/spec/libraries/mspa/activityControls_spec.js @@ -1,220 +1,153 @@ -import {mspaRule, setupRules, isTransmitUfpdConsentDenied, isTransmitGeoConsentDenied, isBasicConsentDenied, isSensitiveNoticeMissing, isConsentDenied} from '../../../../libraries/mspa/activityControls.js'; +import {mspaRule, setupRules, isTransmitUfpdConsentDenied, isTransmitGeoConsentDenied, isBasicConsentDenied, sensitiveNoticeIs, isConsentDenied} from '../../../../libraries/mspa/activityControls.js'; import {ruleRegistry} from '../../../../src/activities/rules.js'; -describe('isBasicConsentDenied', () => { - const cd = { - // not covered, opt in to targeted, sale, and share, all notices given, opt into precise geo - Gpc: 0, - KnownChildSensitiveDataConsents: [0, 0], - MspaCoveredTransaction: 2, - MspaOptOutOptionMode: 0, - MspaServiceProviderMode: 0, - PersonalDataConsents: 0, - SaleOptOut: 2, - SaleOptOutNotice: 1, - SensitiveDataLimitUseNotice: 1, - SensitiveDataProcessing: [0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0], - SensitiveDataProcessingOptOutNotice: 1, - SharingNotice: 1, - SharingOptOut: 2, - SharingOptOutNotice: 1, - TargetedAdvertisingOptOut: 2, - TargetedAdvertisingOptOutNotice: 1, - Version: 1 - }; - it('should be false (basic consent conditions pass) with variety of notice and opt in', () => { - const result = isBasicConsentDenied(cd); - expect(result).to.equal(false); - }); - it('should be true (basic consent conditions do not pass) with personal data consent set to true (invalid state)', () => { - cd.PersonalDataConsents = 2; - const result = isBasicConsentDenied(cd); - expect(result).to.equal(true); - cd.PersonalDataConsents = 0; - }); - it('should be true (basic consent conditions do not pass) with sensitive opt in but no notice', () => { - cd.SensitiveDataLimitUseNotice = 0; - const result = isBasicConsentDenied(cd); - expect(result).to.equal(true); - cd.SensitiveDataLimitUseNotice = 1; - }); -}) -describe('isSensitiveNoticeMissing', () => { - const cd = { - // not covered, opt in to targeted, sale, and share, all notices given, opt into precise geo - Gpc: 0, - KnownChildSensitiveDataConsents: [0, 0], - MspaCoveredTransaction: 2, - MspaOptOutOptionMode: 0, - MspaServiceProviderMode: 0, - PersonalDataConsents: 0, - SaleOptOut: 2, - SaleOptOutNotice: 1, - SensitiveDataLimitUseNotice: 1, - SensitiveDataProcessing: [0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0], - SensitiveDataProcessingOptOutNotice: 1, - SharingNotice: 1, - SharingOptOut: 2, - SharingOptOutNotice: 1, - TargetedAdvertisingOptOut: 2, - TargetedAdvertisingOptOutNotice: 1, - Version: 1 - }; - it('should be false (sensitive notice is given or not needed) with variety of notice and opt in', () => { - const result = isSensitiveNoticeMissing(cd); - expect(result).to.equal(false); - }); - it('should be true (sensitive notice is missing) with variety of notice and opt in', () => { - cd.SensitiveDataLimitUseNotice = 2; - const result = isSensitiveNoticeMissing(cd); - expect(result).to.equal(true); - cd.SensitiveDataLimitUseNotice = 1; - }); -}) -describe('isConsentDenied', () => { - const cd = { - // not covered, opt in to targeted, sale, and share, all notices given, opt into precise geo - Gpc: 0, - KnownChildSensitiveDataConsents: [0, 0], - MspaCoveredTransaction: 2, - MspaOptOutOptionMode: 0, - MspaServiceProviderMode: 0, - PersonalDataConsents: 0, - SaleOptOut: 2, - SaleOptOutNotice: 1, - SensitiveDataLimitUseNotice: 1, - SensitiveDataProcessing: [0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0], - SensitiveDataProcessingOptOutNotice: 1, - SharingNotice: 1, - SharingOptOut: 2, - SharingOptOutNotice: 1, - TargetedAdvertisingOptOut: 2, - TargetedAdvertisingOptOutNotice: 1, - Version: 1 - }; - it('should be false (consent given personalized ads / sale / share) with variety of notice and opt in', () => { - const result = isConsentDenied(cd); - expect(result).to.equal(false); - }); - it('should be true (no consent) on opt out of targeted ads via TargetedAdvertisingOptOut', () => { - cd.TargetedAdvertisingOptOut = 1; - const result = isConsentDenied(cd); - expect(result).to.equal(true); - cd.TargetedAdvertisingOptOut = 2; - }); - it('should be true (no consent) on opt out of targeted ads via no TargetedAdvertisingOptOutNotice', () => { - cd.TargetedAdvertisingOptOutNotice = 2; - const result = isConsentDenied(cd); - expect(result).to.equal(true); - cd.TargetedAdvertisingOptOutNotice = 1; - }); - it('should be true (no consent) if TargetedAdvertisingOptOutNotice is 0 and TargetedAdvertisingOptOut is 2', () => { - cd.TargetedAdvertisingOptOutNotice = 0; - const result = isConsentDenied(cd); - expect(result).to.equal(true); - cd.TargetedAdvertisingOptOutNotice = 1; - }); -}) -describe('isTransmitUfpdConsentDenied', () => { - const cd = { - // not covered, opt in to targeted, sale, and share, all notices given, opt into precise geo - Gpc: 0, - KnownChildSensitiveDataConsents: [0, 0], - MspaCoveredTransaction: 2, - MspaOptOutOptionMode: 0, - MspaServiceProviderMode: 0, - PersonalDataConsents: 0, - SaleOptOut: 2, - SaleOptOutNotice: 1, - SensitiveDataLimitUseNotice: 1, - SensitiveDataProcessing: [0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0], - SensitiveDataProcessingOptOutNotice: 1, - SharingNotice: 1, - SharingOptOut: 2, - SharingOptOutNotice: 1, - TargetedAdvertisingOptOut: 2, - TargetedAdvertisingOptOutNotice: 1, - Version: 1 - }; - it('should be false (consent given to add ufpd) with variety of notice and opt in', () => { - const result = isTransmitUfpdConsentDenied(cd); - expect(result).to.equal(false); - }); - it('should be true (consent denied to add ufpd) if no consent to process health information', () => { - cd.SensitiveDataProcessing[2] = 1; - const result = isTransmitUfpdConsentDenied(cd); - expect(result).to.equal(true); - cd.SensitiveDataProcessing[2] = 0; - }); - it('should be true (consent denied to add ufpd) with consent to process biometric data, as this should not be on openrtb', () => { - cd.SensitiveDataProcessing[6] = 1; - const result = isTransmitUfpdConsentDenied(cd); - expect(result).to.equal(true); - cd.SensitiveDataProcessing[6] = 1; - }); - it('should be true (consent denied to add ufpd) without sharing notice', () => { - cd.SharingNotice = 2; - const result = isTransmitUfpdConsentDenied(cd); - expect(result).to.equal(true); - cd.SharingNotice = 1; - }); - it('should be true (consent denied to add ufpd) with sale opt out', () => { - cd.SaleOptOut = 1; - const result = isTransmitUfpdConsentDenied(cd); - expect(result).to.equal(true); - cd.SaleOptOut = 2; - }); - it('should be true (consent denied to add ufpd) without targeted ads opt out', () => { - cd.TargetedAdvertisingOptOut = 1; - const result = isTransmitUfpdConsentDenied(cd); - expect(result).to.equal(true); - cd.TargetedAdvertisingOptOut = 2; - }); - it('should be true (consent denied to add ufpd) with missing sensitive data limit notice', () => { - cd.SensitiveDataLimitUseNotice = 2; - const result = isTransmitUfpdConsentDenied(cd); - expect(result).to.equal(true); - cd.SensitiveDataLimitUseNotice = 1; - }); -}) -describe('isTransmitGeoConsentDenied', () => { - const cd = { - // not covered, opt out of geo - Gpc: 0, - KnownChildSensitiveDataConsents: [0, 0], - MspaCoveredTransaction: 2, - MspaOptOutOptionMode: 0, - MspaServiceProviderMode: 0, - PersonalDataConsents: 0, - SaleOptOut: 2, - SaleOptOutNotice: 1, - SensitiveDataLimitUseNotice: 1, - SensitiveDataProcessing: [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], - SensitiveDataProcessingOptOutNotice: 1, - SharingNotice: 1, - SharingOptOut: 2, - SharingOptOutNotice: 1, - TargetedAdvertisingOptOut: 2, - TargetedAdvertisingOptOutNotice: 1, - Version: 1 - }; - it('should be true (consent denied to add precise geo) -- sensitive flag denied', () => { - const result = isTransmitGeoConsentDenied(cd); - expect(result).to.equal(true); +describe('Consent interpretation', () => { + function mkConsent(flags) { + return Object.assign({ + // not covered, opt in to targeted, sale, and share, all notices given, opt into precise geo + Gpc: 0, + KnownChildSensitiveDataConsents: [0, 0], + MspaCoveredTransaction: 2, + MspaOptOutOptionMode: 0, + MspaServiceProviderMode: 0, + PersonalDataConsents: 0, + SaleOptOut: 2, + SaleOptOutNotice: 1, + SensitiveDataLimitUseNotice: 1, + SensitiveDataProcessing: [0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0], + SensitiveDataProcessingOptOutNotice: 1, + SharingNotice: 1, + SharingOptOut: 2, + SharingOptOutNotice: 1, + TargetedAdvertisingOptOut: 2, + TargetedAdvertisingOptOutNotice: 1, + Version: 1 + }, flags) + } + describe('isBasicConsentDenied', () => { + it('should be false (basic consent conditions pass) with variety of notice and opt in', () => { + const result = isBasicConsentDenied(mkConsent()); + expect(result).to.equal(false); + }); + it('should be true (basic consent conditions do not pass) with personal data consent set to true (invalid state)', () => { + const result = isBasicConsentDenied(mkConsent({ + PersonalDataConsents: 2 + })); + expect(result).to.equal(true); + }); + it('should not deny when consent for under-13 is null', () => { + expect(isBasicConsentDenied(mkConsent({ + KnownChildSensitiveDataConsents: [0, null] + }))).to.be.false; + }) }); - it('should be true (consent denied to add precise geo) -- sensitive data limit usage not given', () => { - cd.SensitiveDataLimitUseNotice = 0; - const result = isTransmitGeoConsentDenied(cd); - expect(result).to.equal(true); - cd.SensitiveDataLimitUseNotice = 1; + + describe('isConsentDenied', () => { + it('should be false (consent given personalized ads / sale / share) with variety of notice and opt in', () => { + const result = isConsentDenied(mkConsent()); + expect(result).to.equal(false); + }); + it('should be true (no consent) on opt out of targeted ads via TargetedAdvertisingOptOut', () => { + const result = isConsentDenied(mkConsent({ + TargetedAdvertisingOptOut: 1 + })); + expect(result).to.equal(true); + }); + it('should be true (no consent) on opt out of targeted ads via no TargetedAdvertisingOptOutNotice', () => { + const result = isConsentDenied(mkConsent({ + TargetedAdvertisingOptOutNotice: 2 + })); + expect(result).to.equal(true); + }); + it('should be true (no consent) if TargetedAdvertisingOptOutNotice is 0 and TargetedAdvertisingOptOut is 2', () => { + const result = isConsentDenied(mkConsent({ + TargetedAdvertisingOptOutNotice: 0 + })); + expect(result).to.equal(true); + }); + it('requires also SharingNotice to accept opt-in for Sharing', () => { + expect(isConsentDenied(mkConsent({ + SharingNotice: 0 + }))).to.be.true; + }) }); - it('should be false (consent given to add precise geo) -- sensitive position 8 (index 7) is true', () => { - cd.SensitiveDataProcessing[7] = 2; - const result = isTransmitGeoConsentDenied(cd); - expect(result).to.equal(false); - cd.SensitiveDataProcessing[7] = 1; + + describe('isTransmitUfpdConsentDenied', () => { + it('should be false (consent given to add ufpd) with variety of notice and opt in', () => { + const result = isTransmitUfpdConsentDenied(mkConsent()); + expect(result).to.equal(false); + }); + Object.entries({ + 'health information': 2, + 'biometric data': 6, + }).forEach(([t, flagNo]) => { + it(`'should be true (consent denied to add ufpd) if no consent to process ${t}'`, () => { + const consent = mkConsent(); + consent.SensitiveDataProcessing[flagNo] = 1; + expect(isTransmitUfpdConsentDenied(consent)).to.be.true; + }) + }); + + ['SharingNotice', 'SensitiveDataLimitUseNotice'].forEach(flag => { + it(`should be true (consent denied to add ufpd) without ${flag}`, () => { + expect(isTransmitUfpdConsentDenied(mkConsent({ + [flag]: 2 + }))).to.be.true; + }) + }); + + ['SaleOptOut', 'TargetedAdvertisingOptOut'].forEach(flag => { + it(`should be true (consent denied to add ufpd) with ${flag}`, () => { + expect(isTransmitUfpdConsentDenied(mkConsent({ + [flag]: 1 + }))).to.be.true; + }) + }); + + it('should be true (basic consent conditions do not pass) with sensitive opt in but no notice', () => { + const cd = mkConsent({ + SensitiveDataLimitUseNotice: 0 + }); + cd.SensitiveDataProcessing[0] = 2; + expect(isTransmitUfpdConsentDenied(cd)).to.be.true; + }); + + it('should deny when sensitive notice is missing', () => { + const result = isTransmitUfpdConsentDenied(mkConsent({ + SensitiveDataLimitUseNotice: 2 + })); + expect(result).to.equal(true); + }); + + it('should not deny when biometric data opt-out is null', () => { + const cd = mkConsent(); + cd.SensitiveDataProcessing[6] = null; + expect(isTransmitUfpdConsentDenied(cd)).to.be.false; + }) }); -}) + + describe('isTransmitGeoConsentDenied', () => { + function geoConsent(geoOptOut, flags) { + const consent = mkConsent(flags); + consent.SensitiveDataProcessing[7] = geoOptOut; + return consent; + } + it('should be true (consent denied to add precise geo) -- sensitive flag denied', () => { + const result = isTransmitGeoConsentDenied(geoConsent(1)); + expect(result).to.equal(true); + }); + it('should be true (consent denied to add precise geo) -- sensitive data limit usage not given', () => { + const result = isTransmitGeoConsentDenied(geoConsent(1, { + SensitiveDataLimitUseNotice: 0 + })); + expect(result).to.equal(true); + }); + it('should be false (consent given to add precise geo) -- sensitive position 8 (index 7) is true', () => { + const result = isTransmitGeoConsentDenied(geoConsent(2)); + expect(result).to.equal(false); + }); + }) +}); describe('mspaRule', () => { it('does not apply if SID is not applicable', () => { From e53bed156432d55cfbaece84780ea504d8bc836c Mon Sep 17 00:00:00 2001 From: Karim Mourra Date: Tue, 25 Jul 2023 11:55:47 -0300 Subject: [PATCH 80/92] Video Module: add demos for intercepting bids with bidsBackHandler (#10225) * adds bids back handler ovrride demo * adds JW Player demo --- .../videoModule/jwplayer/bidMarkedAsUsed.html | 2 +- .../jwplayer/bidRequestScheduling.html | 2 +- .../jwplayer/bidsBackHandlerOverride.html | 137 +++++++++++++++ .../videoModule/jwplayer/eventListeners.html | 2 +- .../videoModule/jwplayer/eventsUI.html | 2 +- .../jwplayer/gamAdServerMediation.html | 2 +- .../videoModule/jwplayer/mediaMetadata.html | 2 +- .../videoModule/jwplayer/playlist.html | 2 +- .../videojs/bidsBackHandlerOverride.html | 164 ++++++++++++++++++ modules/videojsVideoProvider.js | 11 +- 10 files changed, 317 insertions(+), 9 deletions(-) create mode 100644 integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html create mode 100644 integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html diff --git a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html index 6aa996783545..80ea81d09b6c 100644 --- a/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html +++ b/integrationExamples/videoModule/jwplayer/bidMarkedAsUsed.html @@ -37,7 +37,7 @@ divId: 'player', vendorCode: 1, // jwplayer vendorCode playerConfig: { - licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', + licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', diff --git a/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html b/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html index 620f891fa501..663765317b02 100644 --- a/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html +++ b/integrationExamples/videoModule/jwplayer/bidRequestScheduling.html @@ -20,7 +20,7 @@ divId: 'player', vendorCode: 1, // JW Player vendorCode playerConfig: { - licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', + licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', diff --git a/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html b/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html new file mode 100644 index 000000000000..66eaff260903 --- /dev/null +++ b/integrationExamples/videoModule/jwplayer/bidsBackHandlerOverride.html @@ -0,0 +1,137 @@ + + + + + + + JW Player with Bids Back Handler override + + + + + +

JW Player with Bids Back Handler override

+
Div-1: Player placeholder div
+
+ + + diff --git a/integrationExamples/videoModule/jwplayer/eventListeners.html b/integrationExamples/videoModule/jwplayer/eventListeners.html index 5332270f5609..39acb086107b 100644 --- a/integrationExamples/videoModule/jwplayer/eventListeners.html +++ b/integrationExamples/videoModule/jwplayer/eventListeners.html @@ -37,7 +37,7 @@ divId: 'player', vendorCode: 1, // vendorCode for jwplayer playerConfig: { - licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', + licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', diff --git a/integrationExamples/videoModule/jwplayer/eventsUI.html b/integrationExamples/videoModule/jwplayer/eventsUI.html index bdfe5f3b883b..78d72a6153d9 100644 --- a/integrationExamples/videoModule/jwplayer/eventsUI.html +++ b/integrationExamples/videoModule/jwplayer/eventsUI.html @@ -39,7 +39,7 @@ divId: 'player', vendorCode: 1, // jwplayer vendorCode playerConfig: { - licenseKey: '577+5vXsluqV2Uy0drAS8wrgiqJlYijZxz3DmoYDm8FTJjdoIe8zYA==', + licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', params: { vendorConfig: { playlist: [{ diff --git a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html index fc7c1b9486cf..018c8eba8b2e 100644 --- a/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html +++ b/integrationExamples/videoModule/jwplayer/gamAdServerMediation.html @@ -36,7 +36,7 @@ divId: 'player', vendorCode: 1, // JW Player vendorCode playerConfig: { - licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', + licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', params: { vendorConfig: { file: 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/SubaruOutbackOnStreetAndDirt.mp4', diff --git a/integrationExamples/videoModule/jwplayer/mediaMetadata.html b/integrationExamples/videoModule/jwplayer/mediaMetadata.html index 03f74f5bd0fd..7581af571d37 100644 --- a/integrationExamples/videoModule/jwplayer/mediaMetadata.html +++ b/integrationExamples/videoModule/jwplayer/mediaMetadata.html @@ -37,7 +37,7 @@ divId: 'player', vendorCode: 1, // JW Player vendorCode playerConfig: { - licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', + licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', params: { vendorConfig: { mediaid: 'XYXYXYXY', diff --git a/integrationExamples/videoModule/jwplayer/playlist.html b/integrationExamples/videoModule/jwplayer/playlist.html index 223fee15c6fb..89efaea3d5c4 100644 --- a/integrationExamples/videoModule/jwplayer/playlist.html +++ b/integrationExamples/videoModule/jwplayer/playlist.html @@ -38,7 +38,7 @@ vendorCode: 1, // JW Player vendorCode playerConfig: { params: { - licenseKey: 'IAjLREYRLylTWsfLN3FoN/O3iQLbs+AfgZLlkAoyH8gSf7TnNtmOLcR8CUY=', + licenseKey: 'zwqnWJlovTKhXv2JIcKBj0Si//K7cVPmBDEyaILcAMw+nVKaizsJRA==', vendorConfig: { playlist: [{ mediaid: 'XYXYXYXY', diff --git a/integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html b/integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html new file mode 100644 index 000000000000..ac8f4163e76d --- /dev/null +++ b/integrationExamples/videoModule/videojs/bidsBackHandlerOverride.html @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + VideoJS with Bids Back Handler override + + + + + +

VideoJS with Bids Back Handler override

+
Div-1: Player placeholder div
+ + + + + + diff --git a/modules/videojsVideoProvider.js b/modules/videojsVideoProvider.js index 23a23447faac..7764e8af995d 100644 --- a/modules/videojsVideoProvider.js +++ b/modules/videojsVideoProvider.js @@ -201,8 +201,15 @@ export function VideojsProvider(providerConfig, vjs_, adState_, timeState_, call return; } - player.ima.changeAdTag(adTagUrl); - player.ima.requestAds(); + // The VideoJS IMA plugin version 1.11.0 will throw when the ad is empty. + try { + player.ima.changeAdTag(adTagUrl); + player.ima.requestAds(); + } catch (e) { + /* + Handling is not required; ad errors are emitted automatically by video.js + */ + } } function onEvent(type, callback, payload) { From 8f5c363fb32f9745f87a596aedcd6e41d6fc92c5 Mon Sep 17 00:00:00 2001 From: "Takaaki.Kojima" Date: Wed, 26 Jul 2023 00:05:18 +0900 Subject: [PATCH 81/92] Update AdGenerationAdapter: fix userSync (#10269) --- modules/adgenerationBidAdapter.js | 6 +++--- test/spec/modules/adgenerationBidAdapter_spec.js | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/adgenerationBidAdapter.js b/modules/adgenerationBidAdapter.js index f18b7629d8f1..9cd7fbc1b80a 100644 --- a/modules/adgenerationBidAdapter.js +++ b/modules/adgenerationBidAdapter.js @@ -28,7 +28,7 @@ export const spec = { buildRequests: function (validBidRequests, bidderRequest) { // convert Native ORTB definition to old-style prebid native definition validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - const ADGENE_PREBID_VERSION = '1.6.0'; + const ADGENE_PREBID_VERSION = '1.6.1'; let serverRequests = []; for (let i = 0, len = validBidRequests.length; i < len; i++) { const validReq = validBidRequests[i]; @@ -59,9 +59,9 @@ export const spec = { data = tryAppendQueryString(data, 'adgext_id5_id_link_type', id5LinkType); data = tryAppendQueryString(data, 'adgext_imuid', imuid); data = tryAppendQueryString(data, 'adgext_uid2', validReq.userId ? validReq.userId.uid2 : null); - data = tryAppendQueryString(data, 'gpid', gpid ? encodeURIComponent(gpid) : null); + data = tryAppendQueryString(data, 'gpid', gpid || null); data = tryAppendQueryString(data, 'uach', sua ? JSON.stringify(sua) : null); - data = tryAppendQueryString(data, 'schain', validReq.schain ? encodeURIComponent(JSON.stringify(validReq.schain)) : null); + data = tryAppendQueryString(data, 'schain', validReq.schain ? JSON.stringify(validReq.schain) : null); // native以外にvideo等の対応が入った場合は要修正 if (!validReq.mediaTypes || !validReq.mediaTypes.native) { diff --git a/test/spec/modules/adgenerationBidAdapter_spec.js b/test/spec/modules/adgenerationBidAdapter_spec.js index de8463731e0d..55ba56542451 100644 --- a/test/spec/modules/adgenerationBidAdapter_spec.js +++ b/test/spec/modules/adgenerationBidAdapter_spec.js @@ -184,12 +184,12 @@ describe('AdgenerationAdapter', function () { } }; const data = { - banner: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&imark=1&tp=https%3A%2F%2Fexample.com`, - bannerUSD: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=USD&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&imark=1&tp=https%3A%2F%2Fexample.com`, - native: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=1x1¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&tp=https%3A%2F%2Fexample.com`, - bannerWithHyperId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&imark=1&tp=https%3A%2F%2Fexample.com&hyper_id=novatiqId`, - bannerWithAdgextCriteoId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&adgext_criteo_id=criteo-id-test-1234567890&imark=1&tp=https%3A%2F%2Fexample.com`, - bannerWithAdgextIds: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.0&adgext_id5_id=id5-id-test-1234567890&adgext_id5_id_link_type=2&adgext_imuid=i.KrAH6ZAZTJOnH5S4N2sogA&adgext_uid2=%5Bobject%20Object%5D&gpid=%252F1111%252Fhomepage%2523300x250&uach=%7B%22source%22%3A2%2C%22platform%22%3A%7B%22brand%22%3A%22macOS%22%7D%2C%22browsers%22%3A%5B%7B%22brand%22%3A%22Chromium%22%2C%22version%22%3A%5B%22112%22%5D%7D%2C%7B%22brand%22%3A%22Google%20Chrome%22%2C%22version%22%3A%5B%22112%22%5D%7D%2C%7B%22brand%22%3A%22Not%3AA-Brand%22%2C%22version%22%3A%5B%2299%22%5D%7D%5D%2C%22mobile%22%3A0%7D&schain=%257B%2522ver%2522%253A%25221.0%2522%252C%2522complete%2522%253A1%252C%2522nodes%2522%253A%255B%257B%2522asi%2522%253A%2522indirectseller.com%2522%252C%2522sid%2522%253A%252200001%2522%252C%2522hp%2522%253A1%257D%255D%257D&imark=1&tp=https%3A%2F%2Fexample.com`, + banner: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.1&imark=1&tp=https%3A%2F%2Fexample.com`, + bannerUSD: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=300x250%2C320x100¤cy=USD&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.1&imark=1&tp=https%3A%2F%2Fexample.com`, + native: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=1x1¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.1&tp=https%3A%2F%2Fexample.com`, + bannerWithHyperId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.1&imark=1&tp=https%3A%2F%2Fexample.com&hyper_id=novatiqId`, + bannerWithAdgextCriteoId: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.1&adgext_criteo_id=criteo-id-test-1234567890&imark=1&tp=https%3A%2F%2Fexample.com`, + bannerWithAdgextIds: `posall=SSPLOC&id=58278&sdktype=0&hb=true&t=json3&sizes=320x100¤cy=JPY&pbver=${prebid.version}&sdkname=prebidjs&adapterver=1.6.1&adgext_id5_id=id5-id-test-1234567890&adgext_id5_id_link_type=2&adgext_imuid=i.KrAH6ZAZTJOnH5S4N2sogA&adgext_uid2=%5Bobject%20Object%5D&gpid=%2F1111%2Fhomepage%23300x250&uach=%7B%22source%22%3A2%2C%22platform%22%3A%7B%22brand%22%3A%22macOS%22%7D%2C%22browsers%22%3A%5B%7B%22brand%22%3A%22Chromium%22%2C%22version%22%3A%5B%22112%22%5D%7D%2C%7B%22brand%22%3A%22Google%20Chrome%22%2C%22version%22%3A%5B%22112%22%5D%7D%2C%7B%22brand%22%3A%22Not%3AA-Brand%22%2C%22version%22%3A%5B%2299%22%5D%7D%5D%2C%22mobile%22%3A0%7D&schain=%7B%22ver%22%3A%221.0%22%2C%22complete%22%3A1%2C%22nodes%22%3A%5B%7B%22asi%22%3A%22indirectseller.com%22%2C%22sid%22%3A%2200001%22%2C%22hp%22%3A1%7D%5D%7D&imark=1&tp=https%3A%2F%2Fexample.com`, }; it('sends bid request to ENDPOINT via GET', function () { const request = spec.buildRequests(bidRequests, bidderRequest)[0]; From 608ab245aef7f3c34ee5ebd5a3ce7b6c27ba1da5 Mon Sep 17 00:00:00 2001 From: Espen <2290914+espen-j@users.noreply.github.com> Date: Tue, 25 Jul 2023 18:16:40 +0200 Subject: [PATCH 82/92] C Wire Adapter: Add support for user-syncs (c-wire/prebid#8) (#10272) --- modules/cwireBidAdapter.js | 19 +++++++++ test/spec/modules/cwireBidAdapter_spec.js | 47 +++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/modules/cwireBidAdapter.js b/modules/cwireBidAdapter.js index 604d7235d0fc..f158e16a64e3 100644 --- a/modules/cwireBidAdapter.js +++ b/modules/cwireBidAdapter.js @@ -2,6 +2,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js'; import {getStorageManager} from '../src/storageManager.js'; import {BANNER} from '../src/mediaTypes.js'; import {generateUUID, getParameterByName, isNumber, logError, logInfo} from '../src/utils.js'; +import {hasPurpose1Consent} from '../src/utils/gpdr.js'; // ------------------------------------ const BIDDER_CODE = 'cwire'; @@ -230,5 +231,23 @@ export const spec = { navigator.sendBeacon(EVENT_ENDPOINT, JSON.stringify(event)) }, + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + logInfo('Collecting user-syncs: ', JSON.stringify({syncOptions, gdprConsent, uspConsent, serverResponses})); + + const syncs = [] + if (hasPurpose1Consent(gdprConsent)) { + logInfo('GDPR purpose 1 consent was given, adding user-syncs') + let type = (syncOptions.pixelEnabled) ? 'image' : null ?? (syncOptions.iframeEnabled) ? 'iframe' : null + if (type) { + syncs.push({ + type: type, + url: 'https://ib.adnxs.com/getuid?https://prebid.cwi.re/v1/cookiesync?xandrId=$UID' + }) + } + } + logInfo('Collected user-syncs: ', JSON.stringify({syncs})) + return syncs + } + }; registerBidder(spec); diff --git a/test/spec/modules/cwireBidAdapter_spec.js b/test/spec/modules/cwireBidAdapter_spec.js index 88c54212aff7..8eedcdb4a076 100644 --- a/test/spec/modules/cwireBidAdapter_spec.js +++ b/test/spec/modules/cwireBidAdapter_spec.js @@ -293,4 +293,51 @@ describe('C-WIRE bid adapter', () => { expect(bids[0].ad).to.exist; }) }); + + describe('add user-syncs', function () { + it('empty user-syncs if no consent given', function () { + const userSyncs = spec.getUserSyncs({}, {}, {}, {}); + + expect(userSyncs).to.be.empty + }) + it('empty user-syncs if no syncOption enabled', function () { + let gdprConsent = { + vendorData: { + purpose: { + consents: 1 + } + }}; + const userSyncs = spec.getUserSyncs({}, {}, gdprConsent, {}); + + expect(userSyncs).to.be.empty + }) + + it('user-syncs with enabled pixel option', function () { + let gdprConsent = { + vendorData: { + purpose: { + consents: 1 + } + }}; + let synOptions = {pixelEnabled: true, iframeEnabled: true}; + const userSyncs = spec.getUserSyncs(synOptions, {}, gdprConsent, {}); + + expect(userSyncs[0].type).to.equal('image'); + expect(userSyncs[0].url).to.equal('https://ib.adnxs.com/getuid?https://prebid.cwi.re/v1/cookiesync?xandrId=$UID'); + }) + + it('user-syncs with enabled iframe option', function () { + let gdprConsent = { + vendorData: { + purpose: { + consents: 1 + } + }}; + let synOptions = {iframeEnabled: true}; + const userSyncs = spec.getUserSyncs(synOptions, {}, gdprConsent, {}); + + expect(userSyncs[0].type).to.equal('iframe'); + expect(userSyncs[0].url).to.equal('https://ib.adnxs.com/getuid?https://prebid.cwi.re/v1/cookiesync?xandrId=$UID'); + }) + }) }); From 8c46315135d2d225c06da5021aef51606a74a078 Mon Sep 17 00:00:00 2001 From: newspassid-prebid <107485317+newspassid-prebid@users.noreply.github.com> Date: Tue, 25 Jul 2023 18:29:07 +0100 Subject: [PATCH 83/92] NewspassId Bid Adapter: tid logic change and GPID Module Support (#10254) * adapter updates GPID support removal of source.tid * updated tests to support latest adapter changes * MD file added --- modules/newspassid.md | 76 +++++++++++++++++++ modules/newspassidBidAdapter.js | 62 ++++++++++----- .../spec/modules/newspassidBidAdapter_spec.js | 6 ++ 3 files changed, 126 insertions(+), 18 deletions(-) create mode 100644 modules/newspassid.md diff --git a/modules/newspassid.md b/modules/newspassid.md new file mode 100644 index 000000000000..6fa709e5ba62 --- /dev/null +++ b/modules/newspassid.md @@ -0,0 +1,76 @@ +--- +Module Name: NewspassId Bidder Adapter +Module Type: Bidder Adapter +Maintainer: techsupport@newspassid.com +layout: bidder +title: Newspass ID +description: LMC Newspass ID Prebid JS Bidder Adapter +biddercode: newspassid +gdpr_supported: false +gvl_id: none +usp_supported: true +coppa_supported: false +schain_supported: true +dchain_supported: false +userIds: criteo, id5Id, tdid, identityLink, liveIntentId, parrableId, pubCommonId, lotamePanoramaId, sharedId, fabrickId +media_types: banner +safeframes_ok: true +deals_supported: true +floors_supported: false +fpd_supported: false +pbjs: true +pbs: false +prebid_member: false +multiformat_supported: will-bid-on-any +--- + +### Description + +LMC Newspass ID Prebid JS Bidder Adapter that connects to the NewspassId demand source(s). + +The Newspass bid adapter supports Banner mediaTypes ONLY. +This is intended for USA audiences only, and does not support GDPR + + +### Bid Params + +{: .table .table-bordered .table-striped } + +| Name | Scope | Description | Example | Type | +|-----------|----------|---------------------------|------------|----------| +| `siteId` | required | The site ID. | `"NPID0000001"` | `string` | +| `publisherId` | required | The publisher ID. | `"4204204201"` | `string` | +| `placementId` | required | The placement ID. | `"0420420421"` | `string` | +| `customData` | optional | publisher key-values used for targeting | `[{"settings":{},"targeting":{"key1": "value1", "key2": "value2"}}], ` | `array` | + +### Test Parameters + + +A test ad unit that will consistently return test creatives: + +``` + +//Banner adUnit + +adUnits = [{ + code: 'id-of-your-banner-div', + mediaTypes: { + banner: { + sizes: [[300, 250], [300,600]] + } + }, + bids: [{ + bidder: 'newspassid', + params: { + publisherId: 'NEWSPASS0001', /* an ID to identify the publisher account - required */ + siteId: '4204204201', /* An ID used to identify a site within a publisher account - required */ + placementId: '8000000015', /* an ID used to identify the piece of inventory - required - for appnexus test use 13144370. */ + customData: [{"settings": {}, "targeting": {"key": "value", "key2": ["value1", "value2"]}}],/* optional array with 'targeting' placeholder for passing publisher specific key-values for targeting. */ + } + }] + }]; +``` + +### Note: + +Please contact us at techsupport@newspassid.com for any assistance testing your implementation before going live into production. diff --git a/modules/newspassidBidAdapter.js b/modules/newspassidBidAdapter.js index b440edc2bebc..2a4b2da186b1 100644 --- a/modules/newspassidBidAdapter.js +++ b/modules/newspassidBidAdapter.js @@ -1,15 +1,24 @@ -import {contains, deepAccess, deepSetValue, isArray, logError, logInfo, logWarn, parseUrl} from '../src/utils.js'; -import {registerBidder} from '../src/adapters/bidderFactory.js'; -import {BANNER, NATIVE} from '../src/mediaTypes.js'; +import { + logInfo, + logError, + deepAccess, + logWarn, + deepSetValue, + isArray, + contains, + parseUrl, + generateUUID +} from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; import {config} from '../src/config.js'; import {getPriceBucketString} from '../src/cpmBucketManager.js'; import {getRefererInfo} from '../src/refererDetection.js'; - const BIDDER_CODE = 'newspassid'; const ORIGIN = 'https://bidder.newspassid.com' // applies only to auction & cookie const AUCTIONURI = '/openrtb2/auction'; const NEWSPASSCOOKIESYNC = '/static/load-cookie.html'; -const NEWSPASSVERSION = '1.0.1'; +const NEWSPASSVERSION = '1.1.4'; export const spec = { version: NEWSPASSVERSION, code: BIDDER_CODE, @@ -155,7 +164,7 @@ export const spec = { let placementId = placementIdOverrideFromGetParam || this.getPlacementId(npBidRequest); // prefer to use a valid override param, else the bidRequest placement Id obj.id = npBidRequest.bidId; // this causes an error if we change it to something else, even if you update the bidRequest object: "WARNING: Bidder newspass made bid for unknown request ID: mb7953.859498327448. Ignoring." obj.tagid = placementId; - let parsed = parseUrl(getRefererInfo().page); + let parsed = parseUrl(this.getRefererInfo().page); obj.secure = parsed.protocol === 'https' ? 1 : 0; let arrBannerSizes = []; if (!npBidRequest.hasOwnProperty('mediaTypes')) { @@ -189,7 +198,6 @@ export const spec = { deepSetValue(obj, 'ext.prebid', {'storedrequest': {'id': placementId}}); obj.ext['newspassid'] = {}; obj.ext['newspassid'].adUnitCode = npBidRequest.adUnitCode; // eg. 'mpu' - obj.ext['newspassid'].transactionId = npBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit if (npBidRequest.params.hasOwnProperty('customData')) { obj.ext['newspassid'].customData = npBidRequest.params.customData; } @@ -216,6 +224,10 @@ export const spec = { if (!schain && deepAccess(npBidRequest, 'schain')) { schain = npBidRequest.schain; } + let gpid = deepAccess(npBidRequest, 'ortb2Imp.ext.gpid'); + if (gpid) { + deepSetValue(obj, 'ext.gpid', gpid); + } return obj; }); let extObj = {}; @@ -240,7 +252,7 @@ export const spec = { let userExtEids = deepAccess(validBidRequests, '0.userIdAsEids', []); // generate the UserIDs in the correct format for UserId module npRequest.site = { 'publisher': {'id': htmlParams.publisherId}, - 'page': getRefererInfo().page, + 'page': this.getRefererInfo().page, 'id': htmlParams.siteId }; npRequest.test = config.getConfig('debug') ? 1 : 0; @@ -259,12 +271,9 @@ export const spec = { } if (singleRequest) { logInfo('buildRequests starting to generate response for a single request'); - // TODO: fix auctionId & transactionId leak: https://github.com/prebid/Prebid.js/issues/9781 - npRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange. - npRequest.auctionId = bidderRequest.auctionId; // not sure if this should be here? + npRequest.id = generateUUID(); // Unique ID of the bid request, provided by the exchange. (REQUIRED) npRequest.imp = tosendtags; npRequest.ext = extObj; - deepSetValue(npRequest, 'source.tid', bidderRequest.auctionId);// RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges). deepSetValue(npRequest, 'user.ext.eids', userExtEids); var ret = { method: 'POST', @@ -280,12 +289,9 @@ export const spec = { let arrRet = tosendtags.map(imp => { logInfo('buildRequests starting to generate non-single response, working on imp : ', imp); let npRequestSingle = Object.assign({}, npRequest); - imp.ext['newspassid'].pageAuctionId = bidderRequest['auctionId']; // make a note in the ext object of what the original auctionId was, in the bidderRequest object - npRequestSingle.id = imp.ext['newspassid'].transactionId; // Unique ID of the bid request, provided by the exchange. - npRequestSingle.auctionId = imp.ext['newspassid'].transactionId; // not sure if this should be here? + npRequestSingle.id = generateUUID(); npRequestSingle.imp = [imp]; npRequestSingle.ext = extObj; - deepSetValue(npRequestSingle, 'source.tid', bidderRequest.auctionId);// RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges). deepSetValue(npRequestSingle, 'user.ext.eids', userExtEids); logInfo('buildRequests RequestSingle (for non-single) = ', npRequestSingle); return { @@ -305,6 +311,7 @@ export const spec = { logInfo(`interpretResponse time: ${startTime}. buildRequests done -> interpretResponse start was ${startTime - this.propertyBag.buildRequestsEnd}ms`); logInfo(`serverResponse, request`, JSON.parse(JSON.stringify(serverResponse)), JSON.parse(JSON.stringify(request))); serverResponse = serverResponse.body || {}; + let aucId = serverResponse.id; // this will be correct for single requests and non-single if (!serverResponse.hasOwnProperty('seatbid')) { return []; } @@ -351,7 +358,7 @@ export const spec = { logInfo(`newspassid.enhancedAdserverTargeting is set to false, no per-bid keys will be sent to adserver.`); } let {seat: winningSeat, bid: winningBid} = this.getWinnerForRequestBid(thisBid.bidId, serverResponse.seatbid); - adserverTargeting['np_auc_id'] = String(request.bidderRequest.auctionId); + adserverTargeting['np_auc_id'] = String(aucId); adserverTargeting['np_winner'] = String(winningSeat); adserverTargeting['np_bid'] = 'true'; if (enhancedAdserverTargeting) { @@ -490,10 +497,29 @@ export const spec = { return null; }, getGetParametersAsObject() { - let parsed = parseUrl(getRefererInfo().page); + let parsed = parseUrl(this.getRefererInfo().location); // was getRefererInfo().page but this is not backwards compatible logInfo('getGetParametersAsObject found:', parsed.search); return parsed.search; }, + getRefererInfo() { + if (getRefererInfo().hasOwnProperty('location')) { + logInfo('FOUND location on getRefererInfo OK (prebid >= 7); will use getRefererInfo for location & page'); + return getRefererInfo(); + } else { + logInfo('DID NOT FIND location on getRefererInfo (prebid < 7); will use legacy code that ALWAYS worked reliably to get location & page ;-)'); + try { + return { + page: top.location.href, + location: top.location.href + }; + } catch (e) { + return { + page: window.location.href, + location: window.location.href + }; + } + } + }, blockTheRequest() { let npRequest = config.getConfig('newspassid.np_request'); if (typeof npRequest == 'boolean' && !npRequest) { diff --git a/test/spec/modules/newspassidBidAdapter_spec.js b/test/spec/modules/newspassidBidAdapter_spec.js index bec6eea7bf2a..6468d4f530a8 100644 --- a/test/spec/modules/newspassidBidAdapter_spec.js +++ b/test/spec/modules/newspassidBidAdapter_spec.js @@ -1667,6 +1667,12 @@ describe('newspassid Adapter', function () { expect(result[0]['price']).to.equal(0.9); expect(result[0]['adserverTargeting']['np_npappnexus_adId']).to.equal('2899ec066a91ff8-0-np-1'); }); + it('should add np_auc_id (response id value)', function () { + const request = spec.buildRequests(validBidRequests, validBidderRequest); + let validres = JSON.parse(JSON.stringify(validBidResponse1adWith2Bidders)); + const result = spec.interpretResponse(validres, request); + expect(utils.deepAccess(result[0].adserverTargeting, 'np_auc_id')).to.equal(validBidResponse1adWith2Bidders.body.id); + }); it('should correctly process an auction with 2 adunits & multiple bidders one of which bids for both adslots', function() { let validres = JSON.parse(JSON.stringify(multiResponse1)); let request = spec.buildRequests(multiRequest1, multiBidderRequest1.bidderRequest); From c06331966f90f1bed15fcc56b09adf791f2bb347 Mon Sep 17 00:00:00 2001 From: jsnellbaker <31102355+jsnellbaker@users.noreply.github.com> Date: Tue, 25 Jul 2023 17:01:22 -0400 Subject: [PATCH 84/92] appnexus bid adapter - update gpid logic (#10263) --- modules/appnexusBidAdapter.js | 2 +- test/spec/modules/appnexusBidAdapter_spec.js | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/appnexusBidAdapter.js b/modules/appnexusBidAdapter.js index 0660f4f4b107..ae4b1a0d489d 100644 --- a/modules/appnexusBidAdapter.js +++ b/modules/appnexusBidAdapter.js @@ -788,7 +788,7 @@ function bidToTag(bid) { tag.keywords = auKeywords; } - let gpid = deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); + let gpid = deepAccess(bid, 'ortb2Imp.ext.gpid') || deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); if (gpid) { tag.gpid = gpid; } diff --git a/test/spec/modules/appnexusBidAdapter_spec.js b/test/spec/modules/appnexusBidAdapter_spec.js index 13ef31a68d45..193e8aa64f88 100644 --- a/test/spec/modules/appnexusBidAdapter_spec.js +++ b/test/spec/modules/appnexusBidAdapter_spec.js @@ -1093,7 +1093,18 @@ describe('AppNexusAdapter', function () { expect(payload.tags[0].use_pmt_rule).to.equal(true); }); - it('should add gpid to the request', function () { + it('should add preferred gpid to the request', function () { + let testGpid = '/12345/my-gpt-tag-0'; + let bidRequest = deepClone(bidRequests[0]); + bidRequest.ortb2Imp = { ext: { gpid: testGpid } }; + + const request = spec.buildRequests([bidRequest]); + const payload = JSON.parse(request.data); + + expect(payload.tags[0].gpid).to.exist.and.equal(testGpid) + }); + + it('should add backup gpid to the request', function () { let testGpid = '/12345/my-gpt-tag-0'; let bidRequest = deepClone(bidRequests[0]); bidRequest.ortb2Imp = { ext: { data: { pbadslot: testGpid } } }; From bcf080d381d3f650a03e1808e89d5e517600b151 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Tue, 25 Jul 2023 14:03:18 -0700 Subject: [PATCH 85/92] topicsFpdModule: replace ad-hoc GDPR logic with `transmitUfpd` acitivity check; check for topics support before loading frames (#10201) * topicsFpdModule: replace ad-hoc GDPR logic with `transmitUfpd` acitivity check; check for topics support before loading frames * Use `enrichUfpd` instead of `transmitUfpd` --- modules/topicsFpdModule.js | 69 +-- test/spec/modules/topicsFpdModule_spec.js | 708 +++++++++++----------- 2 files changed, 364 insertions(+), 413 deletions(-) diff --git a/modules/topicsFpdModule.js b/modules/topicsFpdModule.js index 97d86d5aff25..14209d55ed36 100644 --- a/modules/topicsFpdModule.js +++ b/modules/topicsFpdModule.js @@ -1,18 +1,22 @@ -import {logError, logWarn, mergeDeep, isEmpty, safeJSONParse, logInfo, hasDeviceAccess} from '../src/utils.js'; +import {isEmpty, logError, logWarn, mergeDeep, safeJSONParse} from '../src/utils.js'; import {getRefererInfo} from '../src/refererDetection.js'; import {submodule} from '../src/hook.js'; import {GreedyPromise} from '../src/utils/promise.js'; import {config} from '../src/config.js'; import {getCoreStorageManager} from '../src/storageManager.js'; import {includes} from '../src/polyfill.js'; -import {gdprDataHandler} from '../src/adapterManager.js'; +import {isActivityAllowed} from '../src/activities/rules.js'; +import {ACTIVITY_ENRICH_UFPD} from '../src/activities/activities.js'; +import {activityParams} from '../src/activities/activityParams.js'; +import {MODULE_TYPE_BIDDER} from '../src/activities/modules.js'; const MODULE_NAME = 'topicsFpd'; const DEFAULT_EXPIRATION_DAYS = 21; -const TCF_REQUIRED_PURPOSES = ['1', '2', '3', '4']; -let HAS_GDPR_CONSENT = true; let LOAD_TOPICS_INITIALISE = false; -const HAS_DEVICE_ACCESS = hasDeviceAccess(); + +export function reset() { + LOAD_TOPICS_INITIALISE = false; +} const bidderIframeList = { maxTopicCaller: 2, @@ -86,10 +90,14 @@ export function getTopicsData(name, topics, taxonomies = TAXONOMIES) { ); } +function isTopicsSupported(doc = document) { + return 'browsingTopics' in doc && doc.featurePolicy.allowsFeature('browsing-topics') +} + export function getTopics(doc = document) { let topics = null; try { - if ('browsingTopics' in doc && doc.featurePolicy.allowsFeature('browsing-topics')) { + if (isTopicsSupported(doc)) { topics = GreedyPromise.resolve(doc.browsingTopics()); } } catch (e) { @@ -126,19 +134,16 @@ export function processFpd(config, {global}, {data = topicsData} = {}) { */ export function getCachedTopics() { let cachedTopicData = []; - if (!HAS_GDPR_CONSENT || !HAS_DEVICE_ACCESS) { - return cachedTopicData; - } const topics = config.getConfig('userSync.topics') || bidderIframeList; const bidderList = topics.bidders || []; let storedSegments = new Map(safeJSONParse(coreStorage.getDataFromLocalStorage(topicStorageName))); storedSegments && storedSegments.forEach((value, cachedBidder) => { // Check bidder exist in config for cached bidder data and then only retrieve the cached data - let bidderConfigObj = bidderList.find(({bidder}) => cachedBidder == bidder) - if (bidderConfigObj) { + let bidderConfigObj = bidderList.find(({bidder}) => cachedBidder === bidder) + if (bidderConfigObj && isActivityAllowed(ACTIVITY_ENRICH_UFPD, activityParams(MODULE_TYPE_BIDDER, cachedBidder))) { if (!isCachedDataExpired(value[lastUpdated], bidderConfigObj?.expiry || DEFAULT_EXPIRATION_DAYS)) { Object.keys(value).forEach((segData) => { - segData != lastUpdated && cachedTopicData.push(value[segData]); + segData !== lastUpdated && cachedTopicData.push(value[segData]); }) } else { // delete the specific bidder map from the store and store the updated maps @@ -204,55 +209,23 @@ function listenMessagesFromTopicIframe() { window.addEventListener('message', receiveMessage, false); } -function checkTCFv2(vendorData, requiredPurposes = TCF_REQUIRED_PURPOSES) { - const {gdprApplies, purpose} = vendorData; - if (!gdprApplies || !purpose) { - return true; - } - return requiredPurposes.map((purposeNo) => { - const purposeConsent = purpose.consents ? purpose.consents[purposeNo] : false; - if (purposeConsent) { - return true; - } - return false; - }).reduce((a, b) => a && b, true); -} - -export function hasGDPRConsent() { - // Check for GDPR consent for purpose 1,2,3,4 and return false if consent has not been given - const gdprConsent = gdprDataHandler.getConsentData(); - const hasGdpr = (gdprConsent && typeof gdprConsent.gdprApplies === 'boolean' && gdprConsent.gdprApplies) ? 1 : 0; - const gdprConsentString = hasGdpr ? gdprConsent.consentString : ''; - if (hasGdpr) { - if ((!gdprConsentString || gdprConsentString === '') || !gdprConsent.vendorData) { - return false; - } - return checkTCFv2(gdprConsent.vendorData); - } - return true; -} - /** * function to load the iframes of the bidder to load the topics data */ -function loadTopicsForBidders() { - HAS_GDPR_CONSENT = hasGDPRConsent(); - if (!HAS_GDPR_CONSENT || !HAS_DEVICE_ACCESS) { - logInfo('Topics Module : Consent string is required to fetch the topics from third party domains.'); - return; - } +export function loadTopicsForBidders(doc = document) { + if (!isTopicsSupported(doc)) return; const topics = config.getConfig('userSync.topics') || bidderIframeList; if (topics) { listenMessagesFromTopicIframe(); const randomBidders = getRandomBidders(topics.bidders || [], topics.maxTopicCaller || 1) randomBidders && randomBidders.forEach(({ bidder, iframeURL }) => { if (bidder && iframeURL) { - let ifrm = document.createElement('iframe'); + let ifrm = doc.createElement('iframe'); ifrm.name = 'ifrm_'.concat(bidder); ifrm.src = ''.concat(iframeURL, '?bidder=').concat(bidder); ifrm.style.display = 'none'; setLoadedIframeURL(new URL(iframeURL).origin); - iframeURL && window.document.documentElement.appendChild(ifrm); + iframeURL && doc.documentElement.appendChild(ifrm); } }) } else { diff --git a/test/spec/modules/topicsFpdModule_spec.js b/test/spec/modules/topicsFpdModule_spec.js index 22d7a98d45d7..97af0e971c36 100644 --- a/test/spec/modules/topicsFpdModule_spec.js +++ b/test/spec/modules/topicsFpdModule_spec.js @@ -1,312 +1,313 @@ import { + getCachedTopics, getTopics, getTopicsData, + loadTopicsForBidders, processFpd, - hasGDPRConsent, - getCachedTopics, receiveMessage, + reset, topicStorageName } from '../../../modules/topicsFpdModule.js'; import {deepClone, safeJSONParse} from '../../../src/utils.js'; -import {gdprDataHandler} from 'src/adapterManager.js'; import {getCoreStorageManager} from 'src/storageManager.js'; +import {config} from 'src/config.js'; +import * as activities from '../../../src/activities/rules.js'; +import {ACTIVITY_ENRICH_UFPD} from '../../../src/activities/activities.js'; -describe('getTopicsData', () => { - function makeTopic(topic, modelv, taxv = '1') { - return { - topic, - taxonomyVersion: taxv, - modelVersion: modelv - }; - } +describe('topics', () => { + beforeEach(() => { + reset(); + }); - function byTaxClass(segments) { - return segments.reduce((memo, segment) => { - memo[`${segment.ext.segtax}:${segment.ext.segclass}`] = segment; - return memo; - }, {}); - } + describe('getTopicsData', () => { + function makeTopic(topic, modelv, taxv = '1') { + return { + topic, + taxonomyVersion: taxv, + modelVersion: modelv + }; + } - [ - { - t: 'no topics', - topics: [], - expected: [] - }, - { - t: 'single topic', - topics: [makeTopic(123, 'm1')], - expected: [ - { - ext: { - segtax: 600, - segclass: 'm1' - }, - segment: [ - {id: '123'} - ] - } - ] - }, - { - t: 'multiple topics with the same model version', - topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1')], - expected: [ - { - ext: { - segtax: 600, - segclass: 'm1' - }, - segment: [ - {id: '123'}, - {id: '321'} - ] - } - ] - }, - { - t: 'multiple topics with different model versions', - topics: [makeTopic(1, 'm1'), makeTopic(2, 'm1'), makeTopic(3, 'm2')], - expected: [ - { - ext: { - segtax: 600, - segclass: 'm1' - }, - segment: [ - {id: '1'}, - {id: '2'} - ] - }, - { - ext: { - segtax: 600, - segclass: 'm2' - }, - segment: [ - {id: '3'} - ] - } - ] - }, - { - t: 'multiple topics, some with a taxonomy version other than "1"', - topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1', 'other')], - expected: [ - { - ext: { - segtax: 600, - segclass: 'm1' - }, - segment: [ - {id: '123'} - ] - } - ] - }, - { - t: 'multiple topics in multiple taxonomies', - taxonomies: { - '1': 600, - '2': 601 + function byTaxClass(segments) { + return segments.reduce((memo, segment) => { + memo[`${segment.ext.segtax}:${segment.ext.segclass}`] = segment; + return memo; + }, {}); + } + + [ + { + t: 'no topics', + topics: [], + expected: [] + }, + { + t: 'single topic', + topics: [makeTopic(123, 'm1')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] + } + ] + }, + { + t: 'multiple topics with the same model version', + topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'}, + {id: '321'} + ] + } + ] }, - topics: [ - makeTopic(123, 'm1', '1'), - makeTopic(321, 'm1', '2'), - makeTopic(213, 'm2', '1'), - ], - expected: [ - { - ext: { - segtax: 600, - segclass: 'm1' + { + t: 'multiple topics with different model versions', + topics: [makeTopic(1, 'm1'), makeTopic(2, 'm1'), makeTopic(3, 'm2')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '1'}, + {id: '2'} + ] }, - segment: [ - {id: '123'} - ] + { + ext: { + segtax: 600, + segclass: 'm2' + }, + segment: [ + {id: '3'} + ] + } + ] + }, + { + t: 'multiple topics, some with a taxonomy version other than "1"', + topics: [makeTopic(123, 'm1'), makeTopic(321, 'm1', 'other')], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] + } + ] + }, + { + t: 'multiple topics in multiple taxonomies', + taxonomies: { + '1': 600, + '2': 601 }, - { - ext: { - segtax: 601, - segclass: 'm1', + topics: [ + makeTopic(123, 'm1', '1'), + makeTopic(321, 'm1', '2'), + makeTopic(213, 'm2', '1'), + ], + expected: [ + { + ext: { + segtax: 600, + segclass: 'm1' + }, + segment: [ + {id: '123'} + ] }, - segment: [ - {id: '321'} - ] - }, - { - ext: { - segtax: 600, - segclass: 'm2' + { + ext: { + segtax: 601, + segclass: 'm1', + }, + segment: [ + {id: '321'} + ] }, - segment: [ - {id: '213'} - ] - } - ] - } - ].forEach(({t, topics, expected, taxonomies}) => { - describe(`on ${t}`, () => { - it('should convert topics to user.data segments correctly', () => { - const actual = getTopicsData('mockName', topics, taxonomies); - expect(actual.length).to.eql(expected.length); - expected = byTaxClass(expected); - Object.entries(byTaxClass(actual)).forEach(([key, datum]) => { - sinon.assert.match(datum, expected[key]); - expect(datum.name).to.equal('mockName'); + { + ext: { + segtax: 600, + segclass: 'm2' + }, + segment: [ + {id: '213'} + ] + } + ] + } + ].forEach(({t, topics, expected, taxonomies}) => { + describe(`on ${t}`, () => { + it('should convert topics to user.data segments correctly', () => { + const actual = getTopicsData('mockName', topics, taxonomies); + expect(actual.length).to.eql(expected.length); + expected = byTaxClass(expected); + Object.entries(byTaxClass(actual)).forEach(([key, datum]) => { + sinon.assert.match(datum, expected[key]); + expect(datum.name).to.equal('mockName'); + }); }); - }); - it('should not set name if null', () => { - getTopicsData(null, topics).forEach((data) => { - expect(data.hasOwnProperty('name')).to.be.false; + it('should not set name if null', () => { + getTopicsData(null, topics).forEach((data) => { + expect(data.hasOwnProperty('name')).to.be.false; + }); }); }); }); }); -}); -describe('getTopics', () => { - Object.entries({ - 'document with no browsingTopics': {}, - 'document that disallows topics': { - featurePolicy: { - allowsFeature: sinon.stub().returns(false) - } - }, - 'document that throws on featurePolicy': { - browsingTopics: sinon.stub(), - get featurePolicy() { - throw new Error(); - } - }, - 'document that throws on browsingTopics': { - browsingTopics: sinon.stub().callsFake(() => { - throw new Error(); - }), - featurePolicy: { - allowsFeature: sinon.stub().returns(true) - } - }, - }).forEach(([t, doc]) => { - it(`should resolve to an empty list on ${t}`, () => { - return getTopics(doc).then((topics) => { - expect(topics).to.eql([]); + describe('getTopics', () => { + Object.entries({ + 'document with no browsingTopics': {}, + 'document that disallows topics': { + featurePolicy: { + allowsFeature: sinon.stub().returns(false) + } + }, + 'document that throws on featurePolicy': { + browsingTopics: sinon.stub(), + get featurePolicy() { + throw new Error(); + } + }, + 'document that throws on browsingTopics': { + browsingTopics: sinon.stub().callsFake(() => { + throw new Error(); + }), + featurePolicy: { + allowsFeature: sinon.stub().returns(true) + } + }, + }).forEach(([t, doc]) => { + it(`should resolve to an empty list on ${t}`, () => { + return getTopics(doc).then((topics) => { + expect(topics).to.eql([]); + }); }); }); - }); - it('should call `document.browsingTopics` when allowed', () => { - const topics = ['t1', 't2']; - return getTopics({ - browsingTopics: sinon.stub().returns(Promise.resolve(topics)), - featurePolicy: { - allowsFeature: sinon.stub().returns(true) - } - }).then((actual) => { - expect(actual).to.eql(topics); - }); - }); -}); - -describe('processFpd', () => { - const mockData = [ - { - name: 'domain', - segment: [{id: 123}] - }, - { - name: 'domain', - segment: [{id: 321}] - } - ]; - - it('should add topics data', () => { - return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) - .then(({global}) => { - expect(global.user.data).to.eql(mockData); + it('should call `document.browsingTopics` when allowed', () => { + const topics = ['t1', 't2']; + return getTopics({ + browsingTopics: sinon.stub().returns(Promise.resolve(topics)), + featurePolicy: { + allowsFeature: sinon.stub().returns(true) + } + }).then((actual) => { + expect(actual).to.eql(topics); }); + }); }); - it('should apppend to existing user.data', () => { - const global = { - user: { - data: [ - {name: 'preexisting'}, - ] + describe('processFpd', () => { + const mockData = [ + { + name: 'domain', + segment: [{id: 123}] + }, + { + name: 'domain', + segment: [{id: 321}] } - }; - return processFpd({}, {global: deepClone(global)}, {data: Promise.resolve(mockData)}) - .then((data) => { - expect(data.global.user.data).to.eql(global.user.data.concat(mockData)); - }); - }); - - it('should not modify fpd when there is no data', () => { - return processFpd({}, {global: {}}, {data: Promise.resolve([])}) - .then((data) => { - expect(data.global).to.eql({}); - }); - }); -}); + ]; -describe('Topics Module GDPR consent check', () => { - let gdprDataHdlrStub; - beforeEach(() => { - gdprDataHdlrStub = sinon.stub(gdprDataHandler, 'getConsentData'); - }); - - afterEach(() => { - gdprDataHdlrStub.restore(); - }); - - it('should return false when GDPR is applied but consent string is not present', () => { - const consentString = ''; - const consentConfig = { - consentString: consentString, - gdprApplies: true, - vendorData: {} - }; - gdprDataHdlrStub.returns(consentConfig); - expect(hasGDPRConsent()).to.equal(false); - }); + it('should add topics data', () => { + return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) + .then(({global}) => { + expect(global.user.data).to.eql(mockData); + }); + }); - it('should return true when GDPR doesn\'t apply', () => { - const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; - const consentConfig = { - consentString: consentString, - gdprApplies: false, - vendorData: {} - }; + it('should apppend to existing user.data', () => { + const global = { + user: { + data: [ + {name: 'preexisting'}, + ] + } + }; + return processFpd({}, {global: deepClone(global)}, {data: Promise.resolve(mockData)}) + .then((data) => { + expect(data.global.user.data).to.eql(global.user.data.concat(mockData)); + }); + }); - gdprDataHdlrStub.returns(consentConfig); - expect(hasGDPRConsent()).to.equal(true); + it('should not modify fpd when there is no data', () => { + return processFpd({}, {global: {}}, {data: Promise.resolve([])}) + .then((data) => { + expect(data.global).to.eql({}); + }); + }); }); - it('should return true when GDPR is applied and purpose consent is true for all purpose[1,2,3,4]', () => { - const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; - const consentConfig = { - consentString: consentString, - gdprApplies: true, - vendorData: { - metadata: consentString, - gdprApplies: true, - purpose: { - consents: { - 1: true, - 2: true, - 3: true, - 4: true + describe('loadTopicsForBidders', () => { + beforeEach(() => { + config.setConfig({ + userSync: { + topics: { + bidders: [{ + bidder: 'mockBidder', + iframeURL: 'https://mock.iframe' + }] } } - } - }; + }) + }); + afterEach(() => { + config.resetConfig(); + }) - gdprDataHdlrStub.returns(consentConfig); - expect(hasGDPRConsent()).to.equal(true); + Object.entries({ + 'support': {}, + 'allow': { + browsingTopics: true, + featurePolicy: { + allowsFeature(feature) { + return feature !== 'browsing-topics'; + } + } + }, + }).forEach(([t, doc]) => { + it(`does not attempt to load frames if browser does not ${t} topics`, () => { + doc.createElement = sinon.stub(); + loadTopicsForBidders(doc); + sinon.assert.notCalled(doc.createElement); + }); + }); }); - it('should return false when GDPR is applied and purpose consent is false for one of the purpose[1,2,3,4]', () => { + describe('getCachedTopics()', () => { + const storage = getCoreStorageManager('topicsFpd'); + const expected = [{ + ext: { + segtax: 600, + segclass: '2206021246' + }, + segment: [{ + 'id': '243' + }, { + 'id': '265' + }], + name: 'ads.pubmatic.com' + }]; const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; const consentConfig = { consentString: consentString, @@ -319,120 +320,97 @@ describe('Topics Module GDPR consent check', () => { 1: true, 2: true, 3: true, - 4: false + 4: true } } } }; - - gdprDataHdlrStub.returns(consentConfig); - expect(hasGDPRConsent()).to.equal(false); - }); -}); - -describe('getCachedTopics()', () => { - const storage = getCoreStorageManager('topicsFpd'); - const expected = [{ - ext: { - segtax: 600, - segclass: '2206021246' - }, - segment: [{ - 'id': '243' - }, { - 'id': '265' - }], - name: 'ads.pubmatic.com' - }]; - const consentString = 'CPi8wgAPi8wgAADABBENCrCsAP_AAH_AAAAAISNB7D=='; - const consentConfig = { - consentString: consentString, - gdprApplies: true, - vendorData: { - metadata: consentString, - gdprApplies: true, - purpose: { - consents: { - 1: true, - 2: true, - 3: true, - 4: true - } + const mockData = [ + { + name: 'domain', + segment: [{id: 123}] + }, + { + name: 'domain', + segment: [{id: 321}], } - } - }; - const mockData = [ - { - name: 'domain', - segment: [{id: 123}] - }, - { - name: 'domain', - segment: [{id: 321}], - } - ]; + ]; - const evt_pm = { - data: '{"segment":{"domain":"ads.pubmatic.com","topics":[{"configVersion":"chrome.1","modelVersion":"2206021246","taxonomyVersion":"1","topic":165,"version":"chrome.1:1:2206021246"}],"bidder":"pubmatic"},"date":1669743901858}', - origin: 'https://ads.pubmatic.com' - }; + const evt = { + data: '{"segment":{"domain":"ads.pubmatic.com","topics":[{"configVersion":"chrome.1","modelVersion":"2206021246","taxonomyVersion":"1","topic":165,"version":"chrome.1:1:2206021246"}],"bidder":"pubmatic"},"date":1669743901858}', + origin: 'https://ads.pubmatic.com' + }; - const evt_rh = { - data: '{"segment":{"domain":"topics.authorizedvault.com","topics":[{"configVersion":"chrome.1","modelVersion":"2206021246","taxonomyVersion":"1","topic":165,"version":"chrome.1:1:2206021246"}],"bidder":"rtbhouse"},"date":1669743901858}', - origin: 'https://topics.authorizedvault.com' - }; + afterEach(() => { + storage.removeDataFromLocalStorage(topicStorageName); + }); - let gdprDataHdlrStub; - beforeEach(() => { - gdprDataHdlrStub = sinon.stub(gdprDataHandler, 'getConsentData'); - }); + describe('when cached data is available and not expired', () => { + let sandbox; + beforeEach(() => { + sandbox = sinon.sandbox.create(); + const storedSegments = JSON.stringify( + [['pubmatic', { + '2206021246': { + 'ext': {'segtax': 600, 'segclass': '2206021246'}, + 'segment': [{'id': '243'}, {'id': '265'}], + 'name': 'ads.pubmatic.com' + }, + 'lastUpdated': new Date().getTime() + }]] + ); + storage.setDataInLocalStorage(topicStorageName, storedSegments); + }); + afterEach(() => { + sandbox.restore(); + }); - afterEach(() => { - storage.removeDataFromLocalStorage(topicStorageName); - gdprDataHdlrStub.restore(); - }); + it('should return segments for bidder if transmitUfpd is allowed', () => { + assert.deepEqual(getCachedTopics(), expected); + }); - it('should return segments for bidder if GDPR consent is true and there is cached segments stored which is not expired', () => { - const storedSegments = JSON.stringify( - [['pubmatic', { - '2206021246': { - 'ext': {'segtax': 600, 'segclass': '2206021246'}, - 'segment': [{'id': '243'}, {'id': '265'}], - 'name': 'ads.pubmatic.com' - }, - 'lastUpdated': new Date().getTime() - }]] - ); - storage.setDataInLocalStorage(topicStorageName, storedSegments); - gdprDataHdlrStub.returns(consentConfig); - assert.deepEqual(getCachedTopics(), expected); - }); + it('should NOT return segments for bidder if enrichUfpd is NOT allowed', () => { + sandbox.stub(activities, 'isActivityAllowed').callsFake((activity, params) => { + return !(activity === ACTIVITY_ENRICH_UFPD && params.component === 'bidder.pubmatic'); + }); + expect(getCachedTopics()).to.eql([]); + }); + }) - it('should return empty segments for bidder if GDPR consent is true and there is cached segments stored which is expired', () => { - let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":10}]]'; - storage.setDataInLocalStorage(topicStorageName, storedSegments); - gdprDataHdlrStub.returns(consentConfig); - assert.deepEqual(getCachedTopics(), []); - }); + it('should return empty segments for bidder if there is cached segments stored which is expired', () => { + let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":10}]]'; + storage.setDataInLocalStorage(topicStorageName, storedSegments); + assert.deepEqual(getCachedTopics(), []); + }); - it('should stored segments if receiveMessage event is triggerred with segment data', () => { - return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) - .then(({global}) => { - receiveMessage(evt_pm); - receiveMessage(evt_rh); + describe('cross-frame messages', () => { + beforeEach(() => { + // init iframe logic so that the receiveMessage origin check passes + loadTopicsForBidders({ + browsingTopics: true, + featurePolicy: { + allowsFeature() { return true } + }, + createElement: sinon.stub().callsFake(() => ({style: {}})), + documentElement: { + appendChild() {} + } + }); + }); + + it('should store segments if receiveMessage event is triggered with segment data', () => { + receiveMessage(evt); let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); - expect(segments.has('pubmatic') || segments.has('rtbhouse')).to.equal(true); + expect(segments.has('pubmatic')).to.equal(true); }); - }); - it('should update stored segments if receiveMessage event is triggerred with segment data', () => { - let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":1669719242027}]]'; - storage.setDataInLocalStorage(topicStorageName, storedSegments); - return processFpd({}, {global: {}}, {data: Promise.resolve(mockData)}) - .then(({global}) => { - receiveMessage(evt_pm); + it('should update stored segments if receiveMessage event is triggerred with segment data', () => { + let storedSegments = '[["pubmatic",{"2206021246":{"ext":{"segtax":600,"segclass":"2206021246"},"segment":[{"id":"243"},{"id":"265"}],"name":"ads.pubmatic.com"},"lastUpdated":1669719242027}]]'; + storage.setDataInLocalStorage(topicStorageName, storedSegments); + receiveMessage(evt); let segments = new Map(safeJSONParse(storage.getDataFromLocalStorage(topicStorageName))); expect(segments.get('pubmatic')[2206021246].segment.length).to.equal(1); }); + }); }); }); From 8e5d24162d5c6e8baed3f5b90cd71216ac60f9ee Mon Sep 17 00:00:00 2001 From: aenrel <123280204+aenrel@users.noreply.github.com> Date: Wed, 26 Jul 2023 05:30:39 -0700 Subject: [PATCH 86/92] Relevad RTD Provider : bugfix around possible duplicated entries (#10280) * Added implementation of the Relevad Real-Time Data Provider * removed bidder from the testing HTML file * Addressed reviewer's request w.r.t. removing bidder-specific handling' * set page url * Addressed code review comments: fixed email address, added description of ORTB attributes we pass to the bidders * Addressed code review comments * Updated contact email address * Added: - a guadrd against duplicates in "bid.params.target" - minor code cleanup --------- Co-authored-by: Relevad <> --- modules/relevadRtdProvider.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/relevadRtdProvider.js b/modules/relevadRtdProvider.js index a7d0305da627..613eaa71a1f9 100644 --- a/modules/relevadRtdProvider.js +++ b/modules/relevadRtdProvider.js @@ -110,6 +110,7 @@ function composeOrtb2Data(rtdData, prefix) { !isEmpty(categories.cat) && deepSetValue(addOrtb2, prefix + '.cat', categories.cat); !isEmpty(categories.pagecat) && deepSetValue(addOrtb2, prefix + '.pagecat', categories.pagecat); !isEmpty(categories.sectioncat) && deepSetValue(addOrtb2, prefix + '.sectioncat', categories.sectioncat); + !isEmpty(categories.sectioncat) && deepSetValue(addOrtb2, prefix + '.ext.data.relevad_rtd', categories.sectioncat); !isEmpty(categories.cattax) && deepSetValue(addOrtb2, prefix + '.cattax', categories.cattax); if (!isEmpty(content) && !isEmpty(content.segs) && content.segtax) { @@ -134,7 +135,8 @@ function setBidderSiteAndContent(bidderOrtbFragment, bidder, rtdData) { try { let addOrtb2 = composeOrtb2Data(rtdData, 'site'); !isEmpty(rtdData.segments) && deepSetValue(addOrtb2, 'user.ext.data.relevad_rtd', rtdData.segments); - !isEmpty(rtdData.categories?.sectioncat) && deepSetValue(addOrtb2, 'site.ext.data.relevad_rtd', rtdData.categories.sectioncat); + !isEmpty(rtdData.segments) && deepSetValue(addOrtb2, 'user.ext.data.segments', rtdData.segments); + !isEmpty(rtdData.categories) && deepSetValue(addOrtb2, 'user.ext.data.contextual_categories', rtdData.categories.pagecat); if (isEmpty(addOrtb2)) { return; } @@ -180,7 +182,7 @@ function getFiltered(data, minscore) { const cats = filterByScore(data.cats, minscore); const pcats = filterByScore(data.pcats, minscore) || cats; const scats = filterByScore(data.scats, minscore) || pcats; - const cattax = data.cattax ? data.cattax : CATTAX_IAB; + const cattax = (data.cattax || data.cattax === undefined) ? data.cattax : CATTAX_IAB; relevadData.categories = {cat: cats, pagecat: pcats, sectioncat: scats, cattax: cattax}; const contsegs = filterByScore(data.contsegs, minscore); @@ -268,15 +270,12 @@ export function addRtdData(reqBids, data, moduleConfig) { } if (wb && !isEmpty(relevadList)) { setBidderSiteAndContent(reqBids.ortb2Fragments?.bidder, bid.bidder, relevadData); + setBidderSiteAndContent(bid, 'ortb2', relevadData); deepSetValue(bid, 'params.keywords.relevad_rtd', relevadList); - deepSetValue(bid, 'params.target', [].concat(bid.params?.target ? [bid.params.target] : []).concat(relevadList.map(entry => { return 'relevad_rtd=' + entry; })).join(';')); + !(bid.params?.target || '').includes('relevad_rtd=') && deepSetValue(bid, 'params.target', [].concat(bid.params?.target ? [bid.params.target] : []).concat(relevadList.map(entry => { return 'relevad_rtd=' + entry; })).join(';')); let firstPartyData = {}; firstPartyData[bid.bidder] = { firstPartyData: { relevad_rtd: relevadList } }; config.setConfig(firstPartyData); - !isEmpty(relevadData.segments) && deepSetValue(bid, 'ortb2.user.ext.data.segments', relevadData.segments); - !isEmpty(relevadData.categories) && deepSetValue(bid, 'ortb2.user.ext.data.contextual_categories', relevadData.categories.pagecat); - !isEmpty(relevadData.categories) && deepSetValue(bid, 'ortb2.site.ext.data.relevad_rtd', relevadData.categories.pagecat); - !isEmpty(relevadData.segments) && deepSetValue(bid, 'ortb2.user.ext.data.relevad_rtd', relevadData.segments); } } } catch (e) { From 0a58ba5f5aec3adb935953028eafbb3c9c610f72 Mon Sep 17 00:00:00 2001 From: southern-growthcode <79725079+southern-growthcode@users.noreply.github.com> Date: Wed, 26 Jul 2023 09:09:17 -0400 Subject: [PATCH 87/92] GrowthCode nobid bug fix (#10278) --- modules/growthCodeAnalyticsAdapter.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/growthCodeAnalyticsAdapter.js b/modules/growthCodeAnalyticsAdapter.js index a2ab4ddbfacd..655f8af53a3a 100644 --- a/modules/growthCodeAnalyticsAdapter.js +++ b/modules/growthCodeAnalyticsAdapter.js @@ -30,8 +30,8 @@ let bidRequestTimeout = 0; let analyticsType = 'endpoint'; let growthCodeAnalyticsAdapter = Object.assign(adapter({url: url, analyticsType}), { - track({eventType, eventData}) { - eventData = eventData ? JSON.parse(JSON.stringify(eventData)) : {}; + track({eventType, args}) { + let eventData = args ? JSON.parse(JSON.stringify(args)) : {}; let data = {}; if (!trackEvents.includes(eventType)) return; switch (eventType) { @@ -98,6 +98,11 @@ let growthCodeAnalyticsAdapter = Object.assign(adapter({url: url, analyticsType} break; } + case CONSTANTS.EVENTS.NO_BID: { + data = eventData + break; + } + default: return; } From 9345b0a1e5f0ec2f613521aaf1e515adea9a2dd0 Mon Sep 17 00:00:00 2001 From: prebidtappx <77485538+prebidtappx@users.noreply.github.com> Date: Wed, 26 Jul 2023 16:30:49 +0200 Subject: [PATCH 88/92] Tappx Bid Adapter: accept ortb2 country (#9907) * Fix: creating host correctly when http or https are added from the beginning * Fix :: Changed double quotes for single quotes * Fix :: Getting the full page URL * Fix :: Changed order params * Fix :: Replaced quotes from double to simple * Fix :: Adapting format to lint * Remove TODO comment * Added more controls * camelcase fix * Changed test * Remove "inIframe" util * Fix: solved error getOs and created getVendor functions * Fix: Solved CircelCI format problems * 10207_include setConfig function and intruccions in readme file * Update tappxBidAdapter.md * Update tappxBidAdapter.js * Fix: repriorising to get the OS correctly * Fix: Correcting EsLint requirements for merge * Update tappxBidAdapter.js please add unit tests --------- Co-authored-by: Jordi Arnau Co-authored-by: ruben_tappx Co-authored-by: jgarciaorad Co-authored-by: Patrick McCann --- modules/tappxBidAdapter.js | 11 ++++++++++- modules/tappxBidAdapter.md | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/modules/tappxBidAdapter.js b/modules/tappxBidAdapter.js index 13a16224a795..f45f16a07289 100644 --- a/modules/tappxBidAdapter.js +++ b/modules/tappxBidAdapter.js @@ -252,6 +252,7 @@ function buildOneRequest(validBidRequests, bidderRequest) { const BIDEXTRA = deepAccess(validBidRequests, 'params.ext'); const bannerMediaType = deepAccess(validBidRequests, 'mediaTypes.banner'); const videoMediaType = deepAccess(validBidRequests, 'mediaTypes.video'); + const ORTB2 = deepAccess(validBidRequests, 'ortb2'); // let requests = []; let payload = {}; @@ -410,6 +411,14 @@ function buildOneRequest(validBidRequests, bidderRequest) { let geo = {}; geo.country = deepAccess(validBidRequests, 'params.geo.country'); // < Device object + let configGeo = {}; + configGeo.country = ORTB2?.device?.geo; + + if (typeof configGeo.country !== 'undefined') { + device.geo = configGeo; + } else if (typeof geo.country !== 'undefined') { + device.geo = geo; + }; // > GDPR let user = {}; @@ -491,7 +500,7 @@ function getLanguage() { function getOs() { let ua = navigator.userAgent; - if (ua.indexOf('Windows') != -1) { return 'Windows'; } else if (ua.indexOf('Mac OS X') != -1) { return 'macOS'; } else if (ua.match(/Android/)) { return 'Android'; } else if (ua.match(/(iPhone|iPod|iPad)/)) { return 'iOS'; } else if (ua.indexOf('Linux') != -1) { return 'Linux'; } else { return 'Unknown'; } + if (ua.match(/Android/)) { return 'Android'; } else if (ua.match(/(iPhone|iPod|iPad)/)) { return 'iOS'; } else if (ua.indexOf('Mac OS X') != -1) { return 'macOS'; } else if (ua.indexOf('Windows') != -1) { return 'Windows'; } else if (ua.indexOf('Linux') != -1) { return 'Linux'; } else { return 'Unknown'; } } function getVendor() { diff --git a/modules/tappxBidAdapter.md b/modules/tappxBidAdapter.md index 677718c261ce..55f18531f287 100644 --- a/modules/tappxBidAdapter.md +++ b/modules/tappxBidAdapter.md @@ -92,3 +92,20 @@ Ads sizes available: [300,250], [320,50], [320,480], [480,320], [728,90], [768,1 } ]; ``` +### Configuration + +Use `setConfig` to configure this submodule ortb2.device.geo, this will allow geolocation +`Geo` object to bring First Party Information. + +```javascript +var TIMEOUT = 1000; +pbjs.setConfig({ + ortb2:{ + device:{ + geo:{ + country:'US' + } + } + } +}); +``` From 9ad889bbe8e0581cb94dcdbc2609adeea5c1e511 Mon Sep 17 00:00:00 2001 From: danielpfei <122355648+danielpfei@users.noreply.github.com> Date: Thu, 27 Jul 2023 14:04:26 +0200 Subject: [PATCH 89/92] AdsInteractive Bid Adapter : update sync url (#10281) * AdsInteractive adapter first commit * minor changes for the best bidding * add new test params * add meta obj, simplify condition * simplify condition * beutify conditions * add usersync, gdpr, fix interpretResp * change AdsInteractive sync ulr --------- Co-authored-by: pfeifer.daniel83 --- modules/adsinteractiveBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/adsinteractiveBidAdapter.js b/modules/adsinteractiveBidAdapter.js index 304b8bcade0a..3dbdb01ac5d0 100644 --- a/modules/adsinteractiveBidAdapter.js +++ b/modules/adsinteractiveBidAdapter.js @@ -6,8 +6,8 @@ import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER } from '../src/mediaTypes.js'; const ADSINTERACTIVE_CODE = 'adsinteractive'; -const USER_SYNC_URL_IMAGE = 'https://pb.adsinteractive.com/img'; -const USER_SYNC_URL_IFRAME = 'https://pb.adsinteractive.com/sync'; +const USER_SYNC_URL_IMAGE = 'https://sync.adsinteractive.com/img'; +const USER_SYNC_URL_IFRAME = 'https://sync.adsinteractive.com/sync'; export const spec = { code: ADSINTERACTIVE_CODE, From 9f54c4824ec358cb7ad68b46e5a7c0ea3e31bf98 Mon Sep 17 00:00:00 2001 From: pubcircle <138609443+pubcircle@users.noreply.github.com> Date: Thu, 27 Jul 2023 15:48:05 +0300 Subject: [PATCH 90/92] pubcircleNewAdapter (#10192) Co-authored-by: root --- modules/pubCircleBidAdapter.js | 231 ++++++++++ modules/pubCircleBidAdapter.md | 79 ++++ test/spec/modules/pubCircleBidAdapter_spec.js | 399 ++++++++++++++++++ 3 files changed, 709 insertions(+) create mode 100644 modules/pubCircleBidAdapter.js create mode 100644 modules/pubCircleBidAdapter.md create mode 100644 test/spec/modules/pubCircleBidAdapter_spec.js diff --git a/modules/pubCircleBidAdapter.js b/modules/pubCircleBidAdapter.js new file mode 100644 index 000000000000..54224fd0403c --- /dev/null +++ b/modules/pubCircleBidAdapter.js @@ -0,0 +1,231 @@ +import { isFn, deepAccess, logMessage, logError } from '../src/utils.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; + +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; + +const BIDDER_CODE = 'pubcircle'; +const AD_URL = 'https://ml.pubcircle.ai/pbjs'; +const SYNC_URL = 'https://cs.pubcircle.ai'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || !bid.ttl || !bid.currency) { + return false; + } + + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl || bid.vastXml); + case NATIVE: + return Boolean(bid.native && bid.native.impressionTrackers && bid.native.impressionTrackers.length); + default: + return false; + } +} + +function getPlacementReqData(bid) { + const { params, bidId, mediaTypes } = bid; + const schain = bid.schain || {}; + const { placementId } = params; + const bidfloor = getBidFloor(bid); + + const placement = { + bidId, + schain, + bidfloor + }; + + placement.placementId = placementId; + placement.type = 'publisher'; + + if (bid.userId) { + getUserId(placement.eids, bid.userId.uid2 && bid.userId.uid2.id, 'uidapi.com'); + getUserId(placement.eids, bid.userId.lotamePanoramaId, 'lotame.com'); + getUserId(placement.eids, bid.userId.idx, 'idx.lat'); + getUserId(placement.eids, bid.userId.idl_env, 'liveramp.com'); + } + + if (mediaTypes && mediaTypes[BANNER]) { + placement.adFormat = BANNER; + placement.sizes = mediaTypes[BANNER].sizes; + } else if (mediaTypes && mediaTypes[VIDEO]) { + placement.adFormat = VIDEO; + placement.playerSize = mediaTypes[VIDEO].playerSize; + placement.minduration = mediaTypes[VIDEO].minduration; + placement.maxduration = mediaTypes[VIDEO].maxduration; + placement.mimes = mediaTypes[VIDEO].mimes; + placement.protocols = mediaTypes[VIDEO].protocols; + placement.startdelay = mediaTypes[VIDEO].startdelay; + placement.placement = mediaTypes[VIDEO].placement; + placement.skip = mediaTypes[VIDEO].skip; + placement.skipafter = mediaTypes[VIDEO].skipafter; + placement.minbitrate = mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = mediaTypes[VIDEO].maxbitrate; + placement.delivery = mediaTypes[VIDEO].delivery; + placement.playbackmethod = mediaTypes[VIDEO].playbackmethod; + placement.api = mediaTypes[VIDEO].api; + placement.linearity = mediaTypes[VIDEO].linearity; + } else if (mediaTypes && mediaTypes[NATIVE]) { + placement.native = mediaTypes[NATIVE]; + placement.adFormat = NATIVE; + } + + return placement; +} + +function getBidFloor(bid) { + if (!isFn(bid.getFloor)) { + return deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (err) { + logError(err); + return 0; + } +} + +function getUserId(eids, id, source, uidExt) { + if (id) { + var uid = { id }; + if (uidExt) { + uid.ext = uidExt; + } + eids.push({ + source, + uids: [ uid ] + }); + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid = {}) => { + const { params, bidId, mediaTypes } = bid; + let valid = Boolean(bidId && params && params.placementId); + + if (mediaTypes && mediaTypes[BANNER]) { + valid = valid && Boolean(mediaTypes[BANNER] && mediaTypes[BANNER].sizes); + } else if (mediaTypes && mediaTypes[VIDEO]) { + valid = valid && Boolean(mediaTypes[VIDEO] && mediaTypes[VIDEO].playerSize); + } else if (mediaTypes && mediaTypes[NATIVE]) { + valid = valid && Boolean(mediaTypes[NATIVE]); + } else { + valid = false; + } + return valid; + }, + + buildRequests: (validBidRequests = [], bidderRequest = {}) => { + // convert Native ORTB definition to old-style prebid native definition + validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); + + let deviceWidth = 0; + let deviceHeight = 0; + + let winLocation; + try { + const winTop = window.top; + deviceWidth = winTop.screen.width; + deviceHeight = winTop.screen.height; + winLocation = winTop.location; + } catch (e) { + logMessage(e); + winLocation = window.location; + } + + const refferUrl = bidderRequest.refererInfo && bidderRequest.refererInfo.page; + let refferLocation; + try { + refferLocation = refferUrl && new URL(refferUrl); + } catch (e) { + logMessage(e); + } + // TODO: does the fallback make sense here? + let location = refferLocation || winLocation; + const language = (navigator && navigator.language) ? navigator.language.split('-')[0] : ''; + const host = location.host; + const page = location.pathname; + const secure = location.protocol === 'https:' ? 1 : 0; + const placements = []; + const request = { + deviceWidth, + deviceHeight, + language, + secure, + host, + page, + placements, + coppa: config.getConfig('coppa') === true ? 1 : 0, + ccpa: bidderRequest.uspConsent || undefined, + gdpr: bidderRequest.gdprConsent || undefined, + tmax: bidderRequest.timeout + }; + + const len = validBidRequests.length; + for (let i = 0; i < len; i++) { + const bid = validBidRequests[i]; + placements.push(getPlacementReqData(bid)); + } + + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + + response.push(resItem); + } + } + return response; + }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncType = syncOptions.iframeEnabled ? 'iframe' : 'image'; + let syncUrl = SYNC_URL + `/${syncType}?pbjs=1`; + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + + const coppa = config.getConfig('coppa') ? 1 : 0; + syncUrl += `&coppa=${coppa}`; + + return [{ + type: syncType, + url: syncUrl + }]; + }, + + onBidViewable: function (bid) { + // to do : we need to implement js tag to fire pixel with viewability counter + } +}; + +registerBidder(spec); diff --git a/modules/pubCircleBidAdapter.md b/modules/pubCircleBidAdapter.md new file mode 100644 index 000000000000..4fc114bf20cd --- /dev/null +++ b/modules/pubCircleBidAdapter.md @@ -0,0 +1,79 @@ +# Overview + +``` +Module Name: PubCirlce Bidder Adapter +Module Type: PubCirlce Bidder Adapter +Maintainer: system@smartyads.com +``` + +# Description + +Connects to PubCirlce exchange for bids. +PubCirlce bid adapter supports Banner, Video (instream and outstream) and Native. + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'addunit1', + mediaTypes: { + banner: { + sizes: [ [300, 250], [320, 50] ], + } + }, + bids: [ + { + bidder: 'pubcircle', + params: { + placementId: 'testBanner', + } + } + ] + }, + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [ [640, 480] ], + context: 'instream', + minduration: 5, + maxduration: 60, + } + }, + bids: [ + { + bidder: 'pubcircle', + params: { + placementId: 'testVideo', + } + } + ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'pubcircle', + params: { + placementId: 'testNative', + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/pubCircleBidAdapter_spec.js b/test/spec/modules/pubCircleBidAdapter_spec.js new file mode 100644 index 000000000000..8aaa023ee1c2 --- /dev/null +++ b/test/spec/modules/pubCircleBidAdapter_spec.js @@ -0,0 +1,399 @@ +import { expect } from 'chai'; +import { spec } from '../../../modules/pubCircleBidAdapter'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; +import { getUniqueIdentifierStr } from '../../../src/utils.js'; + +const bidder = 'pubcircle' + +describe('PubCircleBidAdapter', function () { + const bids = [ + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + placementId: 'testBanner', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [VIDEO]: { + playerSize: [[300, 300]], + minduration: 5, + maxduration: 60 + } + }, + params: { + placementId: 'testVideo', + } + }, + { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [NATIVE]: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + } + }, + params: { + placementId: 'testNative', + } + } + ]; + + const invalidBid = { + bidId: getUniqueIdentifierStr(), + bidder: bidder, + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, + params: { + + } + } + + const bidderRequest = { + uspConsent: '1---', + gdprConsent: 'COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA.IFoEUQQgAIQwgIwQABAEAAAAOIAACAIAAAAQAIAgEAACEAAAAAgAQBAAAAAAAGBAAgAAAAAAAFAAECAAAgAAQARAEQAAAAAJAAIAAgAAAYQEAAAQmAgBC3ZAYzUw', + refererInfo: { + referer: 'https://test.com' + }, + timeout: 500 + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bids[0])).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + expect(spec.isBidRequestValid(invalidBid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests(bids, bidderRequest); + + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://ml.pubcircle.ai/pbjs'); + }); + + it('Returns general data valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', + 'deviceHeight', + 'language', + 'secure', + 'host', + 'page', + 'placements', + 'coppa', + 'ccpa', + 'gdpr', + 'tmax' + ); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.coppa).to.be.a('number'); + expect(data.gdpr).to.be.a('string'); + expect(data.ccpa).to.be.a('string'); + expect(data.tmax).to.be.a('number'); + expect(data.placements).to.have.lengthOf(3); + }); + + it('Returns valid placements', function () { + const { placements } = serverRequest.data; + for (let i = 0, len = placements.length; i < len; i++) { + const placement = placements[i]; + expect(placement.placementId).to.be.oneOf(['testBanner', 'testVideo', 'testNative']); + expect(placement.adFormat).to.be.oneOf([BANNER, VIDEO, NATIVE]); + expect(placement.bidId).to.be.a('string'); + expect(placement.schain).to.be.an('object'); + expect(placement.bidfloor).to.exist.and.to.equal(0); + expect(placement.type).to.exist.and.to.equal('publisher'); + + if (placement.adFormat === BANNER) { + expect(placement.sizes).to.be.an('array'); + } + switch (placement.adFormat) { + case BANNER: + expect(placement.sizes).to.be.an('array'); + break; + case VIDEO: + expect(placement.playerSize).to.be.an('array'); + expect(placement.minduration).to.be.an('number'); + expect(placement.maxduration).to.be.an('number'); + break; + case NATIVE: + expect(placement.native).to.be.an('object'); + break; + } + } + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + delete bidderRequest.uspConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = '1---'; + delete bidderRequest.gdprConsent; + serverRequest = spec.buildRequests(bids, bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([], bidderRequest); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal(banner.body[0].requestId); + expect(dataItem.cpm).to.equal(banner.body[0].cpm); + expect(dataItem.width).to.equal(banner.body[0].width); + expect(dataItem.height).to.equal(banner.body[0].height); + expect(dataItem.ad).to.equal(banner.body[0].ad); + expect(dataItem.ttl).to.equal(banner.body[0].ttl); + expect(dataItem.creativeId).to.equal(banner.body[0].creativeId); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal(banner.body[0].currency); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + meta: { + advertiserDomains: ['google.com'], + advertiserId: 1234 + } + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); + + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.pubcircle.ai/image?pbjs=1&gdpr=1&gdpr_consent=ALL&coppa=0') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1---' + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('image') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.pubcircle.ai/image?pbjs=1&ccpa_consent=1---&coppa=0') + }); + }); +}); From 0cf06fb82b583203997d30a33a4254e6612bf892 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 27 Jul 2023 14:01:22 +0000 Subject: [PATCH 91/92] Prebid 8.6.0 release --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f8dc7ad289f4..d832f8bb4fc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.6.0-pre", + "version": "8.6.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 8bbbe9a6d789..2aac856a77f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.6.0-pre", + "version": "8.6.0", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": { From f688821e88bb57918d4e370433d184250c3d95e6 Mon Sep 17 00:00:00 2001 From: "Prebid.js automated release" Date: Thu, 27 Jul 2023 14:01:22 +0000 Subject: [PATCH 92/92] Increment version to 8.7.0-pre --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d832f8bb4fc2..282e5214691e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.6.0", + "version": "8.7.0-pre", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 2aac856a77f0..2d0d1997662b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "8.6.0", + "version": "8.7.0-pre", "description": "Header Bidding Management Library", "main": "src/prebid.js", "scripts": {