From 0a3a36f4afbd115b37a73a774dc4405b714dacb8 Mon Sep 17 00:00:00 2001 From: abermanov Date: Tue, 10 Jun 2025 16:01:48 +0200 Subject: [PATCH 1/3] Zeta SSP Adapter: add GPP support. --- modules/zeta_global_sspBidAdapter.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index aa35066e26..c4ef5b3f70 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -167,6 +167,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); + } + // schain if (validBidRequests[0].schain) { payload.source = { @@ -237,7 +246,7 @@ export const spec = { /** * Register User Sync. */ - getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent) => { + getUserSyncs: (syncOptions, responses, gdprConsent, uspConsent, gppConsent) => { let syncurl = ''; // Attaching GDPR Consent Params in UserSync url @@ -251,6 +260,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'; From 0f1d7d41a3e9d5e6be4f8f89516472451f0ac3f4 Mon Sep 17 00:00:00 2001 From: abermanov Date: Mon, 16 Jun 2025 16:32:44 +0200 Subject: [PATCH 2/3] Zeta SSP Adapter: tests for GPP. --- modules/zeta_global_sspBidAdapter.js | 8 +- .../modules/zeta_global_sspBidAdapter_spec.js | 178 ++++++++++++++++-- 2 files changed, 164 insertions(+), 22 deletions(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index c4ef5b3f70..11b28744d0 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -139,9 +139,11 @@ export const spec = { } }; const rInfo = bidderRequest.refererInfo; - // TODO: do the fallbacks make sense here? - payload.site.page = rInfo.page || rInfo.topmostLocation; - payload.site.domain = parseDomain(payload.site.page, {noLeadingWww: true}); + if (rInfo) { + // TODO: do the fallbacks make sense here? + payload.site.page = rInfo.page || rInfo.topmostLocation; + payload.site.domain = parseDomain(payload.site.page, {noLeadingWww: true}); + } payload.device.ua = navigator.userAgent; payload.device.language = navigator.language; diff --git a/test/spec/modules/zeta_global_sspBidAdapter_spec.js b/test/spec/modules/zeta_global_sspBidAdapter_spec.js index 7beac2f820..df1114a8ce 100644 --- a/test/spec/modules/zeta_global_sspBidAdapter_spec.js +++ b/test/spec/modules/zeta_global_sspBidAdapter_spec.js @@ -1,6 +1,7 @@ import {spec} from '../../../modules/zeta_global_sspBidAdapter.js' import {BANNER, VIDEO} from '../../../src/mediaTypes'; import {deepClone} from '../../../src/utils'; +import {expect} from 'chai'; describe('Zeta Ssp Bid Adapter', function () { const eids = [ @@ -429,28 +430,97 @@ describe('Zeta Ssp Bid Adapter', function () { expect(bid3.meta.advertiserDomains).to.equal(receivedBid3.adomain); }); - it('Different cases for user syncs', function () { + describe('getUserSyncs', function() { const USER_SYNC_URL_IFRAME = 'https://ssp.disqus.com/sync?type=iframe'; const USER_SYNC_URL_IMAGE = 'https://ssp.disqus.com/sync?type=image'; - const sync1 = spec.getUserSyncs({iframeEnabled: true})[0]; - expect(sync1.type).to.equal('iframe'); - expect(sync1.url).to.include(USER_SYNC_URL_IFRAME); - - const sync2 = spec.getUserSyncs({iframeEnabled: false})[0]; - expect(sync2.type).to.equal('image'); - expect(sync2.url).to.include(USER_SYNC_URL_IMAGE); - - const sync3 = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true})[0]; - expect(sync3.type).to.equal('iframe'); - expect(sync3.url).to.include(USER_SYNC_URL_IFRAME); - expect(sync3.url).to.include('&gdpr='); - - const sync4 = spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true}, 'test')[0]; - expect(sync4.type).to.equal('iframe'); - expect(sync4.url).to.include(USER_SYNC_URL_IFRAME); - expect(sync4.url).to.include('&gdpr='); - expect(sync4.url).to.include('&us_privacy='); + it('execute as per config', function() { + expect(spec.getUserSyncs({iframeEnabled: true})).to.deep.equal([{ + type: 'iframe', url: `${USER_SYNC_URL_IFRAME}` + }]); + expect(spec.getUserSyncs({iframeEnabled: false})).to.deep.equal([{ + type: 'image', url: `${USER_SYNC_URL_IMAGE}` + }]); + }); + + it('GDPR', function() { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true, consentString: 'foo'})).to.deep.equal([{ + type: 'iframe', url: `${USER_SYNC_URL_IFRAME}&gdpr=1&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: false, consentString: 'foo'})).to.deep.equal([{ + type: 'iframe', url: `${USER_SYNC_URL_IFRAME}&gdpr=0&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({iframeEnabled: true}, {}, {gdprApplies: true, consentString: undefined})).to.deep.equal([{ + type: 'iframe', url: `${USER_SYNC_URL_IFRAME}&gdpr=1&gdpr_consent=` + }]); + expect(spec.getUserSyncs({iframeEnabled: false}, {}, {gdprApplies: true, consentString: 'foo'})).to.deep.equal([{ + type: 'image', url: `${USER_SYNC_URL_IMAGE}&gdpr=1&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({iframeEnabled: false}, {}, {gdprApplies: false, consentString: 'foo'})).to.deep.equal([{ + type: 'image', url: `${USER_SYNC_URL_IMAGE}&gdpr=0&gdpr_consent=foo` + }]); + expect(spec.getUserSyncs({iframeEnabled: false}, {}, {gdprApplies: true, consentString: undefined})).to.deep.equal([{ + type: 'image', url: `${USER_SYNC_URL_IMAGE}&gdpr=1&gdpr_consent=` + }]); + }); + + it('CCPA', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, '1NYN')).to.deep.equal([{ + type: 'iframe', url: `${USER_SYNC_URL_IFRAME}&us_privacy=1NYN` + }]); + expect(spec.getUserSyncs({ iframeEnabled: false }, {}, undefined, '1NYN')).to.deep.equal([{ + type: 'image', url: `${USER_SYNC_URL_IMAGE}&us_privacy=1NYN` + }]); + }); + + 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: `${USER_SYNC_URL_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: `${USER_SYNC_URL_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: `${USER_SYNC_URL_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: `${USER_SYNC_URL_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: `${USER_SYNC_URL_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: `${USER_SYNC_URL_IMAGE}&gpp=${encodeURIComponent(gppConsent.gppString)}&gpp_sid=${encodeURIComponent(gppConsent.applicableSections)}` + }]); + }); + }); }); it('Test provide gdpr and ccpa values in payload', function () { @@ -462,6 +532,76 @@ describe('Zeta Ssp Bid Adapter', function () { expect(payload.regs.ext.us_privacy).to.eql('someCCPAString'); }); + describe('buildRequests: 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(bannerRequest, 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(bannerRequest, 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(bannerRequest, 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('Test do not override user object', function () { const request = spec.buildRequests(bannerRequest, bannerRequest[0]); const payload = JSON.parse(request.data); From 19f807b82a42b00a237402cb53fb0fbc88fe0f59 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Thu, 26 Jun 2025 14:52:10 -0400 Subject: [PATCH 3/3] Update zeta_global_sspBidAdapter.js --- modules/zeta_global_sspBidAdapter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/zeta_global_sspBidAdapter.js b/modules/zeta_global_sspBidAdapter.js index 6cec8a35e4..736fd71762 100644 --- a/modules/zeta_global_sspBidAdapter.js +++ b/modules/zeta_global_sspBidAdapter.js @@ -139,7 +139,6 @@ export const spec = { }; const rInfo = bidderRequest.refererInfo; if (rInfo) { - // TODO: do the fallbacks make sense here? payload.site.page = cropPage(rInfo.page || rInfo.topmostLocation); payload.site.domain = parseDomain(payload.site.page, {noLeadingWww: true}); }