From c472d98bb87f25861b1bc85d808d1a94841729b8 Mon Sep 17 00:00:00 2001 From: jwier Date: Tue, 13 Jul 2021 10:33:46 -0700 Subject: [PATCH 1/2] add getUserSyncs to the conversant adapter --- modules/conversantBidAdapter.js | 44 +++++++++++++- .../spec/modules/conversantBidAdapter_spec.js | 58 ++++++++++++++++++- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index 5af399365bd..68ec7979484 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -1,7 +1,7 @@ import * as utils from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; -import { BANNER, VIDEO } from '../src/mediaTypes.js'; -import { getStorageManager } from '../src/storageManager.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {getStorageManager} from '../src/storageManager.js'; const GVLID = 24; export const storage = getStorageManager(GVLID); @@ -248,6 +248,46 @@ export const spec = { 'secure': 'number', 'mobile': 'number' }, params); + }, + + /** + * Register User Sync. + */ + getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) { + let params = {} + const syncs = []; + + // Attaching GDPR Consent Params in UserSync url + if (gdprConsent) { + params.gdpr = (gdprConsent.gdprApplies) ? 1 : 0; + params.gdpr_consent = encodeURIComponent(gdprConsent.consentString || ''); + } + + // CCPA + if (uspConsent) { + params.us_privacy = encodeURIComponent(uspConsent); + } + + if (responses && responses.ext) { + const pixels = [{urls: responses.ext.fsyncs, type: 'iframe'}, {urls: responses.ext.psyncs, type: 'image'}] + .filter((entry) => { + return entry.urls && + ((entry.type === 'iframe' && syncOptions.iframeEnabled) || + (entry.type === 'image' && syncOptions.pixelEnabled)); + }) + .flatMap((entry) => { + return entry.urls.flatMap((endpoint) => { + let urlInfo = utils.parseUrl(endpoint); + utils.mergeDeep(urlInfo.search, params); + if (Object.keys(urlInfo.search).length === 0) { + delete urlInfo.search; // empty search object causes buildUrl to add a trailing ? to the url + } + return {type: entry.type, url: utils.buildUrl(urlInfo)}; + }); + }); + syncs.push(...pixels); + } + return syncs; } }; diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 96a7f419341..2e37416e96e 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -1,7 +1,7 @@ import {expect} from 'chai'; import {spec, storage} from 'modules/conversantBidAdapter.js'; import * as utils from 'src/utils.js'; -import { createEidsArray } from 'modules/userId/eids.js'; +import {createEidsArray} from 'modules/userId/eids.js'; describe('Conversant adapter tests', function() { const siteId = '108060'; @@ -661,4 +661,60 @@ describe('Conversant adapter tests', function() { expect(payload.imp[0]).to.have.property('bidfloor', 0); }); }); + + describe('getUserSyncs', function() { + const syncurl_iframe = 'https://sync.dotomi.com/iframe'; + const syncurl_image = 'https://sync.dotomi.com/pixel'; + const cnvrResponse = {ext: {psyncs: [syncurl_image], fsyncs: [syncurl_iframe]}}; + let sandbox; + beforeEach(function () { + sandbox = sinon.sandbox.create(); + }); + afterEach(function() { + sandbox.restore(); + }); + + it('empty params', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, {}, undefined, undefined)) + .to.deep.equal([]); + expect(spec.getUserSyncs({ iframeEnabled: true }, {ext: {}}, undefined, undefined)) + .to.deep.equal([]); + expect(spec.getUserSyncs({ iframeEnabled: true }, cnvrResponse, undefined, undefined)) + .to.deep.equal([{ type: 'iframe', url: syncurl_iframe }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, cnvrResponse, undefined, undefined)) + .to.deep.equal([{ type: 'image', url: syncurl_image }]); + expect(spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, cnvrResponse, undefined, undefined)) + .to.deep.equal([{type: 'iframe', url: syncurl_iframe}, {type: 'image', url: syncurl_image}]); + }); + + it('URL building', function() { + expect(spec.getUserSyncs({pixelEnabled: true}, {ext: {psyncs: [`${syncurl_image}?sid=1234`]}}, undefined, undefined)) + .to.deep.equal([{type: 'image', url: `${syncurl_image}?sid=1234`}]); + expect(spec.getUserSyncs({pixelEnabled: true}, {ext: {psyncs: [`${syncurl_image}?sid=1234`]}}, undefined, '1NYN')) + .to.deep.equal([{type: 'image', url: `${syncurl_image}?sid=1234&us_privacy=1NYN`}]); + }); + + it('GDPR', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, cnvrResponse, {gdprApplies: true, consentString: 'consentstring'}, undefined)) + .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?gdpr=1&gdpr_consent=consentstring` }]); + expect(spec.getUserSyncs({ iframeEnabled: true }, cnvrResponse, {gdprApplies: false, consentString: 'consentstring'}, undefined)) + .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?gdpr=0&gdpr_consent=consentstring` }]); + expect(spec.getUserSyncs({ iframeEnabled: true }, cnvrResponse, {gdprApplies: true, consentString: undefined}, undefined)) + .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?gdpr=1&gdpr_consent=` }]); + + expect(spec.getUserSyncs({ pixelEnabled: true }, cnvrResponse, {gdprApplies: true, consentString: 'consentstring'}, undefined)) + .to.deep.equal([{ type: 'image', url: `${syncurl_image}?gdpr=1&gdpr_consent=consentstring` }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, cnvrResponse, {gdprApplies: false, consentString: 'consentstring'}, undefined)) + .to.deep.equal([{ type: 'image', url: `${syncurl_image}?gdpr=0&gdpr_consent=consentstring` }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, cnvrResponse, {gdprApplies: true, consentString: undefined}, undefined)) + .to.deep.equal([{ type: 'image', url: `${syncurl_image}?gdpr=1&gdpr_consent=` }]); + }); + + it('US_Privacy', function() { + expect(spec.getUserSyncs({ iframeEnabled: true }, cnvrResponse, undefined, '1NYN')) + .to.deep.equal([{ type: 'iframe', url: `${syncurl_iframe}?us_privacy=1NYN` }]); + expect(spec.getUserSyncs({ pixelEnabled: true }, cnvrResponse, undefined, '1NYN')) + .to.deep.equal([{ type: 'image', url: `${syncurl_image}?us_privacy=1NYN` }]); + }); + }); }); From e593b78c9cbc7accec1a004972c52aa2261a7193 Mon Sep 17 00:00:00 2001 From: jwier Date: Thu, 12 Aug 2021 13:47:40 -0700 Subject: [PATCH 2/2] Review Changes --- modules/conversantBidAdapter.js | 12 +++++++----- test/spec/modules/conversantBidAdapter_spec.js | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/conversantBidAdapter.js b/modules/conversantBidAdapter.js index 68ec7979484..a1ca094273b 100644 --- a/modules/conversantBidAdapter.js +++ b/modules/conversantBidAdapter.js @@ -254,7 +254,7 @@ export const spec = { * Register User Sync. */ getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) { - let params = {} + let params = {}; const syncs = []; // Attaching GDPR Consent Params in UserSync url @@ -275,16 +275,18 @@ export const spec = { ((entry.type === 'iframe' && syncOptions.iframeEnabled) || (entry.type === 'image' && syncOptions.pixelEnabled)); }) - .flatMap((entry) => { - return entry.urls.flatMap((endpoint) => { + .map((entry) => { + return entry.urls.map((endpoint) => { let urlInfo = utils.parseUrl(endpoint); utils.mergeDeep(urlInfo.search, params); if (Object.keys(urlInfo.search).length === 0) { delete urlInfo.search; // empty search object causes buildUrl to add a trailing ? to the url } return {type: entry.type, url: utils.buildUrl(urlInfo)}; - }); - }); + }) + .reduce((x, y) => x.concat(y), []); + }) + .reduce((x, y) => x.concat(y), []); syncs.push(...pixels); } return syncs; diff --git a/test/spec/modules/conversantBidAdapter_spec.js b/test/spec/modules/conversantBidAdapter_spec.js index 2e37416e96e..e871ab3f9c6 100644 --- a/test/spec/modules/conversantBidAdapter_spec.js +++ b/test/spec/modules/conversantBidAdapter_spec.js @@ -663,8 +663,8 @@ describe('Conversant adapter tests', function() { }); describe('getUserSyncs', function() { - const syncurl_iframe = 'https://sync.dotomi.com/iframe'; - const syncurl_image = 'https://sync.dotomi.com/pixel'; + const syncurl_iframe = 'https://sync.dotomi.com:8080/iframe'; + const syncurl_image = 'https://sync.dotomi.com:8080/pixel'; const cnvrResponse = {ext: {psyncs: [syncurl_image], fsyncs: [syncurl_iframe]}}; let sandbox; beforeEach(function () {