-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Add setConfig method #1325
Add setConfig method #1325
Conversation
6369692
to
7f7a470
Compare
Just to a quick sanity check... As written, no prebid files will be able to read from the config "on load." Any Did you guys already consider & accept that limitation? |
@dbemiller |
in that case... I need to challenge this.
can always be replaced by:
The latter is less error-prone:
Easier to test:
...and works more reliably if publishers want to run multiple auctions with different configs. It's also possible to maintain backwards compatibility (in the short term). For example, we could start by implementing:
Then we can rewrite the Are there any real advantages to setting the state beforehand? |
Noting the discussion in standup in writing. There is one benefit of
is a better user experience than:
I talked a bit more with @mkendall07 afterwards, and would like to modify the suggestion a bit. Suppose we do:
Then the state has only been passed once, and we still maintain all the benefits I mentioned above. |
in the var configured = pbjs.withConfig(cfg);
configured.doOneThing();
configured.doAnotherThing();
configured.doAThirdThing(); pattern, what is |
Good question... a separate instance. In my view, The only two reasons I know of why adapters use the global today are:
(1) isn't supported in Prebid 1.0 anyway. If there are other reasons, though, please do let me know. A more concrete example of core would look something like this:
|
We paused dev on this but will pick it up soon. I'd like to propose we continue with the existing API for the following reasons:
|
92dcc5b
to
e53a2de
Compare
src/config.js
Outdated
utils.logError('setConfig options must be an object'); | ||
} | ||
|
||
if (options.bidderTimeout) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make a convention that setConfig(obj)
just extends extends the config
object? like Object.assign(config, options)
rather than individual assignment? Seems like the individual assignment could get verbose with a lot of options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, was meaning to squash these, will use Object.assign
, thanks
src/config.js
Outdated
const DEFAULT_DEBUG = false; | ||
const DEFAULT_BIDDER_TIMEOUT = 3000; | ||
|
||
export let config = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need some way to subscribe to changes to this object. For instance, the currency module (in review still) will need to kick off a request to go and download the currency file as soon as its configuration is set. But without some sort of subscription model, it won't know when that is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added subscribe
function, please review if this works for your use case
b71e1d7
to
a190c30
Compare
src/config.js
Outdated
* subscribe((config) => console.log('config set:', config)); | ||
* subscribe(({ debug }) => debug ? console.log('debug set:', debug) : ''); | ||
*/ | ||
export function subscribe(listener) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think listing to a label would be a slight improvement. Say I'm the currency module then I subscribe with
subscribe("currency", function() { ... });
and then I'm only alerted when the currency configuration is updated, and I'm only passed the relevant currency configuration.
// "currency" subscribe handler called
pbjs.setConfig({
currency: {
...
}
});
// "currency" subscribe handler not called
pbjs.setConfig({
bidderTimeout: 3000
})
That way if pbjs.setConfig
is used multiple times to update different configs we clean up a lot of the noise of alerting every listener.
However, if the first argument to subscribe
is the callback function (i.e. no label is specified) we could still have it behave as it does now where it gets called for any configuration change and sends the whole config.
src/prebid.js
Outdated
@@ -745,6 +748,14 @@ $$PREBID_GLOBAL$$.setS2SConfig = function(options) { | |||
adaptermanager.setS2SConfig(config); | |||
}; | |||
|
|||
/** | |||
* Set Prebid config options | |||
* @param {object} options |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is gonna need a more specific JSDoc to be of much use to anyone. Remember we're trying to work towards #1408.
Check out the docs for an example of typedef and optional properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't quite parse how best to doc this, will try in future
src/prebid.js
Outdated
* Set Prebid config options | ||
* @param {object} options | ||
*/ | ||
$$PREBID_GLOBAL$$.setConfig = function(options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this can just be $$PREBID_GLOBAL$$.setConfig = setConfig
?
@@ -246,7 +246,9 @@ exports.enableAnalytics = function (config) { | |||
}; | |||
|
|||
exports.setBidderSequence = function (order) { | |||
_bidderSequence = order; | |||
if (order === CONSTANTS.ORDER.RANDOM) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to change this behavior? It looks a little suspiciously out of place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like it was moved from https://github.com/prebid/Prebid.js/pull/1325/files#diff-f8e049a1b6d4f9aa96fca41d2f7aa11dL697
I don't know why it did that check, but it doesn't look like this pull-request changes any functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, intention was to consolidate the setBidderSequence
behavior into adaptermanager rather than having it spread across two functions. Noticed this while looking at this function when adding the bidderSequence
config. Open to dropping this change if it's too out of scope
@@ -645,8 +647,9 @@ $$PREBID_GLOBAL$$.setPriceGranularity = function (granularity) { | |||
} | |||
}; | |||
|
|||
/** @deprecated - use pbjs.setConfig({ enableSendAllBids: <true|false> }) */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would recommend calling this sendAllBids
instead, for better parallelism with other config options.
This name reads like "enable the send all bids feature"... which is less straightforward than "send all the bids"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current name comes from https://gist.github.com/mkendall07/51ee5f6b9f2df01a89162cf6de7fe5b6#setconfig---new-api, ccing @mkendall07 for feedback
src/config.js
Outdated
|
||
const ALL_TOPICS = '*'; | ||
|
||
let listeners = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chatted about this a little bit offline... For the sake of testability, concurrent auctions, and infinite scroll, I think we should strongly avoid adding global, mutable state to the project. Especially in new modules.
I suggested an alternate pattern, which @matthewlane sounded to like too. I'm pretty sure it'll require a getConfig
on $$PREBID_GLOBAL$$
, which kinda sucks... but I think it's a worthwhile tradeoff.
I can describe it here if anyone else cares or wants to hear it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you describe what you're thinking? I think what we have here is pretty good.
I don't really consider this global state as its encapsulated to the config module; however, it is "side-effecty", but I'm not sure that's a terrible thing. However, it does need a way to clean up its side-effects for testing purposes, i.e. we need some way to remove listeners.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this file:
export function newConfig() {
let listeners = [];
return {
getConfig() { ... }
setConfig(options) { ... }
subscribe(topic, listener) { ... }
};
}
In prebid.js
:
import { newConfig } from './config';
const config = newConfig();
$$PREBID_GLOBAL$$.getConfig = config.getConfig
$$PREBID_GLOBAL$$.setConfig = config.setConfig
In modules:
const getConfig = $$PREBID_GLOBAL$$.getConfig
Unit tests don't need to worry about side-effects because they can call newConfig()
each time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that what we have here is workable. Honestly, the question which is really in the back of my mind is "how do we get to an API like @protonate suggested, without it being such a massive project?" Something like:
prebid.push(function() {
prebid.doAuction().then(renderAd);
}
As written, this config module can only be used in a project which is driven by global state. That's fine... because the current project is driven by global state.
Using my suggestion, this config module could be used in both systems.
function doAuction() {
const config = newConfig();
...auction impl here...
}
If we keep adding new modules which rely on global state, then certain features (like infinite scroll) become more and more difficult to support with time.
If we add modules which don't rely on global state, then we can reuse them out of the box if(/when) we migrate in that direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this would really gain us anything. You would have to move the let config
into the newConfig
closure as well, right? So then we still have a "global" state for Prebid.js, it's just inside of a closure now making it harder to get at. Also, this means getConfig
, setConfig
, etc would need to be updated to pass in the closure reference to that state so they could interact with it.
import { newConfig } from './config';
const config = newConfig();
$$PREBID_GLOBAL$$.getConfig = config.getConfig
$$PREBID_GLOBAL$$.setConfig = config.setConfig
Wouldn't each of the tests have to do this as well in order to reset global Prebid.js state? I think i'd be a little more straight forward to have a resetConfig()
that clears any config state, in this case config
and listeners
.
src/config.js
Outdated
* // subscribe to only 'logging' changes | ||
* subscribe('logging', (config) => console.log('logging set:', config)); | ||
*/ | ||
export function subscribe(topic, listener) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how this subscribe
is implemented now. 👍
However, I think it might be nice to merge its functionality with getConfig
for a few reasons. For one, I think that getConfig
is a more descriptive name, especially if we export this on the public API (which I think we should). Also, it really is fulfilling the same purpose as getConfig
; it's just implemented as a push rather than a pull.
I propose we merge subscribe
into getConfig
. if getConfig
is called with no arguments or string, behave as getConfig
does now, if it's called with a function or a string and a function, then it behaves as subscribe
behaves now.
421b3b5
to
dd2849b
Compare
b0ae9dd
to
de4831d
Compare
de4831d
to
9cbeeb3
Compare
* Get Prebid config options | ||
* @param {object} options | ||
*/ | ||
$$PREBID_GLOBAL$$.getConfig = config.getConfig; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't need to be on the global API, does it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was the one who suggested this. I think it's a small thing but makes Prebid.js more extensible by third-party libraries. So overall I think it's a small net-gain for a one-liner.
|
||
/** | ||
* Set Prebid config options | ||
* @param {object} options |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More specific JSDocs would be super helpful here.
I understand we should probably merge this PR to unblock people, though... so can we make it a priority shortly after? It improves the developer experience a ton.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
* Add setConfig API method * Use getter/setter methods in config module to manage configuration * Use getter/setter on object props to manage legacy api access * Convert bidderTimeout * Add and use getConfig * Convert publisherDomain * Convert cookieSyncDelay * priceGranularity calls setPriceGranularity * Convert enableSendAllBids * s2sConfig calls setS2SConfig * bidderSequence calls setBidderSequence * Add subscribe function * Enable subscribing to specific configuration changes * Simplify function exposure * Add missing pbjs * Merge subscribe into getConfig and return unsubscribe function * Expose getConfig on global * Reset config in tests * Refactor
Type of change
Description of change
Adds
pbjs.setConfig
andpbjs.getConfig
api methods for working with Prebid configuration.Usage:
pbjs.setConfig({ debug: <true|false> });
to enable/disable logging (this will replacepbjs.logging = <true|false>
, which still works for now but will be depreciated).pbjs.setConfig({ <key>: <value> });
for setting arbitrary configurationThe
getConfig
function is for retrieving the current configuration object or subscribing to configuration updates. When called with no parameters, the entire config object is returned. When called with a string parameter, a single configuration property matching that parameter is returned.config.getConfig()
=> get config objectconfig.getConfig('debug')
=> getdebug
configThe
getConfig
function also contains a 'subscribe' ability that adds a callback function to a set of listeners that are invoked wheneversetConfig
is called. The subscribed function will be passed the options object that was used in thesetConfig
call. Individual topics can be subscribed to by passing a string as the first parameter and a callback function as the secondOther information
Implements #1247