From cf827a03ac204ca663b80aeaf39cb002ade19b7b Mon Sep 17 00:00:00 2001 From: Zeeshan Rasool Date: Wed, 9 Nov 2022 12:11:09 +0000 Subject: [PATCH 01/17] add logic to parse and pass ssp data to appnexus --- modules/permutiveRtdProvider.js | 52 +++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index c11d1c12436..3cbe5a5c3ff 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -10,6 +10,7 @@ import {submodule} from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safeJSONParse} from '../src/utils.js'; import {includes} from '../src/polyfill.js'; +import {config} from '../src/config' const MODULE_NAME = 'permutive' @@ -174,6 +175,8 @@ function setSegments (reqBidsConfigObj, moduleConfig, segmentData) { return } + passCohortsToAppnexusAuctionKeywords(segmentData?.ssp ?? {}) + adUnits.forEach(adUnit => { adUnit.bids.forEach(bid => { let { bidder } = bid @@ -238,8 +241,7 @@ function getDefaultBidderFn (bidder) { deepSetValue(bid, 'params.visitor.p_standard', data.ac) } if (data.rubicon && data.rubicon.length) { - const rubiconCohorts = deepAccess(bid, 'params.video') ? data.rubicon.map(String) : data.rubicon - deepSetValue(bid, 'params.visitor.permutive', rubiconCohorts) + deepSetValue(bid, 'params.visitor.permutive', data.rubicon) } return bid @@ -275,6 +277,22 @@ export function isPermutiveOnPage () { return typeof window.permutive !== 'undefined' && typeof window.permutive.ready === 'function' } +/** + * Parses the _pssps string in LS into a JS object + * @param {string} sspKVString -- in form of "stuff,stuff;somemorestuff,1234" + * @example For a given _pssps string in the form of "appnexus,pubmatic;1234,5678,abcd" this function will return { ssps: [appnexus, pubmatic], cohorts: [1234, 5678, "abcd"] } + */ +function permutiveSspParser(sspKVString) { + if (!sspKVString) return {} + + const intermediateTuple = sspKVString.split(';') + + return { + ssps: intermediateTuple[0].split(','), + cohorts: intermediateTuple[1].split(',') + } +} + /** * Get all relevant segment IDs in an object * @param {number} maxSegs - Maximum number of segments to be included @@ -284,12 +302,14 @@ export function getSegments (maxSegs) { const legacySegs = readSegments('_psegs').map(Number).filter(seg => seg >= 1000000).map(String) const _ppam = readSegments('_ppam') const _pcrprs = readSegments('_pcrprs') + const _pssps = readPermutiveSspData() const segments = { ac: [..._pcrprs, ..._ppam, ...legacySegs], rubicon: readSegments('_prubicons'), appnexus: readSegments('_papns'), gam: readSegments('_pdfps'), + ssp: _pssps, } for (const bidder in segments) { @@ -313,6 +333,19 @@ function readSegments (key) { } } +/** + * Gets the permutive ssp data string and parses it into an object + * or returns an empty object + * @return {{ ssp?: string[], cohorts?: string[] }} + */ +function readPermutiveSspData() { + try { + return permutiveSspParser(storage.getDataFromLocalStorage('_pssps') || '') + } catch (e) { + return {} + } +} + const unknownIabSegmentId = '_unknown_' /** @@ -341,6 +374,21 @@ function iabSegmentId(permutiveSegmentId, iabIds) { return iabIds[permutiveSegmentId] || unknownIabSegmentId } +/** + * Passes Permutive's standard cohorts to Appnexus/Xandr via auction level keywords + * @param {Object} sspData -- {ssp?: string[], cohorts?: string[]} + */ +function passCohortsToAppnexusAuctionKeywords(sspData) { + const shouldSetConfig = Object.hasOwnProperty('ssp') && Object.hasOwnProperty('cohorts') && sspData.ssp.includes('appnexus') + if(shouldSetConfig) { + config.setConfig({ + appnexusAuctionKeywords: { + 'p_standard': sspData.cohorts + } + }) + } +} + /** @type {RtdSubmodule} */ export const permutiveSubmodule = { name: MODULE_NAME, From 6f4cb684966a7237d9e63d118d7f79650b489579 Mon Sep 17 00:00:00 2001 From: Zeeshan Rasool Date: Wed, 9 Nov 2022 12:32:20 +0000 Subject: [PATCH 02/17] simplify shouldSetConfig condition --- modules/permutiveRtdProvider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 3cbe5a5c3ff..290b0dfd2bd 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -175,7 +175,7 @@ function setSegments (reqBidsConfigObj, moduleConfig, segmentData) { return } - passCohortsToAppnexusAuctionKeywords(segmentData?.ssp ?? {}) + passCohortsToAppnexusAuctionKeywords(segmentData.ssp) adUnits.forEach(adUnit => { adUnit.bids.forEach(bid => { @@ -379,7 +379,7 @@ function iabSegmentId(permutiveSegmentId, iabIds) { * @param {Object} sspData -- {ssp?: string[], cohorts?: string[]} */ function passCohortsToAppnexusAuctionKeywords(sspData) { - const shouldSetConfig = Object.hasOwnProperty('ssp') && Object.hasOwnProperty('cohorts') && sspData.ssp.includes('appnexus') + const shouldSetConfig = sspData?.ssp?.includes('appnexus') && sspData?.cohorts if(shouldSetConfig) { config.setConfig({ appnexusAuctionKeywords: { From 841c6e0af8e8779b7b0f2e57da2005e6b7c06cd6 Mon Sep 17 00:00:00 2001 From: Paulius Imbrasas Date: Wed, 9 Nov 2022 17:52:14 +0000 Subject: [PATCH 03/17] Write SSP cohorts into p_standard targeting (+ bug fixes) --- modules/permutiveRtdProvider.js | 30 +++++++++++++------ .../spec/modules/permutiveRtdProvider_spec.js | 4 ++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 290b0dfd2bd..f02b1571c55 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -10,7 +10,7 @@ import {submodule} from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safeJSONParse} from '../src/utils.js'; import {includes} from '../src/polyfill.js'; -import {config} from '../src/config' +import {config} from '../src/config.js'; const MODULE_NAME = 'permutive' @@ -225,10 +225,19 @@ function getCustomBidderFn (moduleConfig, bidder) { * @return {Object} Bidder function */ function getDefaultBidderFn (bidder) { + const isPStandardTargetingEnabled = (data, acEnabled) => { + return (acEnabled && data.ac && data.ac.length) || (data.ssp && data.ssp.length) + } + const pStandardTargeting = (data, acEnabled) => { + const ac = (acEnabled) ? (data.ac ?? []) : [] + const ssp = data.ssp ?? [] + return [...new Set([...ac, ...ssp])] + } const bidderMap = { appnexus: function (bid, data, acEnabled) { - if (acEnabled && data.ac && data.ac.length) { - deepSetValue(bid, 'params.keywords.p_standard', data.ac) + if (isPStandardTargetingEnabled(data, acEnabled)) { + const segments = pStandardTargeting(data, acEnabled) + deepSetValue(bid, 'params.keywords.p_standard', segments) } if (data.appnexus && data.appnexus.length) { deepSetValue(bid, 'params.keywords.permutive', data.appnexus) @@ -237,8 +246,9 @@ function getDefaultBidderFn (bidder) { return bid }, rubicon: function (bid, data, acEnabled) { - if (acEnabled && data.ac && data.ac.length) { - deepSetValue(bid, 'params.visitor.p_standard', data.ac) + if (isPStandardTargetingEnabled(data, acEnabled)) { + const segments = pStandardTargeting(data, acEnabled) + deepSetValue(bid, 'params.visitor.p_standard', segments) } if (data.rubicon && data.rubicon.length) { deepSetValue(bid, 'params.visitor.permutive', data.rubicon) @@ -247,8 +257,9 @@ function getDefaultBidderFn (bidder) { return bid }, ozone: function (bid, data, acEnabled) { - if (acEnabled && data.ac && data.ac.length) { - deepSetValue(bid, 'params.customData.0.targeting.p_standard', data.ac) + if (isPStandardTargetingEnabled(data, acEnabled)) { + const segments = pStandardTargeting(data, acEnabled) + deepSetValue(bid, 'params.customData.0.targeting.p_standard', segments) } return bid @@ -309,13 +320,14 @@ export function getSegments (maxSegs) { rubicon: readSegments('_prubicons'), appnexus: readSegments('_papns'), gam: readSegments('_pdfps'), - ssp: _pssps, } for (const bidder in segments) { segments[bidder] = segments[bidder].slice(0, maxSegs) } + segments.ssp = _pssps + return segments } @@ -380,7 +392,7 @@ function iabSegmentId(permutiveSegmentId, iabIds) { */ function passCohortsToAppnexusAuctionKeywords(sspData) { const shouldSetConfig = sspData?.ssp?.includes('appnexus') && sspData?.cohorts - if(shouldSetConfig) { + if (shouldSetConfig) { config.setConfig({ appnexusAuctionKeywords: { 'p_standard': sspData.cohorts diff --git a/test/spec/modules/permutiveRtdProvider_spec.js b/test/spec/modules/permutiveRtdProvider_spec.js index 3f104ee1e2e..03e0db3790d 100644 --- a/test/spec/modules/permutiveRtdProvider_spec.js +++ b/test/spec/modules/permutiveRtdProvider_spec.js @@ -555,6 +555,7 @@ function transformedTargeting (data = getTargetingData()) { appnexus: data._papns, rubicon: data._prubicons, gam: data._pdfps, + ssp: data._pssps, } } @@ -565,7 +566,8 @@ function getTargetingData () { _papns: ['appnexus1', 'appnexus2'], _psegs: ['1234', '1000001', '1000002'], _ppam: ['ppam1', 'ppam2'], - _pcrprs: ['pcrprs1', 'pcrprs2'] + _pcrprs: ['pcrprs1', 'pcrprs2', 'dup'], + _pssps: ['xyz', 'abc', 'dup'], } } From 7232ac0832b930fa5a2e1b06ae28ffa2e5ce3b7a Mon Sep 17 00:00:00 2001 From: Zeeshan Rasool Date: Fri, 11 Nov 2022 14:42:00 +0000 Subject: [PATCH 04/17] fix tests --- modules/permutiveRtdProvider.js | 51 +++++-------------- .../spec/modules/permutiveRtdProvider_spec.js | 18 ++++--- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index f02b1571c55..9fc4915b654 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -226,12 +226,12 @@ function getCustomBidderFn (moduleConfig, bidder) { */ function getDefaultBidderFn (bidder) { const isPStandardTargetingEnabled = (data, acEnabled) => { - return (acEnabled && data.ac && data.ac.length) || (data.ssp && data.ssp.length) + return (acEnabled && data.ac && data.ac.length) || (data.ssp && data.ssp.cohorts.length) } const pStandardTargeting = (data, acEnabled) => { const ac = (acEnabled) ? (data.ac ?? []) : [] - const ssp = data.ssp ?? [] - return [...new Set([...ac, ...ssp])] + const ssp = (data.ssp && data.ssp.cohorts) ? data.ssp : { ssps: [], cohorts: [] } + return [...new Set([...ac, ...ssp.cohorts])] } const bidderMap = { appnexus: function (bid, data, acEnabled) { @@ -251,7 +251,7 @@ function getDefaultBidderFn (bidder) { deepSetValue(bid, 'params.visitor.p_standard', segments) } if (data.rubicon && data.rubicon.length) { - deepSetValue(bid, 'params.visitor.permutive', data.rubicon) + deepSetValue(bid, 'params.visitor.permutive', data.rubicon.map(String)) } return bid @@ -288,22 +288,6 @@ export function isPermutiveOnPage () { return typeof window.permutive !== 'undefined' && typeof window.permutive.ready === 'function' } -/** - * Parses the _pssps string in LS into a JS object - * @param {string} sspKVString -- in form of "stuff,stuff;somemorestuff,1234" - * @example For a given _pssps string in the form of "appnexus,pubmatic;1234,5678,abcd" this function will return { ssps: [appnexus, pubmatic], cohorts: [1234, 5678, "abcd"] } - */ -function permutiveSspParser(sspKVString) { - if (!sspKVString) return {} - - const intermediateTuple = sspKVString.split(';') - - return { - ssps: intermediateTuple[0].split(','), - cohorts: intermediateTuple[1].split(',') - } -} - /** * Get all relevant segment IDs in an object * @param {number} maxSegs - Maximum number of segments to be included @@ -313,21 +297,25 @@ export function getSegments (maxSegs) { const legacySegs = readSegments('_psegs').map(Number).filter(seg => seg >= 1000000).map(String) const _ppam = readSegments('_ppam') const _pcrprs = readSegments('_pcrprs') - const _pssps = readPermutiveSspData() const segments = { ac: [..._pcrprs, ..._ppam, ...legacySegs], rubicon: readSegments('_prubicons'), appnexus: readSegments('_papns'), gam: readSegments('_pdfps'), + ssp: readSegments('_pssps'), } for (const bidder in segments) { - segments[bidder] = segments[bidder].slice(0, maxSegs) + if (bidder === 'ssp') { + if (segments[bidder].cohorts && Array.isArray(segments[bidder].cohorts)) { + segments[bidder].cohorts = segments[bidder].cohorts.slice(0, maxSegs) + } + } else { + segments[bidder] = segments[bidder].slice(0, maxSegs) + } } - segments.ssp = _pssps - return segments } @@ -345,19 +333,6 @@ function readSegments (key) { } } -/** - * Gets the permutive ssp data string and parses it into an object - * or returns an empty object - * @return {{ ssp?: string[], cohorts?: string[] }} - */ -function readPermutiveSspData() { - try { - return permutiveSspParser(storage.getDataFromLocalStorage('_pssps') || '') - } catch (e) { - return {} - } -} - const unknownIabSegmentId = '_unknown_' /** @@ -391,7 +366,7 @@ function iabSegmentId(permutiveSegmentId, iabIds) { * @param {Object} sspData -- {ssp?: string[], cohorts?: string[]} */ function passCohortsToAppnexusAuctionKeywords(sspData) { - const shouldSetConfig = sspData?.ssp?.includes('appnexus') && sspData?.cohorts + const shouldSetConfig = sspData?.ssps?.includes('appnexus') && sspData?.cohorts if (shouldSetConfig) { config.setConfig({ appnexusAuctionKeywords: { diff --git a/test/spec/modules/permutiveRtdProvider_spec.js b/test/spec/modules/permutiveRtdProvider_spec.js index 03e0db3790d..4208fb7f7ea 100644 --- a/test/spec/modules/permutiveRtdProvider_spec.js +++ b/test/spec/modules/permutiveRtdProvider_spec.js @@ -291,7 +291,11 @@ describe('permutiveRtdProvider', function () { const segments = getSegments(max) for (const key in segments) { - expect(segments[key]).to.have.length(max) + if (key === 'ssp') { + expect(segments[key].cohorts).to.have.length(max) + } else { + expect(segments[key]).to.have.length(max) + } } }) }) @@ -311,7 +315,7 @@ describe('permutiveRtdProvider', function () { if (bidder === 'appnexus') { expect(deepAccess(params, 'keywords.permutive')).to.eql(data.appnexus) - expect(deepAccess(params, 'keywords.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'keywords.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -332,7 +336,7 @@ describe('permutiveRtdProvider', function () { if (bidder === 'rubicon') { expect(deepAccess(params, 'visitor.permutive')).to.eql(data.rubicon) - expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -363,7 +367,7 @@ describe('permutiveRtdProvider', function () { deepAccess(params, 'visitor.permutive'), 'Should map all targeting values to a string', ).to.eql(data.rubicon.map(String)) - expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -383,7 +387,7 @@ describe('permutiveRtdProvider', function () { const { bidder, params } = bid if (bidder === 'ozone') { - expect(deepAccess(params, 'customData.0.targeting.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'customData.0.targeting.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -417,7 +421,7 @@ describe('permutiveRtdProvider', function () { if (bidder === 'rubicon') { expect(deepAccess(params, 'visitor.permutive')).to.eql(data.gam) - expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -567,7 +571,7 @@ function getTargetingData () { _psegs: ['1234', '1000001', '1000002'], _ppam: ['ppam1', 'ppam2'], _pcrprs: ['pcrprs1', 'pcrprs2', 'dup'], - _pssps: ['xyz', 'abc', 'dup'], + _pssps: { ssps: ['xyz', 'abc', 'dup'], cohorts: ['123', 'abc'] } } } From 35edee7ca299ef19da83abdd1e3049b32decf8af Mon Sep 17 00:00:00 2001 From: Paulius Imbrasas Date: Fri, 11 Nov 2022 14:50:18 +0000 Subject: [PATCH 05/17] Simplify --- modules/permutiveRtdProvider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 9fc4915b654..5911d599fff 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -230,8 +230,8 @@ function getDefaultBidderFn (bidder) { } const pStandardTargeting = (data, acEnabled) => { const ac = (acEnabled) ? (data.ac ?? []) : [] - const ssp = (data.ssp && data.ssp.cohorts) ? data.ssp : { ssps: [], cohorts: [] } - return [...new Set([...ac, ...ssp.cohorts])] + const ssp = data?.ssp?.cohorts ?? [] + return [...new Set([...ac, ...ssp])] } const bidderMap = { appnexus: function (bid, data, acEnabled) { From 29bfb8362161b49f16d4be2a0294d73387255187 Mon Sep 17 00:00:00 2001 From: Zeeshan Rasool Date: Wed, 9 Nov 2022 12:11:09 +0000 Subject: [PATCH 06/17] add logic to parse and pass ssp data to appnexus --- modules/permutiveRtdProvider.js | 52 +++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index c11d1c12436..3cbe5a5c3ff 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -10,6 +10,7 @@ import {submodule} from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safeJSONParse} from '../src/utils.js'; import {includes} from '../src/polyfill.js'; +import {config} from '../src/config' const MODULE_NAME = 'permutive' @@ -174,6 +175,8 @@ function setSegments (reqBidsConfigObj, moduleConfig, segmentData) { return } + passCohortsToAppnexusAuctionKeywords(segmentData?.ssp ?? {}) + adUnits.forEach(adUnit => { adUnit.bids.forEach(bid => { let { bidder } = bid @@ -238,8 +241,7 @@ function getDefaultBidderFn (bidder) { deepSetValue(bid, 'params.visitor.p_standard', data.ac) } if (data.rubicon && data.rubicon.length) { - const rubiconCohorts = deepAccess(bid, 'params.video') ? data.rubicon.map(String) : data.rubicon - deepSetValue(bid, 'params.visitor.permutive', rubiconCohorts) + deepSetValue(bid, 'params.visitor.permutive', data.rubicon) } return bid @@ -275,6 +277,22 @@ export function isPermutiveOnPage () { return typeof window.permutive !== 'undefined' && typeof window.permutive.ready === 'function' } +/** + * Parses the _pssps string in LS into a JS object + * @param {string} sspKVString -- in form of "stuff,stuff;somemorestuff,1234" + * @example For a given _pssps string in the form of "appnexus,pubmatic;1234,5678,abcd" this function will return { ssps: [appnexus, pubmatic], cohorts: [1234, 5678, "abcd"] } + */ +function permutiveSspParser(sspKVString) { + if (!sspKVString) return {} + + const intermediateTuple = sspKVString.split(';') + + return { + ssps: intermediateTuple[0].split(','), + cohorts: intermediateTuple[1].split(',') + } +} + /** * Get all relevant segment IDs in an object * @param {number} maxSegs - Maximum number of segments to be included @@ -284,12 +302,14 @@ export function getSegments (maxSegs) { const legacySegs = readSegments('_psegs').map(Number).filter(seg => seg >= 1000000).map(String) const _ppam = readSegments('_ppam') const _pcrprs = readSegments('_pcrprs') + const _pssps = readPermutiveSspData() const segments = { ac: [..._pcrprs, ..._ppam, ...legacySegs], rubicon: readSegments('_prubicons'), appnexus: readSegments('_papns'), gam: readSegments('_pdfps'), + ssp: _pssps, } for (const bidder in segments) { @@ -313,6 +333,19 @@ function readSegments (key) { } } +/** + * Gets the permutive ssp data string and parses it into an object + * or returns an empty object + * @return {{ ssp?: string[], cohorts?: string[] }} + */ +function readPermutiveSspData() { + try { + return permutiveSspParser(storage.getDataFromLocalStorage('_pssps') || '') + } catch (e) { + return {} + } +} + const unknownIabSegmentId = '_unknown_' /** @@ -341,6 +374,21 @@ function iabSegmentId(permutiveSegmentId, iabIds) { return iabIds[permutiveSegmentId] || unknownIabSegmentId } +/** + * Passes Permutive's standard cohorts to Appnexus/Xandr via auction level keywords + * @param {Object} sspData -- {ssp?: string[], cohorts?: string[]} + */ +function passCohortsToAppnexusAuctionKeywords(sspData) { + const shouldSetConfig = Object.hasOwnProperty('ssp') && Object.hasOwnProperty('cohorts') && sspData.ssp.includes('appnexus') + if(shouldSetConfig) { + config.setConfig({ + appnexusAuctionKeywords: { + 'p_standard': sspData.cohorts + } + }) + } +} + /** @type {RtdSubmodule} */ export const permutiveSubmodule = { name: MODULE_NAME, From 2624db63112806262d5ee6e0916c891f421b50bc Mon Sep 17 00:00:00 2001 From: Zeeshan Rasool Date: Wed, 9 Nov 2022 12:32:20 +0000 Subject: [PATCH 07/17] simplify shouldSetConfig condition --- modules/permutiveRtdProvider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 3cbe5a5c3ff..290b0dfd2bd 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -175,7 +175,7 @@ function setSegments (reqBidsConfigObj, moduleConfig, segmentData) { return } - passCohortsToAppnexusAuctionKeywords(segmentData?.ssp ?? {}) + passCohortsToAppnexusAuctionKeywords(segmentData.ssp) adUnits.forEach(adUnit => { adUnit.bids.forEach(bid => { @@ -379,7 +379,7 @@ function iabSegmentId(permutiveSegmentId, iabIds) { * @param {Object} sspData -- {ssp?: string[], cohorts?: string[]} */ function passCohortsToAppnexusAuctionKeywords(sspData) { - const shouldSetConfig = Object.hasOwnProperty('ssp') && Object.hasOwnProperty('cohorts') && sspData.ssp.includes('appnexus') + const shouldSetConfig = sspData?.ssp?.includes('appnexus') && sspData?.cohorts if(shouldSetConfig) { config.setConfig({ appnexusAuctionKeywords: { From 3870d14b32930e6a46f2acf523269ae6cfb6260b Mon Sep 17 00:00:00 2001 From: Paulius Imbrasas Date: Wed, 9 Nov 2022 17:52:14 +0000 Subject: [PATCH 08/17] Write SSP cohorts into p_standard targeting (+ bug fixes) --- modules/permutiveRtdProvider.js | 30 +++++++++++++------ .../spec/modules/permutiveRtdProvider_spec.js | 4 ++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 290b0dfd2bd..f02b1571c55 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -10,7 +10,7 @@ import {submodule} from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safeJSONParse} from '../src/utils.js'; import {includes} from '../src/polyfill.js'; -import {config} from '../src/config' +import {config} from '../src/config.js'; const MODULE_NAME = 'permutive' @@ -225,10 +225,19 @@ function getCustomBidderFn (moduleConfig, bidder) { * @return {Object} Bidder function */ function getDefaultBidderFn (bidder) { + const isPStandardTargetingEnabled = (data, acEnabled) => { + return (acEnabled && data.ac && data.ac.length) || (data.ssp && data.ssp.length) + } + const pStandardTargeting = (data, acEnabled) => { + const ac = (acEnabled) ? (data.ac ?? []) : [] + const ssp = data.ssp ?? [] + return [...new Set([...ac, ...ssp])] + } const bidderMap = { appnexus: function (bid, data, acEnabled) { - if (acEnabled && data.ac && data.ac.length) { - deepSetValue(bid, 'params.keywords.p_standard', data.ac) + if (isPStandardTargetingEnabled(data, acEnabled)) { + const segments = pStandardTargeting(data, acEnabled) + deepSetValue(bid, 'params.keywords.p_standard', segments) } if (data.appnexus && data.appnexus.length) { deepSetValue(bid, 'params.keywords.permutive', data.appnexus) @@ -237,8 +246,9 @@ function getDefaultBidderFn (bidder) { return bid }, rubicon: function (bid, data, acEnabled) { - if (acEnabled && data.ac && data.ac.length) { - deepSetValue(bid, 'params.visitor.p_standard', data.ac) + if (isPStandardTargetingEnabled(data, acEnabled)) { + const segments = pStandardTargeting(data, acEnabled) + deepSetValue(bid, 'params.visitor.p_standard', segments) } if (data.rubicon && data.rubicon.length) { deepSetValue(bid, 'params.visitor.permutive', data.rubicon) @@ -247,8 +257,9 @@ function getDefaultBidderFn (bidder) { return bid }, ozone: function (bid, data, acEnabled) { - if (acEnabled && data.ac && data.ac.length) { - deepSetValue(bid, 'params.customData.0.targeting.p_standard', data.ac) + if (isPStandardTargetingEnabled(data, acEnabled)) { + const segments = pStandardTargeting(data, acEnabled) + deepSetValue(bid, 'params.customData.0.targeting.p_standard', segments) } return bid @@ -309,13 +320,14 @@ export function getSegments (maxSegs) { rubicon: readSegments('_prubicons'), appnexus: readSegments('_papns'), gam: readSegments('_pdfps'), - ssp: _pssps, } for (const bidder in segments) { segments[bidder] = segments[bidder].slice(0, maxSegs) } + segments.ssp = _pssps + return segments } @@ -380,7 +392,7 @@ function iabSegmentId(permutiveSegmentId, iabIds) { */ function passCohortsToAppnexusAuctionKeywords(sspData) { const shouldSetConfig = sspData?.ssp?.includes('appnexus') && sspData?.cohorts - if(shouldSetConfig) { + if (shouldSetConfig) { config.setConfig({ appnexusAuctionKeywords: { 'p_standard': sspData.cohorts diff --git a/test/spec/modules/permutiveRtdProvider_spec.js b/test/spec/modules/permutiveRtdProvider_spec.js index 3f104ee1e2e..03e0db3790d 100644 --- a/test/spec/modules/permutiveRtdProvider_spec.js +++ b/test/spec/modules/permutiveRtdProvider_spec.js @@ -555,6 +555,7 @@ function transformedTargeting (data = getTargetingData()) { appnexus: data._papns, rubicon: data._prubicons, gam: data._pdfps, + ssp: data._pssps, } } @@ -565,7 +566,8 @@ function getTargetingData () { _papns: ['appnexus1', 'appnexus2'], _psegs: ['1234', '1000001', '1000002'], _ppam: ['ppam1', 'ppam2'], - _pcrprs: ['pcrprs1', 'pcrprs2'] + _pcrprs: ['pcrprs1', 'pcrprs2', 'dup'], + _pssps: ['xyz', 'abc', 'dup'], } } From 4bf276c39c846a92fb77fb7aba44dcd2e728dbc0 Mon Sep 17 00:00:00 2001 From: Zeeshan Rasool Date: Fri, 11 Nov 2022 14:42:00 +0000 Subject: [PATCH 09/17] fix tests --- modules/permutiveRtdProvider.js | 51 +++++-------------- .../spec/modules/permutiveRtdProvider_spec.js | 18 ++++--- 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index f02b1571c55..9fc4915b654 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -226,12 +226,12 @@ function getCustomBidderFn (moduleConfig, bidder) { */ function getDefaultBidderFn (bidder) { const isPStandardTargetingEnabled = (data, acEnabled) => { - return (acEnabled && data.ac && data.ac.length) || (data.ssp && data.ssp.length) + return (acEnabled && data.ac && data.ac.length) || (data.ssp && data.ssp.cohorts.length) } const pStandardTargeting = (data, acEnabled) => { const ac = (acEnabled) ? (data.ac ?? []) : [] - const ssp = data.ssp ?? [] - return [...new Set([...ac, ...ssp])] + const ssp = (data.ssp && data.ssp.cohorts) ? data.ssp : { ssps: [], cohorts: [] } + return [...new Set([...ac, ...ssp.cohorts])] } const bidderMap = { appnexus: function (bid, data, acEnabled) { @@ -251,7 +251,7 @@ function getDefaultBidderFn (bidder) { deepSetValue(bid, 'params.visitor.p_standard', segments) } if (data.rubicon && data.rubicon.length) { - deepSetValue(bid, 'params.visitor.permutive', data.rubicon) + deepSetValue(bid, 'params.visitor.permutive', data.rubicon.map(String)) } return bid @@ -288,22 +288,6 @@ export function isPermutiveOnPage () { return typeof window.permutive !== 'undefined' && typeof window.permutive.ready === 'function' } -/** - * Parses the _pssps string in LS into a JS object - * @param {string} sspKVString -- in form of "stuff,stuff;somemorestuff,1234" - * @example For a given _pssps string in the form of "appnexus,pubmatic;1234,5678,abcd" this function will return { ssps: [appnexus, pubmatic], cohorts: [1234, 5678, "abcd"] } - */ -function permutiveSspParser(sspKVString) { - if (!sspKVString) return {} - - const intermediateTuple = sspKVString.split(';') - - return { - ssps: intermediateTuple[0].split(','), - cohorts: intermediateTuple[1].split(',') - } -} - /** * Get all relevant segment IDs in an object * @param {number} maxSegs - Maximum number of segments to be included @@ -313,21 +297,25 @@ export function getSegments (maxSegs) { const legacySegs = readSegments('_psegs').map(Number).filter(seg => seg >= 1000000).map(String) const _ppam = readSegments('_ppam') const _pcrprs = readSegments('_pcrprs') - const _pssps = readPermutiveSspData() const segments = { ac: [..._pcrprs, ..._ppam, ...legacySegs], rubicon: readSegments('_prubicons'), appnexus: readSegments('_papns'), gam: readSegments('_pdfps'), + ssp: readSegments('_pssps'), } for (const bidder in segments) { - segments[bidder] = segments[bidder].slice(0, maxSegs) + if (bidder === 'ssp') { + if (segments[bidder].cohorts && Array.isArray(segments[bidder].cohorts)) { + segments[bidder].cohorts = segments[bidder].cohorts.slice(0, maxSegs) + } + } else { + segments[bidder] = segments[bidder].slice(0, maxSegs) + } } - segments.ssp = _pssps - return segments } @@ -345,19 +333,6 @@ function readSegments (key) { } } -/** - * Gets the permutive ssp data string and parses it into an object - * or returns an empty object - * @return {{ ssp?: string[], cohorts?: string[] }} - */ -function readPermutiveSspData() { - try { - return permutiveSspParser(storage.getDataFromLocalStorage('_pssps') || '') - } catch (e) { - return {} - } -} - const unknownIabSegmentId = '_unknown_' /** @@ -391,7 +366,7 @@ function iabSegmentId(permutiveSegmentId, iabIds) { * @param {Object} sspData -- {ssp?: string[], cohorts?: string[]} */ function passCohortsToAppnexusAuctionKeywords(sspData) { - const shouldSetConfig = sspData?.ssp?.includes('appnexus') && sspData?.cohorts + const shouldSetConfig = sspData?.ssps?.includes('appnexus') && sspData?.cohorts if (shouldSetConfig) { config.setConfig({ appnexusAuctionKeywords: { diff --git a/test/spec/modules/permutiveRtdProvider_spec.js b/test/spec/modules/permutiveRtdProvider_spec.js index 03e0db3790d..4208fb7f7ea 100644 --- a/test/spec/modules/permutiveRtdProvider_spec.js +++ b/test/spec/modules/permutiveRtdProvider_spec.js @@ -291,7 +291,11 @@ describe('permutiveRtdProvider', function () { const segments = getSegments(max) for (const key in segments) { - expect(segments[key]).to.have.length(max) + if (key === 'ssp') { + expect(segments[key].cohorts).to.have.length(max) + } else { + expect(segments[key]).to.have.length(max) + } } }) }) @@ -311,7 +315,7 @@ describe('permutiveRtdProvider', function () { if (bidder === 'appnexus') { expect(deepAccess(params, 'keywords.permutive')).to.eql(data.appnexus) - expect(deepAccess(params, 'keywords.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'keywords.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -332,7 +336,7 @@ describe('permutiveRtdProvider', function () { if (bidder === 'rubicon') { expect(deepAccess(params, 'visitor.permutive')).to.eql(data.rubicon) - expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -363,7 +367,7 @@ describe('permutiveRtdProvider', function () { deepAccess(params, 'visitor.permutive'), 'Should map all targeting values to a string', ).to.eql(data.rubicon.map(String)) - expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -383,7 +387,7 @@ describe('permutiveRtdProvider', function () { const { bidder, params } = bid if (bidder === 'ozone') { - expect(deepAccess(params, 'customData.0.targeting.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'customData.0.targeting.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -417,7 +421,7 @@ describe('permutiveRtdProvider', function () { if (bidder === 'rubicon') { expect(deepAccess(params, 'visitor.permutive')).to.eql(data.gam) - expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac) + expect(deepAccess(params, 'visitor.p_standard')).to.eql(data.ac.concat(data.ssp.cohorts)) } }) }) @@ -567,7 +571,7 @@ function getTargetingData () { _psegs: ['1234', '1000001', '1000002'], _ppam: ['ppam1', 'ppam2'], _pcrprs: ['pcrprs1', 'pcrprs2', 'dup'], - _pssps: ['xyz', 'abc', 'dup'], + _pssps: { ssps: ['xyz', 'abc', 'dup'], cohorts: ['123', 'abc'] } } } From 31275029eed034ce23310e1c99cf8559409676d6 Mon Sep 17 00:00:00 2001 From: Paulius Imbrasas Date: Fri, 11 Nov 2022 14:50:18 +0000 Subject: [PATCH 10/17] Simplify --- modules/permutiveRtdProvider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 9fc4915b654..5911d599fff 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -230,8 +230,8 @@ function getDefaultBidderFn (bidder) { } const pStandardTargeting = (data, acEnabled) => { const ac = (acEnabled) ? (data.ac ?? []) : [] - const ssp = (data.ssp && data.ssp.cohorts) ? data.ssp : { ssps: [], cohorts: [] } - return [...new Set([...ac, ...ssp.cohorts])] + const ssp = data?.ssp?.cohorts ?? [] + return [...new Set([...ac, ...ssp])] } const bidderMap = { appnexus: function (bid, data, acEnabled) { From 6221511823c7b2ad76061072c138fe07383b165b Mon Sep 17 00:00:00 2001 From: Zeeshan Rasool Date: Wed, 16 Nov 2022 18:25:04 +0000 Subject: [PATCH 11/17] use new key for auction kw cohorts --- modules/permutiveRtdProvider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 5911d599fff..3bb95e68580 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -370,7 +370,7 @@ function passCohortsToAppnexusAuctionKeywords(sspData) { if (shouldSetConfig) { config.setConfig({ appnexusAuctionKeywords: { - 'p_standard': sspData.cohorts + 'p_standard_aud': sspData.cohorts } }) } From b1a4564502515065470a1d32997819f26a3ea52e Mon Sep 17 00:00:00 2001 From: Paulius Imbrasas Date: Fri, 18 Nov 2022 10:48:16 +0000 Subject: [PATCH 12/17] Push SSP cohorts to SSPs via ORTB2 --- modules/permutiveRtdProvider.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 3bb95e68580..29d9055d08a 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -119,9 +119,21 @@ export function setBidderRtb (bidderOrtb2, customModuleConfig) { const transformationConfigs = deepAccess(moduleConfig, 'params.transformations') || [] const segmentData = getSegments(maxSegs) - acBidders.forEach(function (bidder) { + const ssps = segmentData?.ssp?.ssps ?? [] + const sspCohorts = segmentData?.ssp?.cohorts ?? [] + + const bidders = new Set([...acBidders, ...ssps]) + bidders.forEach(function (bidder) { const currConfig = { ortb2: bidderOrtb2[bidder] || {} } - const nextConfig = updateOrtbConfig(currConfig, segmentData.ac, transformationConfigs) // ORTB2 uses the `ac` segment IDs + + const isAcBidder = acBidders.indexOf(bidder) > -1 + const isSspBidder = ssps.indexOf(bidder) > -1 + + let cohorts = [] + if (isAcBidder) cohorts = segmentData.ac + if (isSspBidder) cohorts = [...new Set([...cohorts, sspCohorts])].slice(0, maxSegs) + + const nextConfig = updateOrtbConfig(currConfig, cohorts, transformationConfigs) // ORTB2 uses the `ac` segment IDs bidderOrtb2[bidder] = nextConfig.ortb2; }) } From 7108de1ec9988c6c84cc26e339050d6b75fde23b Mon Sep 17 00:00:00 2001 From: Paulius Imbrasas Date: Fri, 18 Nov 2022 13:26:38 +0000 Subject: [PATCH 13/17] Add tests and fix bugs --- modules/permutiveRtdProvider.js | 2 +- .../spec/modules/permutiveRtdProvider_spec.js | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 29d9055d08a..77f516b5f60 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -131,7 +131,7 @@ export function setBidderRtb (bidderOrtb2, customModuleConfig) { let cohorts = [] if (isAcBidder) cohorts = segmentData.ac - if (isSspBidder) cohorts = [...new Set([...cohorts, sspCohorts])].slice(0, maxSegs) + if (isSspBidder) cohorts = [...new Set([...cohorts, ...sspCohorts])].slice(0, maxSegs) const nextConfig = updateOrtbConfig(currConfig, cohorts, transformationConfigs) // ORTB2 uses the `ac` segment IDs bidderOrtb2[bidder] = nextConfig.ortb2; diff --git a/test/spec/modules/permutiveRtdProvider_spec.js b/test/spec/modules/permutiveRtdProvider_spec.js index 4208fb7f7ea..f087f9ca81e 100644 --- a/test/spec/modules/permutiveRtdProvider_spec.js +++ b/test/spec/modules/permutiveRtdProvider_spec.js @@ -279,6 +279,43 @@ describe('permutiveRtdProvider', function () { expect(bidderConfig[bidder].user.data).to.deep.include.members([sampleOrtbConfig.user.data[0]]) }) }) + it('should merge ortb2 correctly for ac and ssps', function () { + setLocalStorage({ + '_ppam': [], + '_psegs': [], + '_pcrprs': ['abc', 'def', 'xyz'], + '_pssps': { + ssps: ['foo', 'bar'], + cohorts: ['xyz', 'uvw'], + } + }) + const moduleConfig = { + name: 'permutive', + waitForIt: true, + params: { + acBidders: ['foo'], + maxSegs: 30 + } + } + const bidderConfig = {}; + const acBidders = moduleConfig.params.acBidders + const expectedTargetingData = [ + { id: 'abc' }, + { id: 'def' }, + { id: 'xyz' }, + { id: 'uvw' }, + ] + + setBidderRtb(bidderConfig, moduleConfig) + + const bidders = [...new Set([...acBidders, 'foo', 'bar'])] + bidders.forEach(bidder => { + expect(bidderConfig[bidder].user.data).to.deep.include.members([{ + name: 'permutive.com', + segment: expectedTargetingData + }], `bidder is ${bidder}`) + }) + }) }) describe('Getting segments', function () { From f6d9b4ebd2fa77ff493157bdf80ae723c38f05be Mon Sep 17 00:00:00 2001 From: Paulius Imbrasas Date: Fri, 18 Nov 2022 15:30:32 +0000 Subject: [PATCH 14/17] Update tests --- modules/permutiveRtdProvider.js | 2 +- .../spec/modules/permutiveRtdProvider_spec.js | 40 ++++++++++++++----- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 77f516b5f60..9fdf48f9b35 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -133,7 +133,7 @@ export function setBidderRtb (bidderOrtb2, customModuleConfig) { if (isAcBidder) cohorts = segmentData.ac if (isSspBidder) cohorts = [...new Set([...cohorts, ...sspCohorts])].slice(0, maxSegs) - const nextConfig = updateOrtbConfig(currConfig, cohorts, transformationConfigs) // ORTB2 uses the `ac` segment IDs + const nextConfig = updateOrtbConfig(currConfig, cohorts, transformationConfigs) bidderOrtb2[bidder] = nextConfig.ortb2; }) } diff --git a/test/spec/modules/permutiveRtdProvider_spec.js b/test/spec/modules/permutiveRtdProvider_spec.js index f087f9ca81e..ea435297db4 100644 --- a/test/spec/modules/permutiveRtdProvider_spec.js +++ b/test/spec/modules/permutiveRtdProvider_spec.js @@ -293,28 +293,46 @@ describe('permutiveRtdProvider', function () { name: 'permutive', waitForIt: true, params: { - acBidders: ['foo'], + acBidders: ['foo', 'other'], maxSegs: 30 } } const bidderConfig = {}; - const acBidders = moduleConfig.params.acBidders - const expectedTargetingData = [ + + setBidderRtb(bidderConfig, moduleConfig) + + // include both ac and ssp cohorts, as foo is both in ac bidders and ssps + const expectedFooTargetingData = [ { id: 'abc' }, { id: 'def' }, { id: 'xyz' }, { id: 'uvw' }, ] + expect(bidderConfig['foo'].user.data).to.deep.include.members([{ + name: 'permutive.com', + segment: expectedFooTargetingData + }]) - setBidderRtb(bidderConfig, moduleConfig) + // don't include ac targeting as it's not in ac bidders + const expectedBarTargetingData = [ + { id: 'xyz' }, + { id: 'uvw' }, + ] + expect(bidderConfig['bar'].user.data).to.deep.include.members([{ + name: 'permutive.com', + segment: expectedBarTargetingData + }]) - const bidders = [...new Set([...acBidders, 'foo', 'bar'])] - bidders.forEach(bidder => { - expect(bidderConfig[bidder].user.data).to.deep.include.members([{ - name: 'permutive.com', - segment: expectedTargetingData - }], `bidder is ${bidder}`) - }) + // only include ac targeting as this ssp is not in ssps list + const expectedOtherTargetingData = [ + { id: 'abc' }, + { id: 'def' }, + { id: 'xyz' }, + ] + expect(bidderConfig['other'].user.data).to.deep.include.members([{ + name: 'permutive.com', + segment: expectedOtherTargetingData + }]) }) }) From e36c43dca5a85de7bd9bb137379297dcf4e6f4c1 Mon Sep 17 00:00:00 2001 From: Zeeshan Rasool Date: Fri, 18 Nov 2022 17:04:26 +0000 Subject: [PATCH 15/17] update example with _pssps --- integrationExamples/gpt/permutiveRtdProvider_example.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integrationExamples/gpt/permutiveRtdProvider_example.html b/integrationExamples/gpt/permutiveRtdProvider_example.html index 118cc678726..554f2081c6d 100644 --- a/integrationExamples/gpt/permutiveRtdProvider_example.html +++ b/integrationExamples/gpt/permutiveRtdProvider_example.html @@ -15,7 +15,8 @@ _papns: ['appnexus1', 'appnexus2'], _psegs: ['1234', '1000001', '1000002'], _ppam: ['ppam1', 'ppam2'], - _pcrprs: ['pcrprs1', 'pcrprs2'] + _pcrprs: ['pcrprs1', 'pcrprs2'], + _pssps: { ssps: ['appnexus', 'some other'], cohorts: ['abcd', 'efgh', 'ijkl'] }, } for (let key in data) { From 891bb68721e0a6e8a00ff0a1d0a9b4d7687d52a9 Mon Sep 17 00:00:00 2001 From: Paulius Imbrasas Date: Fri, 2 Dec 2022 11:02:54 +0000 Subject: [PATCH 16/17] Remove custom `appnexusAuctionKeywords` and use user.keywords in ortb2 config --- modules/permutiveRtdProvider.js | 30 +++++---------- .../spec/modules/permutiveRtdProvider_spec.js | 38 ++++++++++++++++++- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 9fdf48f9b35..929d8bd0611 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -10,11 +10,11 @@ import {submodule} from '../src/hook.js'; import {getStorageManager} from '../src/storageManager.js'; import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safeJSONParse} from '../src/utils.js'; import {includes} from '../src/polyfill.js'; -import {config} from '../src/config.js'; const MODULE_NAME = 'permutive' export const PERMUTIVE_SUBMODULE_CONFIG_KEY = 'permutive-prebid-rtd' +export const PERMUTIVE_STANDARD_AUD_KEYWORD = 'p_standard_aud' export const storage = getStorageManager({gvlid: null, moduleName: MODULE_NAME}) @@ -133,7 +133,7 @@ export function setBidderRtb (bidderOrtb2, customModuleConfig) { if (isAcBidder) cohorts = segmentData.ac if (isSspBidder) cohorts = [...new Set([...cohorts, ...sspCohorts])].slice(0, maxSegs) - const nextConfig = updateOrtbConfig(currConfig, cohorts, transformationConfigs) + const nextConfig = updateOrtbConfig(currConfig, cohorts, sspCohorts, transformationConfigs) bidderOrtb2[bidder] = nextConfig.ortb2; }) } @@ -144,9 +144,10 @@ export function setBidderRtb (bidderOrtb2, customModuleConfig) { * @param {Object[]} transformationConfigs - array of objects with `id` and `config` properties, used to determine * the transformations on user data to include the ORTB2 object * @param {string[]} segmentIDs - Permutive segment IDs + * @param {string[]} sspSegmentIDs - Permutive SSP segment IDs * @return {Object} Merged ortb2 object */ -function updateOrtbConfig (currConfig, segmentIDs, transformationConfigs) { +function updateOrtbConfig (currConfig, segmentIDs, sspSegmentIDs, transformationConfigs) { const name = 'permutive.com' const permutiveUserData = { @@ -167,6 +168,12 @@ function updateOrtbConfig (currConfig, segmentIDs, transformationConfigs) { deepSetValue(ortbConfig, 'ortb2.user.data', updatedUserData) + // As of writing this, only used for AppNexus/Xandr in place of appnexusAuctionKeywords in config + const currentUserKeywords = deepAccess(ortbConfig, 'ortb2.user.keywords') || "" + const keywords = sspSegmentIDs.map(segment => `${PERMUTIVE_STANDARD_AUD_KEYWORD}=${segment}`).join(',') + const updatedUserKeywords = (currentUserKeywords === '') ? keywords : `${currentUserKeywords},${keywords}` + deepSetValue(ortbConfig, 'ortb2.user.keywords', updatedUserKeywords) + return ortbConfig } @@ -187,8 +194,6 @@ function setSegments (reqBidsConfigObj, moduleConfig, segmentData) { return } - passCohortsToAppnexusAuctionKeywords(segmentData.ssp) - adUnits.forEach(adUnit => { adUnit.bids.forEach(bid => { let { bidder } = bid @@ -373,21 +378,6 @@ function iabSegmentId(permutiveSegmentId, iabIds) { return iabIds[permutiveSegmentId] || unknownIabSegmentId } -/** - * Passes Permutive's standard cohorts to Appnexus/Xandr via auction level keywords - * @param {Object} sspData -- {ssp?: string[], cohorts?: string[]} - */ -function passCohortsToAppnexusAuctionKeywords(sspData) { - const shouldSetConfig = sspData?.ssps?.includes('appnexus') && sspData?.cohorts - if (shouldSetConfig) { - config.setConfig({ - appnexusAuctionKeywords: { - 'p_standard_aud': sspData.cohorts - } - }) - } -} - /** @type {RtdSubmodule} */ export const permutiveSubmodule = { name: MODULE_NAME, diff --git a/test/spec/modules/permutiveRtdProvider_spec.js b/test/spec/modules/permutiveRtdProvider_spec.js index ea435297db4..5030e662ea9 100644 --- a/test/spec/modules/permutiveRtdProvider_spec.js +++ b/test/spec/modules/permutiveRtdProvider_spec.js @@ -242,6 +242,42 @@ describe('permutiveRtdProvider', function () { }) }) it('should not overwrite ortb2 config', function () { + const moduleConfig = getConfig() + const acBidders = moduleConfig.params.acBidders + const sampleOrtbConfig = { + site: { + name: 'example' + }, + user: { + data: [ + { + name: 'www.dataprovider1.com', + ext: { taxonomyname: 'iab_audience_taxonomy' }, + segment: [{ id: '687' }, { id: '123' }] + } + ] + } + } + + const bidderConfig = Object.fromEntries(acBidders.map(bidder => [bidder, sampleOrtbConfig])) + + const transformedUserData = { + name: 'transformation', + ext: { test: true }, + segment: [1, 2, 3] + } + + setBidderRtb(bidderConfig, moduleConfig, { + // TODO: this argument is unused, is the test still valid / needed? + testTransformation: userData => transformedUserData + }) + + acBidders.forEach(bidder => { + expect(bidderConfig[bidder].site.name).to.equal(sampleOrtbConfig.site.name) + expect(bidderConfig[bidder].user.data).to.deep.include.members([sampleOrtbConfig.user.data[0]]) + }) + }) + it('should update user.keywords and not override existing values', function () { const moduleConfig = getConfig() const acBidders = moduleConfig.params.acBidders const sampleOrtbConfig = { @@ -275,8 +311,8 @@ describe('permutiveRtdProvider', function () { acBidders.forEach(bidder => { expect(bidderConfig[bidder].site.name).to.equal(sampleOrtbConfig.site.name) - expect(bidderConfig[bidder].user.keywords).to.equal(sampleOrtbConfig.user.keywords) expect(bidderConfig[bidder].user.data).to.deep.include.members([sampleOrtbConfig.user.data[0]]) + expect(bidderConfig[bidder].user.keywords).to.deep.equal('a,b,p_standard_aud=123,p_standard_aud=abc') }) }) it('should merge ortb2 correctly for ac and ssps', function () { From adb1de93c519a5e183ea4ae228e63ee70511c8de Mon Sep 17 00:00:00 2001 From: Paulius Imbrasas Date: Tue, 6 Dec 2022 14:15:16 +0000 Subject: [PATCH 17/17] Fix linting issues --- modules/permutiveRtdProvider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/permutiveRtdProvider.js b/modules/permutiveRtdProvider.js index 929d8bd0611..d62834cfcfc 100644 --- a/modules/permutiveRtdProvider.js +++ b/modules/permutiveRtdProvider.js @@ -169,9 +169,9 @@ function updateOrtbConfig (currConfig, segmentIDs, sspSegmentIDs, transformation deepSetValue(ortbConfig, 'ortb2.user.data', updatedUserData) // As of writing this, only used for AppNexus/Xandr in place of appnexusAuctionKeywords in config - const currentUserKeywords = deepAccess(ortbConfig, 'ortb2.user.keywords') || "" + const currentUserKeywords = deepAccess(ortbConfig, 'ortb2.user.keywords') || '' const keywords = sspSegmentIDs.map(segment => `${PERMUTIVE_STANDARD_AUD_KEYWORD}=${segment}`).join(',') - const updatedUserKeywords = (currentUserKeywords === '') ? keywords : `${currentUserKeywords},${keywords}` + const updatedUserKeywords = (currentUserKeywords === '') ? keywords : `${currentUserKeywords},${keywords}` deepSetValue(ortbConfig, 'ortb2.user.keywords', updatedUserKeywords) return ortbConfig