Skip to content

Commit 7c2cc39

Browse files
AntonioGargarojorgeluisrocha
authored andcommitted
Permutive RTD Module: migrate magnite to ortb2 (prebid#9555)
* feat(permutiveRtd): migrate rubicon targeting to ortb2 * perf(permutiveRtd): prevent redundant cohort reads and updates * fix(permutiveRtd): enable debugger logs for ortb2 updates * fix(permutiveRtd): provide identity bidder fn fallback * test(permutiveRtd): update params to follow refactor * fix(permutiveRtd): prevent multiple targeting updates once in realtime * fix(permutiveRtd): require `waitForIt` and permutive to be false to complete immediately * fix(permutiveRtd): remove bidder specific logic
1 parent dbf86d5 commit 7c2cc39

File tree

2 files changed

+247
-216
lines changed

2 files changed

+247
-216
lines changed

modules/permutiveRtdProvider.js

Lines changed: 86 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
import {getGlobal} from '../src/prebidGlobal.js';
99
import {submodule} from '../src/hook.js';
1010
import {getStorageManager} from '../src/storageManager.js';
11-
import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safeJSONParse} from '../src/utils.js';
11+
import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safeJSONParse, prefixLog} from '../src/utils.js';
1212
import {includes} from '../src/polyfill.js';
1313

1414
const MODULE_NAME = 'permutive'
1515

16+
const logger = prefixLog('[PermutiveRTD]')
17+
1618
export const PERMUTIVE_SUBMODULE_CONFIG_KEY = 'permutive-prebid-rtd'
19+
export const PERMUTIVE_STANDARD_KEYWORD = 'p_standard'
20+
export const PERMUTIVE_CUSTOM_COHORTS_KEYWORD = 'permutive'
1721
export const PERMUTIVE_STANDARD_AUD_KEYWORD = 'p_standard_aud'
1822

1923
export const storage = getStorageManager({gvlid: null, moduleName: MODULE_NAME})
@@ -24,30 +28,6 @@ function init(moduleConfig, userConsent) {
2428
return true
2529
}
2630

