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

Prebid Core: Add readConfig functionality to clone the config instead of referencing it #7237

Merged
merged 9 commits into from
Sep 8, 2021
23 changes: 22 additions & 1 deletion src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { isValidPriceConfig } from './cpmBucketManager.js';
import find from 'core-js-pure/features/array/find.js';
import includes from 'core-js-pure/features/array/includes.js';
import Set from 'core-js-pure/features/set';
import { mergeDeep } from './utils.js';
import { mergeDeep, deepClone } from './utils.js';

const from = require('core-js-pure/features/array/from.js');
const utils = require('./utils.js');
Expand Down Expand Up @@ -315,6 +315,26 @@ export function newConfig() {
return Object.assign({}, config);
}

/*
* Returns the configuration object if called without parameters,
* or single configuration property if given a string matching a configuration
* property name. Allows deep access e.g. getConfig('currency.adServerCurrency')
*
* If called with callback parameter, or a string and a callback parameter,
* subscribes to configuration updates. See `subscribe` function for usage.
*
* The object returned is a deepClone of the `config` property.
*/
function readConfig(...args) {
if (args.length <= 1 && typeof args[0] !== 'function') {
const option = args[0];
const configClone = deepClone(_getConfig());
return option ? utils.deepAccess(configClone, option) : configClone;
}

return subscribe(...args);
}

/*
* Returns configuration object if called without parameters,
* or single configuration property if given a string matching a configuration
Expand Down Expand Up @@ -629,6 +649,7 @@ export function newConfig() {
getCurrentBidder,
resetBidder,
getConfig,
readConfig,
setConfig,
setDefaults,
resetConfig,
Expand Down
1 change: 1 addition & 0 deletions src/prebid.js
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@ $$PREBID_GLOBAL$$.markWinningBidAsUsed = function (markBidRequest) {
* @alias module:pbjs.getConfig
*/
$$PREBID_GLOBAL$$.getConfig = config.getConfig;
$$PREBID_GLOBAL$$.readConfig = config.readConfig;

/**
* Set Prebid config options.
Expand Down
63 changes: 63 additions & 0 deletions test/spec/config_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const utils = require('src/utils');

let getConfig;
let setConfig;
let readConfig;
let getBidderConfig;
let setBidderConfig;
let setDefaults;
Expand All @@ -17,6 +18,7 @@ describe('config API', function () {
const config = newConfig();
getConfig = config.getConfig;
setConfig = config.setConfig;
readConfig = config.readConfig;
getBidderConfig = config.getBidderConfig;
setBidderConfig = config.setBidderConfig;
setDefaults = config.setDefaults;
Expand All @@ -37,6 +39,67 @@ describe('config API', function () {
expect(getConfig()).to.be.a('object');
});

it('readConfig returns deepCopy of the internal config object', function () {
setConfig({ foo: {biz: 'bar'} });
const config1 = readConfig('foo');
config1.biz = 'buz';
const config2 = readConfig('foo');
expect(readConfig()).to.be.a('object');
expect(config1.biz).to.not.equal(config2.biz);
});

it('readConfig retrieves arbitrary configuration properties', function () {
setConfig({ baz: 'qux' });
expect(readConfig('baz')).to.equal('qux');
});

it('readConfig has subscribe functionality for adding listeners to config updates', function () {
const listener = sinon.spy();

readConfig(listener);

setConfig({ foo: 'bar' });

sinon.assert.calledOnce(listener);
sinon.assert.calledWith(listener, { foo: 'bar' });
});

it('readConfig subscribers can unsubscribe', function () {
const listener = sinon.spy();

const unsubscribe = getConfig(listener);

unsubscribe();

readConfig({ logging: true });

sinon.assert.notCalled(listener);
});

it('readConfig subscribers can subscribe to topics', function () {
const listener = sinon.spy();

readConfig('logging', listener);

setConfig({ logging: true, foo: 'bar' });

sinon.assert.calledOnce(listener);
sinon.assert.calledWithExactly(listener, { logging: true });
});

it('readConfig topic subscribers are only called when that topic is changed', function () {
const listener = sinon.spy();
const wildcard = sinon.spy();

readConfig('subject', listener);
readConfig(wildcard);

setConfig({ foo: 'bar' });

sinon.assert.notCalled(listener);
sinon.assert.calledOnce(wildcard);
});

it('sets and gets arbitrary configuration properties', function () {
setConfig({ baz: 'qux' });
expect(getConfig('baz')).to.equal('qux');
Expand Down