Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permutive RTD Module: merge external submodule params #8881

Merged
merged 2 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 55 additions & 8 deletions modules/permutiveRtdProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@
import {getGlobal} from '../src/prebidGlobal.js';
import {submodule} from '../src/hook.js';
import {getStorageManager} from '../src/storageManager.js';
import {deepAccess, deepSetValue, isFn, logError, mergeDeep} from '../src/utils.js';
import {deepAccess, deepSetValue, isFn, logError, mergeDeep, isPlainObject, safeJSONParse} from '../src/utils.js';
import {includes} from '../src/polyfill.js';

const MODULE_NAME = 'permutive'

export const PERMUTIVE_SUBMODULE_CONFIG_KEY = 'permutive-prebid-rtd'

export const storage = getStorageManager({gvlid: null, moduleName: MODULE_NAME})

function init (moduleConfig, userConsent) {
function init(moduleConfig, userConsent) {
readPermutiveModuleConfigFromCache()

return true
}

Expand Down Expand Up @@ -43,20 +47,63 @@ export function initSegments (reqBidsConfigObj, callback, customModuleConfig) {
}
}

function liftIntoParams(params) {
return isPlainObject(params) ? { params } : {}
}

let cachedPermutiveModuleConfig = {}

/**
* Merges segments into existing bidder config
* Access the submodules RTD params that are cached to LocalStorage by the Permutive SDK. This lets the RTD submodule
* apply publisher defined params set in the Permutive platform, so they may still be applied if the Permutive SDK has
* not initialised before this submodule is initialised.
*/
function readPermutiveModuleConfigFromCache() {
const params = safeJSONParse(storage.getDataFromLocalStorage(PERMUTIVE_SUBMODULE_CONFIG_KEY))
return cachedPermutiveModuleConfig = liftIntoParams(params)
}

/**
* Access the submodules RTD params attached to the Permutive SDK.
*
* @return The Permutive config available by the Permutive SDK or null if the operation errors.
*/
function getParamsFromPermutive() {
try {
return liftIntoParams(window.permutive.addons.prebid.getPermutiveRtdConfig())
} catch (e) {
return null
}
}

/**
* Merges segments into existing bidder config in reverse priority order. The highest priority is 1.
*
* 1. customModuleConfig <- set by publisher with pbjs.setConfig
* 2. permutiveRtdConfig <- set by the publisher using the Permutive platform
* 3. defaultConfig
*
* As items with a higher priority will be deeply merged into the previous config, deep merges are performed by
* reversing the priority order.
*
* @param {Object} customModuleConfig - Publisher config for module
* @return {Object} Merged defatul and custom config
* @return {Object} Deep merges of the default, Permutive and custom config.
*/
function getModuleConfig (customModuleConfig) {
export function getModuleConfig(customModuleConfig) {
// Use the params from Permutive if available, otherwise fallback to the cached value set by Permutive.
const permutiveModuleConfig = getParamsFromPermutive() || cachedPermutiveModuleConfig

return mergeDeep({
waitForIt: false,
params: {
maxSegs: 500,
acBidders: [],
overwrites: {}
}
}, customModuleConfig)
overwrites: {},
},
},
permutiveModuleConfig,
customModuleConfig,
)
}

