From 91aa96ebe257c40cc4a221ffa185ba0eec30df3f Mon Sep 17 00:00:00 2001 From: optidigital-prebid <124287395+optidigital-prebid@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:25:17 +0200 Subject: [PATCH 1/4] add gpp suport (#11381) Co-authored-by: Dawid W --- modules/optidigitalBidAdapter.js | 24 ++++++++++-- .../modules/optidigitalBidAdapter_spec.js | 38 +++++++++++++++++-- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/modules/optidigitalBidAdapter.js b/modules/optidigitalBidAdapter.js index 27b858c84fe..396131fd8aa 100755 --- a/modules/optidigitalBidAdapter.js +++ b/modules/optidigitalBidAdapter.js @@ -92,12 +92,24 @@ export const spec = { } } + if (bidderRequest?.gppConsent?.gppString) { + payload.gpp = { + consent: bidderRequest.gppConsent.gppString, + sid: bidderRequest.gppConsent.applicableSections + } + } else if (bidderRequest?.ortb2?.regs?.gpp) { + payload.gpp = { + consent: bidderRequest.ortb2.regs.gpp, + sid: bidderRequest.ortb2.regs.gpp_sid + } + } + if (window.location.href.indexOf('optidigitalTestMode=true') !== -1) { payload.testMode = true; } if (bidderRequest && bidderRequest.uspConsent) { - payload.uspConsent = bidderRequest.uspConsent; + payload.us_privacy = bidderRequest.uspConsent; } if (_getEids(validBidRequests[0])) { @@ -153,7 +165,7 @@ export const spec = { * @param {ServerResponse[]} serverResponses List of server's responses. * @return {UserSync[]} The user syncs which should be dropped. */ - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { + getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) { let syncurl = ''; if (!isSynced) { // Attaching GDPR Consent Params in UserSync url @@ -161,8 +173,12 @@ export const spec = { syncurl += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); syncurl += '&gdpr_consent=' + encodeURIComponent(gdprConsent.consentString || ''); } - if (uspConsent && uspConsent.consentString) { - syncurl += `&ccpa_consent=${uspConsent.consentString}`; + if (uspConsent) { + syncurl += '&us_privacy=' + encodeURIComponent(uspConsent); + } + if (gppConsent?.gppString && gppConsent?.applicableSections?.length) { + syncurl += '&gpp=' + encodeURIComponent(gppConsent.gppString); + syncurl += '&gpp_sid=' + encodeURIComponent(gppConsent?.applicableSections?.join(',')); } if (syncOptions.iframeEnabled) { diff --git a/test/spec/modules/optidigitalBidAdapter_spec.js b/test/spec/modules/optidigitalBidAdapter_spec.js index 30e72452c39..273b29001d1 100755 --- a/test/spec/modules/optidigitalBidAdapter_spec.js +++ b/test/spec/modules/optidigitalBidAdapter_spec.js @@ -423,7 +423,32 @@ describe('optidigitalAdapterTests', function () { bidderRequest.uspConsent = '1YYY'; const request = spec.buildRequests(validBidRequests, bidderRequest); const payload = JSON.parse(request.data); - expect(payload.uspConsent).to.exist; + expect(payload.us_privacy).to.exist; + }); + + it('should send gppConsent to given endpoint where there is gppConsent', function() { + let consentString = 'BOJ/P2HOJ/P2HABABMAAAAAZ+A=='; + bidderRequest.gppConsent = { + 'gppString': consentString, + 'applicableSections': [7] + }; + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.gpp).to.exist; + }); + + it('should send gppConsent to given endpoint when there is gpp in ortb2', function() { + let consentString = 'BOJ/P2HOJ/P2HABABMAAAAAZ+A=='; + bidderRequest.gppConsent = undefined; + bidderRequest.ortb2 = { + regs: { + gpp: consentString, + gpp_sid: [7] + } + } + const request = spec.buildRequests(validBidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.gpp).to.exist; }); it('should use appropriate mediaTypes banner sizes', function() { @@ -546,9 +571,14 @@ describe('optidigitalAdapterTests', function () { type: 'iframe', url: `${syncurlIframe}&gdpr=1&gdpr_consent=` }]); }); - it('should return appropriate URL with GDPR equals to 1, GDPR consent and CCPA consent', function() { - expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, {consentString: 'fooUsp'})).to.deep.equal([{ - type: 'iframe', url: `${syncurlIframe}&gdpr=1&gdpr_consent=foo&ccpa_consent=fooUsp` + it('should return appropriate URL with GDPR equals to 1, GDPR consent and US Privacy consent', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, 'fooUsp')).to.deep.equal([{ + type: 'iframe', url: `${syncurlIframe}&gdpr=1&gdpr_consent=foo&us_privacy=fooUsp` + }]); + }); + it('should return appropriate URL with GDPR equals to 1, GDPR consent, US Privacy consent and GPP consent', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, {gdprApplies: true, consentString: 'foo'}, 'fooUsp', {gppString: 'fooGpp', applicableSections: [7]})).to.deep.equal([{ + type: 'iframe', url: `${syncurlIframe}&gdpr=1&gdpr_consent=foo&us_privacy=fooUsp&gpp=fooGpp&gpp_sid=7` }]); }); }); From d11851e5a3469859f3c98209f0d5dcfd2cd79012 Mon Sep 17 00:00:00 2001 From: relaido <63339139+relaido@users.noreply.github.com> Date: Tue, 23 Apr 2024 22:56:27 +0900 Subject: [PATCH 2/4] Relaido Bid Adapter: add pagekvt to request and add ogUrl to params (#11318) * add relaido adapter * remove event listener * fixed UserSyncs and e.data * fix conflicts * Change the initial value of userIdAsEids to an empty array * add pagekvt to request * add ogUrl to params --------- Co-authored-by: ishigami_shingo Co-authored-by: cmertv-sishigami Co-authored-by: t_bun Co-authored-by: n.maeura --- modules/relaidoBidAdapter.js | 79 ++++++++++++++++++--- test/spec/modules/relaidoBidAdapter_spec.js | 62 ++++++++++++++++ 2 files changed, 132 insertions(+), 9 deletions(-) diff --git a/modules/relaidoBidAdapter.js b/modules/relaidoBidAdapter.js index 751e8fa442c..a180d04cc71 100644 --- a/modules/relaidoBidAdapter.js +++ b/modules/relaidoBidAdapter.js @@ -7,17 +7,19 @@ import { isArray, isNumber, parseSizesInput, - getBidIdParameter + getBidIdParameter, + isGptPubadsDefined } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; import { Renderer } from '../src/Renderer.js'; import { getStorageManager } from '../src/storageManager.js'; import sha1 from 'crypto-js/sha1'; +import { isSlotMatchingAdUnitCode } from '../libraries/gptUtils/gptUtils.js'; const BIDDER_CODE = 'relaido'; const BIDDER_DOMAIN = 'api.relaido.jp'; -const ADAPTER_VERSION = '1.1.0'; +const ADAPTER_VERSION = '1.2.0'; const DEFAULT_TTL = 300; const UUID_KEY = 'relaido_uuid'; @@ -47,6 +49,7 @@ function buildRequests(validBidRequests, bidderRequest) { let bidDomain = null; let bidder = null; let count = null; + let isOgUrlOption = false; for (let i = 0; i < validBidRequests.length; i++) { const bidRequest = validBidRequests[i]; @@ -92,6 +95,10 @@ function buildRequests(validBidRequests, bidderRequest) { count = bidRequest.bidRequestsCount; } + if (getBidIdParameter('ogUrl', bidRequest.params)) { + isOgUrlOption = true; + } + bids.push({ bid_id: bidRequest.bidId, placement_id: getBidIdParameter('placementId', bidRequest.params), @@ -105,10 +112,13 @@ function buildRequests(validBidRequests, bidderRequest) { height: height, banner_sizes: getBannerSizes(bidRequest), media_type: mediaType, - userIdAsEids: bidRequest.userIdAsEids || {}, + userIdAsEids: bidRequest.userIdAsEids || [], + pagekvt: getTargeting(bidRequest), }); } + const canonicalUrl = getCanonicalUrl(bidderRequest.refererInfo?.canonicalUrl, isOgUrlOption); + const data = JSON.stringify({ version: ADAPTER_VERSION, bids: bids, @@ -118,8 +128,8 @@ function buildRequests(validBidRequests, bidderRequest) { uuid: getUuid(), pv: '$prebid.version$', imuid: imuid, - canonical_url: bidderRequest.refererInfo?.canonicalUrl || null, - canonical_url_hash: getCanonicalUrlHash(bidderRequest.refererInfo), + canonical_url: canonicalUrl, + canonical_url_hash: getCanonicalUrlHash(canonicalUrl), ref: bidderRequest.refererInfo.page }); @@ -294,12 +304,25 @@ function getUuid() { return newId; } -function getCanonicalUrlHash(refererInfo) { - const canonicalUrl = refererInfo.canonicalUrl || null; +function getOgUrl() { + try { + const ogURLElement = window.top.document.querySelector('meta[property="og:url"]'); + return ogURLElement ? ogURLElement.content : null; + } catch (e) { + const ogURLElement = document.querySelector('meta[property="og:url"]'); + return ogURLElement ? ogURLElement.content : null; + } +} + +function getCanonicalUrl(canonicalUrl, isOgUrlOption) { if (!canonicalUrl) { - return null; + return (isOgUrlOption) ? getOgUrl() : null; } - return sha1(canonicalUrl).toString(); + return canonicalUrl; +} + +function getCanonicalUrlHash(canonicalUrl) { + return (canonicalUrl) ? sha1(canonicalUrl).toString() : null; } function hasBannerMediaType(bid) { @@ -349,6 +372,44 @@ function getBannerSizes(bidRequest) { return parseSizesInput(sizes).join(','); } +function getTargeting(bidRequest) { + const targetings = {}; + const pubads = getPubads(); + if (pubads) { + const keys = pubads.getTargetingKeys(); + for (const key of keys) { + const values = pubads.getTargeting(key); + targetings[key] = values; + } + } + const adUnitSlot = getAdUnit(bidRequest.adUnitCode); + if (adUnitSlot) { + const keys = adUnitSlot.getTargetingKeys(); + for (const key of keys) { + const values = adUnitSlot.getTargeting(key); + targetings[key] = values; + } + } + return targetings; +} + +function getPubads() { + return (isGptPubadsDefined()) ? window.googletag.pubads() : null; +} + +function getAdUnit(adUnitCode) { + if (isGptPubadsDefined()) { + const adSlots = window.googletag.pubads().getSlots(); + const isMatchingAdSlot = isSlotMatchingAdUnitCode(adUnitCode); + for (let i = 0; i < adSlots.length; i++) { + if (isMatchingAdSlot(adSlots[i])) { + return adSlots[i]; + } + } + } + return null; +} + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], diff --git a/test/spec/modules/relaidoBidAdapter_spec.js b/test/spec/modules/relaidoBidAdapter_spec.js index f0d019913e8..4a07c84a494 100644 --- a/test/spec/modules/relaidoBidAdapter_spec.js +++ b/test/spec/modules/relaidoBidAdapter_spec.js @@ -3,6 +3,7 @@ import {spec} from 'modules/relaidoBidAdapter.js'; import * as utils from 'src/utils.js'; import {VIDEO} from 'src/mediaTypes.js'; import {getCoreStorageManager} from '../../../src/storageManager.js'; +import * as mockGpt from '../integration/faker/googletag.js'; const UUID_KEY = 'relaido_uuid'; const relaido_uuid = 'hogehoge'; @@ -15,14 +16,18 @@ describe('RelaidoAdapter', function () { let serverRequest; let generateUUIDStub; let triggerPixelStub; + let sandbox; + before(() => { const storage = getCoreStorageManager(); storage.setCookie(UUID_KEY, relaido_uuid); }); beforeEach(function () { + mockGpt.disable(); generateUUIDStub = sinon.stub(utils, 'generateUUID').returns(relaido_uuid); triggerPixelStub = sinon.stub(utils, 'triggerPixel'); + sandbox = sinon.sandbox.create(); bidRequest = { bidder: 'relaido', params: { @@ -115,6 +120,7 @@ describe('RelaidoAdapter', function () { afterEach(() => { generateUUIDStub.restore(); triggerPixelStub.restore(); + sandbox.restore(); }); describe('spec.isBidRequestValid', function () { @@ -251,8 +257,10 @@ describe('RelaidoAdapter', function () { expect(request.bid_id).to.equal(bidRequest.bidId); expect(request.transaction_id).to.equal(bidRequest.ortb2Imp.ext.tid); expect(request.media_type).to.equal('video'); + expect(request.pagekvt).to.deep.equal({}); expect(data.uuid).to.equal(relaido_uuid); expect(data.pv).to.equal('$prebid.version$'); + expect(request.userIdAsEids).to.be.an('array'); }); it('should build bid requests by banner', function () { @@ -335,6 +343,60 @@ describe('RelaidoAdapter', function () { expect(data.bids[0].userIdAsEids).to.have.lengthOf(1); expect(data.bids[0].userIdAsEids[0].source).to.equal('hogehoge.com'); }); + + it('should get pagekvt', function () { + mockGpt.enable(); + window.googletag.pubads().clearTargeting(); + window.googletag.pubads().setTargeting('testkey', ['testvalue']); + bidRequest.adUnitCode = 'test-adunit-code-1'; + window.googletag.pubads().setSlots([mockGpt.makeSlot({ code: bidRequest.adUnitCode })]); + const bidRequests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(bidRequests.data); + expect(data.bids).to.have.lengthOf(1); + const request = data.bids[0]; + expect(request.pagekvt).to.deep.equal({testkey: ['testvalue']}); + }); + + it('should get canonicalUrl (ogUrl:true)', function () { + bidRequest.params.ogUrl = true; + bidderRequest.refererInfo.canonicalUrl = null; + let documentStub = sandbox.stub(window.top.document, 'querySelector'); + documentStub.withArgs('meta[property="og:url"]').returns({ + content: 'http://localhost:9999/fb-test' + }); + const bidRequests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(bidRequests.data); + expect(data.bids).to.have.lengthOf(1); + expect(data.canonical_url).to.equal('http://localhost:9999/fb-test'); + expect(data.canonical_url_hash).to.equal('cd106829f866d60ee4ed43c6e2a5d0a5212ffc97'); + }); + + it('should not get canonicalUrl (ogUrl:false)', function () { + bidRequest.params.ogUrl = false; + bidderRequest.refererInfo.canonicalUrl = null; + let documentStub = sandbox.stub(window.top.document, 'querySelector'); + documentStub.withArgs('meta[property="og:url"]').returns({ + content: 'http://localhost:9999/fb-test' + }); + const bidRequests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(bidRequests.data); + expect(data.bids).to.have.lengthOf(1); + expect(data.canonical_url).to.be.null; + expect(data.canonical_url_hash).to.be.null; + }); + + it('should not get canonicalUrl (ogUrl:nothing)', function () { + bidderRequest.refererInfo.canonicalUrl = null; + let documentStub = sandbox.stub(window.top.document, 'querySelector'); + documentStub.withArgs('meta[property="og:url"]').returns({ + content: 'http://localhost:9999/fb-test' + }); + const bidRequests = spec.buildRequests([bidRequest], bidderRequest); + const data = JSON.parse(bidRequests.data); + expect(data.bids).to.have.lengthOf(1); + expect(data.canonical_url).to.be.null; + expect(data.canonical_url_hash).to.be.null; + }); }); describe('spec.interpretResponse', function () { From ce125a7bce4c00b8e3d2857eb2426abcbc262760 Mon Sep 17 00:00:00 2001 From: Antonios Sarhanis Date: Wed, 24 Apr 2024 00:09:22 +1000 Subject: [PATCH 3/4] Adnuntius Bid Adapter: send script-override parameter to ad server (#11363) --- modules/adnuntiusBidAdapter.js | 5 +++++ test/spec/modules/adnuntiusBidAdapter_spec.js | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/adnuntiusBidAdapter.js b/modules/adnuntiusBidAdapter.js index 78f9f4c19aa..9c8aa5dd5c4 100644 --- a/modules/adnuntiusBidAdapter.js +++ b/modules/adnuntiusBidAdapter.js @@ -209,6 +209,11 @@ export const spec = { queryParamsAndValues.push('gdpr=' + flag); } + const searchParams = new URLSearchParams(window.location.search); + if (searchParams.has('script-override')) { + queryParamsAndValues.push('so=' + searchParams.get('script-override')); + } + storageTool.refreshStorage(bidderRequest); const urlRelatedMetaData = storageTool.getUrlRelatedData(); diff --git a/test/spec/modules/adnuntiusBidAdapter_spec.js b/test/spec/modules/adnuntiusBidAdapter_spec.js index 2fa1a52e863..cad5e9ea1cf 100644 --- a/test/spec/modules/adnuntiusBidAdapter_spec.js +++ b/test/spec/modules/adnuntiusBidAdapter_spec.js @@ -15,6 +15,9 @@ describe('adnuntiusBidAdapter', function() { const meta = [{key: 'valueless'}, {value: 'keyless'}, {key: 'voidAuIds'}, {key: 'voidAuIds', value: [{auId: '11118b6bc', exp: misc.getUnixTimestamp()}, {exp: misc.getUnixTimestamp(1)}]}, {key: 'valid-withnetwork', value: 'also-valid-network', network: 'the-network', exp: misc.getUnixTimestamp(1)}, {key: 'valid', value: 'also-valid', exp: misc.getUnixTimestamp(1)}, {key: 'expired', value: 'fwefew', exp: misc.getUnixTimestamp()}, {key: 'usi', value: 'should be skipped because timestamp', exp: misc.getUnixTimestamp(), network: 'adnuntius'}, {key: 'usi', value: usi, exp: misc.getUnixTimestamp(100), network: 'adnuntius'}, {key: 'usi', value: 'should be skipped because timestamp', exp: misc.getUnixTimestamp()}] let storage; + // need this to make the restore work correctly -- something to do with stubbing static prototype methods + let stub1 = {}, stub2 = {}; + before(() => { getGlobal().bidderSettings = { adnuntius: { @@ -34,6 +37,13 @@ describe('adnuntiusBidAdapter', function() { afterEach(function() { config.resetConfig(); + + if (stub1.restore) { + stub1.restore(); + } + if (stub2.restore) { + stub2.restore(); + } }); const tzo = new Date().getTimezoneOffset(); @@ -457,13 +467,20 @@ describe('adnuntiusBidAdapter', function() { describe('buildRequests', function() { it('Test requests', function() { + stub1 = sinon.stub(URLSearchParams.prototype, 'has').callsFake(() => { + return true; + }); + stub2 = sinon.stub(URLSearchParams.prototype, 'get').callsFake(() => { + return 'overridden-value'; + }); + const request = spec.buildRequests(bidderRequests, {}); expect(request.length).to.equal(1); expect(request[0]).to.have.property('bid'); const bid = request[0].bid[0] expect(bid).to.have.property('bidId'); expect(request[0]).to.have.property('url'); - expect(request[0].url).to.equal(ENDPOINT_URL); + expect(request[0].url).to.equal(ENDPOINT_URL.replace('format=prebid', 'format=prebid&so=overridden-value')); expect(request[0]).to.have.property('data'); expect(request[0].data).to.equal('{"adUnits":[{"auId":"000000000008b6bc","targetId":"123","maxDeals":1,"dimensions":[[640,480],[600,400]]},{"auId":"0000000000000551","targetId":"adn-0000000000000551","dimensions":[[1640,1480],[1600,1400]]}]}'); }); From d3d452e2d83f49957f9efa5353634fe579cf75c2 Mon Sep 17 00:00:00 2001 From: MartinGumGum <109325501+MartinGumGum@users.noreply.github.com> Date: Tue, 23 Apr 2024 15:17:22 -0700 Subject: [PATCH 4/4] GumGum Bid Adapter: stringify dsa object (#11386) * displaymanager: add field for displaymanager and displaymanagerver * fix: displayManager to include 'gumgum' * fix: test case * Bug, stringifying DSA object. --------- Co-authored-by: ahzgg Co-authored-by: ahzgg <163184035+ahzgg@users.noreply.github.com> --- modules/gumgumBidAdapter.js | 2 +- test/spec/modules/gumgumBidAdapter_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/gumgumBidAdapter.js b/modules/gumgumBidAdapter.js index ff249ec9b50..4b12431302f 100644 --- a/modules/gumgumBidAdapter.js +++ b/modules/gumgumBidAdapter.js @@ -424,7 +424,7 @@ function buildRequests(validBidRequests, bidderRequest) { } const dsa = deepAccess(bidderRequest, 'ortb2.regs.ext.dsa'); if (dsa) { - data.dsa = dsa + data.dsa = JSON.stringify(dsa) } if (coppa) { data.coppa = coppa; diff --git a/test/spec/modules/gumgumBidAdapter_spec.js b/test/spec/modules/gumgumBidAdapter_spec.js index 3720d357b51..1c5940c06a3 100644 --- a/test/spec/modules/gumgumBidAdapter_spec.js +++ b/test/spec/modules/gumgumBidAdapter_spec.js @@ -610,7 +610,7 @@ describe('gumgumAdapter', function () { // Call the buildRequests function to generate the bid request const [bidRequest] = spec.buildRequests(bidRequests, fakeBidRequest); // Assert that the DSA information in the bid request matches the provided ORTB2 data - expect(bidRequest.data.dsa).to.deep.equal(fakeBidRequest.ortb2.regs.ext.dsa); + expect(bidRequest.data.dsa).to.deep.equal(JSON.stringify(fakeBidRequest.ortb2.regs.ext.dsa)); }); it('should not set coppa parameter if coppa config is set to false', function () { config.setConfig({