27-
/**
28-
* Set segment targeting from cache and then try to wait for Permutive
29-
* to initialise to get realtime segment targeting
30-
* @param {Object} reqBidsConfigObj
31-
* @param {function} callback - Called when submodule is done
32-
* @param {customModuleConfig} reqBidsConfigObj - Publisher config for module
33-
*/
34-
export function initSegments (reqBidsConfigObj, callback, customModuleConfig) {
35-
const permutiveOnPage = isPermutiveOnPage()
36-
const moduleConfig = getModuleConfig(customModuleConfig)
37-
const segmentData = getSegments(moduleConfig.params.maxSegs)
38-
39-
setSegments(reqBidsConfigObj, moduleConfig, segmentData)
40-
41-
if (moduleConfig.waitForIt && permutiveOnPage) {
42-
window.permutive.ready(function () {
43-
setSegments(reqBidsConfigObj, moduleConfig, segmentData)
44-
callback()
45-
}, 'realtime')
46-
} else {
47-
callback()
48-
}
49-
}
50-
5131
function liftIntoParams(params) {
5232
return isPlainObject(params) ? { params } : {}
5333
}
@@ -109,15 +89,13 @@ export function getModuleConfig(customModuleConfig) {
10989

11090
/**
11191
* Sets ortb2 config for ac bidders
112-
* @param {Object} bidderOrtb2
92+
* @param {Object} bidderOrtb2 - The ortb2 object for the all bidders
11393
* @param {Object} customModuleConfig - Publisher config for module
11494
*/
115-
export function setBidderRtb (bidderOrtb2, customModuleConfig) {
116-
const moduleConfig = getModuleConfig(customModuleConfig)
95+
export function setBidderRtb (bidderOrtb2, moduleConfig, segmentData) {
11796
const acBidders = deepAccess(moduleConfig, 'params.acBidders')
11897
const maxSegs = deepAccess(moduleConfig, 'params.maxSegs')
11998
const transformationConfigs = deepAccess(moduleConfig, 'params.transformations') || []
120-
const segmentData = getSegments(maxSegs)
12199

122100
const ssps = segmentData?.ssp?.ssps ?? []
123101
const sspCohorts = segmentData?.ssp?.cohorts ?? []
@@ -126,28 +104,37 @@ export function setBidderRtb (bidderOrtb2, customModuleConfig) {
126104
bidders.forEach(function (bidder) {
127105
const currConfig = { ortb2: bidderOrtb2[bidder] || {} }
128106

107+
let cohorts = []
108+
129109
const isAcBidder = acBidders.indexOf(bidder) > -1
130-
const isSspBidder = ssps.indexOf(bidder) > -1
110+
if (isAcBidder) {
111+
cohorts = segmentData.ac
112+
}
131113

132-
let cohorts = []
133-
if (isAcBidder) cohorts = segmentData.ac
134-
if (isSspBidder) cohorts = [...new Set([...cohorts, ...sspCohorts])].slice(0, maxSegs)
114+
const isSspBidder = ssps.indexOf(bidder) > -1
115+
if (isSspBidder) {
116+
cohorts = [...new Set([...cohorts, ...sspCohorts])].slice(0, maxSegs)
117+
}
135118

136-
const nextConfig = updateOrtbConfig(currConfig, cohorts, sspCohorts, transformationConfigs)
137-
bidderOrtb2[bidder] = nextConfig.ortb2;
119+
const nextConfig = updateOrtbConfig(bidder, currConfig, cohorts, sspCohorts, transformationConfigs, segmentData)
120+
bidderOrtb2[bidder] = nextConfig.ortb2
138121
})
139122
}
140123

141124
/**
142125
* Updates `user.data` object in existing bidder config with Permutive segments
126+
* @param string bidder - The bidder
143127
* @param {Object} currConfig - Current bidder config
144128
* @param {Object[]} transformationConfigs - array of objects with `id` and `config` properties, used to determine
145129
* the transformations on user data to include the ORTB2 object
146130
* @param {string[]} segmentIDs - Permutive segment IDs
147131
* @param {string[]} sspSegmentIDs - Permutive SSP segment IDs
132+
* @param {Object} segmentData - The segments available for targeting
148133
* @return {Object} Merged ortb2 object
149134
*/
150-
function updateOrtbConfig (currConfig, segmentIDs, sspSegmentIDs, transformationConfigs) {
135+
function updateOrtbConfig(bidder, currConfig, segmentIDs, sspSegmentIDs, transformationConfigs, segmentData) {
136+
const customCohortsData = deepAccess(segmentData, bidder) || []
137+
151138
const name = 'permutive.com'
152139

153140
const permutiveUserData = {
@@ -174,6 +161,19 @@ function updateOrtbConfig (currConfig, segmentIDs, sspSegmentIDs, transformation
174161
const updatedUserKeywords = (currentUserKeywords === '') ? keywords : `${currentUserKeywords},${keywords}`
175162
deepSetValue(ortbConfig, 'ortb2.user.keywords', updatedUserKeywords)
176163

164+
// Set user extensions
165+
if (segmentIDs.length > 0) {
166+
deepSetValue(ortbConfig, `ortb2.user.ext.data.${PERMUTIVE_STANDARD_KEYWORD}`, segmentIDs)
167+
logger.logInfo(`Extending ortb2.user.ext.data with "${PERMUTIVE_STANDARD_KEYWORD}"`, segmentIDs)
168+
}
169+
170+
if (customCohortsData.length > 0) {
171+
deepSetValue(ortbConfig, `ortb2.user.ext.data.${PERMUTIVE_CUSTOM_COHORTS_KEYWORD}`, customCohortsData.map(String))
172+
logger.logInfo(`Extending ortb2.user.ext.data with "${PERMUTIVE_CUSTOM_COHORTS_KEYWORD}"`, customCohortsData)
173+
}
174+
175+
logger.logInfo(`Updating ortb2 config for ${bidder}`, ortbConfig)
176+
177177
return ortbConfig
178178
}
179179

@@ -262,17 +262,6 @@ function getDefaultBidderFn (bidder) {
262262

263263
return bid
264264
},
265-
rubicon: function (bid, data, acEnabled) {
266-
if (isPStandardTargetingEnabled(data, acEnabled)) {
267-
const segments = pStandardTargeting(data, acEnabled)
268-
deepSetValue(bid, 'params.visitor.p_standard', segments)
269-
}
270-
if (data.rubicon && data.rubicon.length) {
271-
deepSetValue(bid, 'params.visitor.permutive', data.rubicon.map(String))
272-
}
273-
274-
return bid
275-
},
276265
ozone: function (bid, data, acEnabled) {
277266
if (isPStandardTargetingEnabled(data, acEnabled)) {
278267
const segments = pStandardTargeting(data, acEnabled)
@@ -283,7 +272,12 @@ function getDefaultBidderFn (bidder) {
283272
}
284273
}
285274

286-
return bidderMap[bidder]
275+
// On no default bidder just return the same bid as passed in
276+
function bidIdentity(bid) {
277+
return bid
278+
}
279+
280+
return bidderMap[bidder] || bidIdentity
287281
}
288282

289283
/**
@@ -383,17 +377,55 @@ function iabSegmentId(permutiveSegmentId, iabIds) {
383377
return iabIds[permutiveSegmentId] || unknownIabSegmentId
384378
}
385379

380+
/**
381+
* Pull the latest configuration and cohort information and update accordingly.
382+
*
383+
* @param reqBidsConfigObj - Bidder provided config for request
384+
* @param customModuleConfig - Publisher provide config
385+
*/
386+
export function readAndSetCohorts(reqBidsConfigObj, moduleConfig) {
387+
const segmentData = getSegments(deepAccess(moduleConfig, 'params.maxSegs'))
388+
389+
makeSafe(function () {
390+
// Legacy route with custom parameters
391+
// ACK policy violation, in process of removing
392+
setSegments(reqBidsConfigObj, moduleConfig, segmentData)
393+
});
394+
395+
makeSafe(function () {
396+
// Route for bidders supporting ORTB2
397+
setBidderRtb(reqBidsConfigObj.ortb2Fragments?.bidder, moduleConfig, segmentData)
398+
})
399+
}
400+
401+
let permutiveSDKInRealTime = false
402+
386403
/** @type {RtdSubmodule} */
387404
export const permutiveSubmodule = {
388405
name: MODULE_NAME,
389406
getBidRequestData: function (reqBidsConfigObj, callback, customModuleConfig) {
407+
const completeBidRequestData = () => {
408+
logger.logInfo(`Request data updated`)
409+
callback()
410+
}
411+
412+
const moduleConfig = getModuleConfig(customModuleConfig)
413+
414+
readAndSetCohorts(reqBidsConfigObj, moduleConfig)
415+
390416
makeSafe(function () {
391-
// Legacy route with custom parameters
392-
initSegments(reqBidsConfigObj, callback, customModuleConfig)
393-
});
394-
makeSafe(function () {
395-
// Route for bidders supporting ORTB2
396-
setBidderRtb(reqBidsConfigObj.ortb2Fragments?.bidder, customModuleConfig)
417+
if (permutiveSDKInRealTime || !(moduleConfig.waitForIt && isPermutiveOnPage())) {
418+
return completeBidRequestData()
419+
}
420+
421+
window.permutive.ready(function () {
422+
logger.logInfo(`SDK is realtime, updating cohorts`)
423+
permutiveSDKInRealTime = true
424+
readAndSetCohorts(reqBidsConfigObj, getModuleConfig(customModuleConfig))
425+
completeBidRequestData()
426+
}, 'realtime')
427+
428+
logger.logInfo(`Registered cohort update when SDK is realtime`)
397429
})
398430
},
399431
init: init

0 commit comments

Comments
 (0)