/**
Expand Down
154 changes: 152 additions & 2 deletions test/spec/modules/permutiveRtdProvider_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
initSegments,
isAcEnabled,
isPermutiveOnPage,
setBidderRtb
setBidderRtb,
getModuleConfig,
PERMUTIVE_SUBMODULE_CONFIG_KEY,
} from 'modules/permutiveRtdProvider.js'
import { deepAccess } from '../../../src/utils.js'
import { deepAccess, deepSetValue, mergeDeep } from '../../../src/utils.js'
import { config } from 'src/config.js'

describe('permutiveRtdProvider', function () {
Expand All @@ -29,6 +31,154 @@ describe('permutiveRtdProvider', function () {
})
})

describe('getModuleConfig', function () {
beforeEach(function () {
// Reads data from the cache
permutiveSubmodule.init()
})

const liftToParams = (params) => ({ params })

const getDefaultConfig = () => ({
waitForIt: false,
params: {
maxSegs: 500,
acBidders: [],
overwrites: {},
},
})

const storeConfigInCacheAndInit = (data) => {
const dataToStore = { [PERMUTIVE_SUBMODULE_CONFIG_KEY]: data }
setLocalStorage(dataToStore)
// Reads data from the cache
permutiveSubmodule.init()

// Cleanup
return () => removeLocalStorage(dataToStore)
}

const setWindowPermutivePrebid = (getPermutiveRtdConfig) => {
// Read from Permutive
const backup = window.permutive

deepSetValue(window, 'permutive.addons.prebid', {
getPermutiveRtdConfig,
})

// Cleanup
return () => window.permutive = backup
}

it('should return default values', function () {
const config = getModuleConfig({})
expect(config).to.deep.equal(getDefaultConfig())
})

it('should override deeply on custom config', function () {
const defaultConfig = getDefaultConfig()

const customModuleConfig = { waitForIt: true, params: { acBidders: ['123'] } }
const config = getModuleConfig(customModuleConfig)

expect(config).to.deep.equal(mergeDeep(defaultConfig, customModuleConfig))
})

it('should override deeply on cached config', function () {
const defaultConfig = getDefaultConfig()

const cachedParamsConfig = { acBidders: ['123'] }
const cleanupCache = storeConfigInCacheAndInit(cachedParamsConfig)

const config = getModuleConfig({})

expect(config).to.deep.equal(mergeDeep(defaultConfig, liftToParams(cachedParamsConfig)))

// Cleanup
cleanupCache()
})

it('should override deeply on Permutive Rtd config', function () {
const defaultConfig = getDefaultConfig()

const permutiveRtdConfigParams = { acBidders: ['123'], overwrites: { '123': true } }
const cleanupPermutive = setWindowPermutivePrebid(function () {
return permutiveRtdConfigParams
})

const config = getModuleConfig({})

expect(config).to.deep.equal(mergeDeep(defaultConfig, liftToParams(permutiveRtdConfigParams)))

// Cleanup
cleanupPermutive()
})

it('should NOT use cached Permutive Rtd config if window.permutive is available', function () {
const defaultConfig = getDefaultConfig()

// As Permutive is available on the window object, this value won't be used.
const cachedParamsConfig = { acBidders: ['123'] }
const cleanupCache = storeConfigInCacheAndInit(cachedParamsConfig)

const permutiveRtdConfigParams = { acBidders: ['456'], overwrites: { '123': true } }
const cleanupPermutive = setWindowPermutivePrebid(function () {
return permutiveRtdConfigParams
})

const config = getModuleConfig({})

expect(config).to.deep.equal(mergeDeep(defaultConfig, liftToParams(permutiveRtdConfigParams)))

// Cleanup
cleanupCache()
cleanupPermutive()
})

it('should handle calling Permutive method throwing error', function () {
const defaultConfig = getDefaultConfig()

const cleanupPermutive = setWindowPermutivePrebid(function () {
throw new Error()
})

const config = getModuleConfig({})

expect(config).to.deep.equal(defaultConfig)

// Cleanup
cleanupPermutive()
})

it('should override deeply in priority order', function () {
const defaultConfig = getDefaultConfig()

// As Permutive is available on the window object, this value won't be used.
const cachedConfig = { acBidders: ['123'] }
const cleanupCache = storeConfigInCacheAndInit(cachedConfig)

// Read from Permutive
const permutiveRtdConfig = { acBidders: ['456'] }
const cleanupPermutive = setWindowPermutivePrebid(function () {
return permutiveRtdConfig
})

const customModuleConfig = { params: { acBidders: ['789'], maxSegs: 499 } }
const config = getModuleConfig(customModuleConfig)

// The configs are in reverse priority order as configs are merged left to right. So the priority is,
// 1. customModuleConfig <- set by publisher with pbjs.setConfig
// 2. permutiveRtdConfig <- set by the publisher using Permutive.
// 3. defaultConfig
const configMergedInPriorityOrder = mergeDeep(defaultConfig, liftToParams(permutiveRtdConfig), customModuleConfig)
expect(config).to.deep.equal(configMergedInPriorityOrder)

// Cleanup
cleanupCache()
cleanupPermutive()
})
})

describe('ortb2 config', function () {
beforeEach(function () {
config.resetConfig()
Expand Down