-
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
Data Controller Module: initial release #8484
Conversation
pbjs.setConfig({ | ||
dataController: { | ||
filterEIDwhenSDA: ['*'] | ||
filterSADwhenEID: ['id5-sync.com'] |
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.
typo
| param name | type | Scope | Description | Params | | ||
| :------------ | :------------ | :------ | :------ | :------ | | ||
| filterEIDwhenSDA | function | optional | Filters user EIDs based on SDA | bidrequest | | ||
| filterSADwhenEID | function | optional | Filters SDA based on configured EIDs | bidrequest | |
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.
typo
if(filterEIDs) { | ||
config.setConfig({'dcUsersAsEids': filterEIDs}); | ||
} | ||
} else if (dataControllerConfig.filterSADwhenEID) { |
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.
typo
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.
@patmmccann do we want to have the param as "supressSDAwhenEID"/"supressEIDwhenSDA"? Filter would be more generic rather than suppress.
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.
All references to SAD should be changed to SDA
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.
updated.
Thanks so much! |
SAD is a typo for SDA
…On Thu, May 26, 2022, 5:16 PM SKOCHERI ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In modules/dataControllerModule/index.js
<#8484 (comment)>:
> +
+ if (!dataControllerConfig) {
+ processBidderRequests.getHooks({hook: processBidderRequestHook}).remove();
+ return;
+ }
+
+ if (dataControllerConfig.filterEIDwhenSDA && dataControllerConfig.filterSADwhenEID) {
+ processBidderRequests.getHooks({hook: processBidderRequestHook}).remove();
+ return;
+ }
+ if (dataControllerConfig.filterEIDwhenSDA) {
+ let filterEIDs = submodule.filterEIDs(bidRequest)
+ if(filterEIDs) {
+ config.setConfig({'dcUsersAsEids': filterEIDs});
+ }
+ } else if (dataControllerConfig.filterSADwhenEID) {
@patmmccann <https://github.com/patmmccann> do we want to have the param
as "supressSDAwhenEID"/"supressEIDwhenSDA"? Filter would be more generic
rather than suppress.
—
Reply to this email directly, view it on GitHub
<#8484 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAM25Z4JHYEGUA743Q4C45TVL7S3PANCNFSM5XCKAN3A>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
import {processBidderRequests} from '../../src/adapters/bidderFactory.js'; | ||
import {logError} from '../../src/utils.js'; | ||
|
||
let submodules = []; |
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.
why is this planning for submodules? I'm not sure I understand the scope of the feature - going by this proposal it seems self-contained to me.
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.
@dgirardi The proposal mentions about Publisher having ability to define the rules to filter EID and SDA hence providing submodule.
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 understand - publishers do not have access to the module system and they wouldn't be able to add a submodule (they would have to fork the repo).
I would expect them to define the rules with setConfig
, and I don't see the need multiple submodules to read that config unless it makes sense to want only part of that proposal - but I don't see anything in it that suggests that.
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.
Will take a look
dataControllerConfig = config.getConfig(MODULE_NAME); | ||
|
||
if (!dataControllerConfig) { | ||
processBidderRequests.getHooks({hook: processBidderRequestHook}).remove(); |
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'd prefer to keep configuration and business logic separate - this will get confusing fast as complexity increases. (IMO it makes more sense to turn the module off from the same place you turn it on).
src/adapters/bidderFactory.js
Outdated
@@ -296,6 +296,12 @@ export function newBidder(spec) { | |||
* @param onCompletion {function()} invoked once when all bid requests have been processed | |||
*/ | |||
export const processBidderRequests = hook('sync', function (spec, bids, bidderRequest, ajax, wrapCallback, {onRequest, onResponse, onError, onBid, onCompletion}) { | |||
let updatedUserAsEids = config.getConfig('dcUsersAsEids'); |
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 looks like it's using config
as a global variable, plus it's adding dead code for those who are not using the data controller module.
Why not do this directly from the hook added by the module?
Also, IMO the makeBidRequests
hook is a better place for this - I don't know if it makes a difference now, but my instinct is that the earlier they have this data the better.
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.
@dgirardi I have update this module to be self contained.
The SDA were not available if this module was called with makeBidRequests as the *RTBProviders are invoked until then
let dataControllerConfig; | ||
const ALL = '*'; | ||
|
||
function filterEIDs(requestObject) { |
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 still a work in progress I assume - this logic looks like it belongs in the module.
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 module development is completed. The test contains how the submodule looks like
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.
the test is testing test code? that can't be right. I'm missing the point entirely, once the bundle is built this code (and therefore the tests on it) are completely irrelevant no? the module in the actual codebase is an empty shell.
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.
Thanks for the update. I think the approach would not work correctly (sorry if I missed it in the previous review) - for two reasons:
processBidderRequest
does not run for Prebid Server bids - only client adapters;- since prebid 7,
setBidderConfig
(orsetConfig
) is not the right way to update first party data - it will not take effect until the next auction (see this)
I think the best way to do this is to hook on startAuction
and:
- perform
filterSDA
by modifying theortb2Fragments
parameter; - perform
filterEIDs
by modifying theadUnits[].bids[].userIdAsEid
parameter - in the same way that property is set by the userId module
const MODULE_NAME = 'dataController'; | ||
let dataControllerConfig; | ||
|
||
const _logInfo = createLogInfo(LOG_PRE_FIX); |
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.
there is a prefixLog
utility function that does something similar already.
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.
@dgirardi Have updated the module to be called before startAuction and fixed other issues pointed out.
} | ||
} | ||
|
||
export function initHook() { |
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 the better pattern is config.getConfig(MODULE_NAME, (cfg) => {if (isValid(cfg) enable() else disable())})
- it's the approach taken by most (if not all) other modules:
- it keeps better separation between setup logic and business logic
- it doesn't use cpu cycles during the auction in the case when the module has not been enabled
- it needs to worry about fewer edge cases, like what happens if configuration is set during (or after) the auction
} | ||
|
||
function hasValidConfiguration() { | ||
dataControllerConfig = config.getConfig(MODULE_NAME); |
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.
may I suggest something like setupConfig
instead of hasValidConfiguration
? to make it clear that the function has side effects.
|
||
function filterSDA(adUnits, ortb2Fragments) { | ||
let bidderEIDSMap = getEIDsSource(adUnits); | ||
for (const [key, value] of Object.entries(ortb2Fragments.bidder)) { |
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.
- should this also remove
user.data
from global configuration, if it's not set by bidder? I'm not sure what the intent of the feature is.
If as the pub I do
setConfig({
ortb2: {
user: {
data: [... stuff here ..]
}
}
})
and assuming there's nothing else, here ortb2Fragments
would come in as:
{
global: {user: {data: [.. stuff ..]}},
bidder: {}
}
so this logic would have no effect, and downstream in the adapters the final ortb2
object would still contain user.data. Is that correct?
value.user.data = []
should bedeepSetValue(value, 'user.data', [])
, otherwise it can throw if any of the intermediates is missing. (Or should it bedelete value?.user?.data
- should it be empty, or missing?)
export function getSegmentConfig(ortb2Fragments) { | ||
let bidderSDAMap = new Map(); | ||
|
||
for (const [key, value] of Object.entries(ortb2Fragments.bidder)) { |
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.
Same here - should this look at global config?
Reading through https://docs.google.com/document/d/1g9GxuMtPwZyk5lRKCyoWVaYJGpqUGEbFNwEBMQswcjU/edit# again, I think the intent is - from the point of view of the publisher, "I don't want this data to be visible to the adapters / SSP". So I think this module should be overzealous, and:
|
Updating to consider the above |
@dgirardi I am trying to create request with user.ext.eids to test. Is this the right path where user eids are set "ortb2Fragments.bidder..user.ext.eids" |
they could be in either |
@dgirardi Updated to consider global and bidder segments and update eids as mentioned above |
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 - I think this conforms to the proposal now as I understand it. I have a few more minor suggestions, I think the first (changing from GLOBAL = 'global'
to GLOBAL = {}
) should be done for correctness, but the rest is not that important.
const LOG_PRE_FIX = 'Data_Controller : '; | ||
const ALL = '*'; | ||
const MODULE_NAME = 'dataController'; | ||
const GLOBAL = 'global'; |
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.
You can use an empty object as a unique Map key (const GLOBAL = {}
), it has the advantage that it cannot clash with, for example, a custom alias bidder named 'global'.
return false; | ||
} | ||
let containsEIDs = false; | ||
_dataControllerConfig.filterSDAwhenEID.forEach(source => { |
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 could be rewritten as return _dataControllerConfig.filterSDAwhenEID.some((source) => bidderEids.has(source))
.
.forEach
always loops over the whole iterable; .some
will stop as soon as the predicate returns true.
if (bidderSegement == undefined) { | ||
return false; | ||
} | ||
_dataControllerConfig.filterEIDwhenSDA.forEach(segment => { |
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.
same, .some
would be more succinct and more efficient here.
} | ||
|
||
function getSegmentConfig(ortb2Fragments) { | ||
let bidderSDAMap = new Map(); |
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.
there's some duplication in here, which you could avoid by reusing the same logic for both global and bidder data. Here's a possible refactor to illustrate what I mean:
let bidderSDAMap = new Map();
function collectSegments(key, data) {
let segmentSet = constructSegment(deepAccess(data, 'user.data') || []);
if (segmentSet && segmentSet.size > 0) bidderSDAMap.set(key, segmentSet);
}
collectSegments(GLOBAL, ortb2Fragments.global);
Object.entries(ortb2Fragments.bidder).forEach(([bidder, data]) => collectSegments(bidder, data));
return bidderSDAMap;
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.
Fixed
# Description | ||
|
||
This module will filter EIDs and SDA based on the configurations. | ||
The filtered EIDs are stored in 'dcUsersAsEids' configuration and filtered SDA are updated in bidder configuration. |
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.
Is this detail no longer relevant? I am not seeing any of this stored any longer, so if that is the case, can this be removed
return; | ||
} | ||
confListener(); // unsubscribe config listener | ||
_dataControllerConfig = dataControllerConfig.dataController; |
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 can clean this up a little to store the dataController in a const then reference that instead or referencing the full path each time.
i.e.
const dataController = dataControllerConfig && dataControllerConfig.dataController;
if (!dataController) {
...
}
if (dataController.filterEIDwhenSDA && dataController. filterSDAwhenEID) {
...
}
confListener(); // unsubscribe config listener
_dataControllerConfig = dataController;
getHook('startAuction').before(filterBidData);
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.
Updated
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.
Thanks for the update! lgtm
* Data Controller Module * Data Controller Module * Correcting typo * Setting hook based on data controller configuration * Setting hook based on data controller configuration * Updating to filter data before startAuction event * Updating to filter data before startAuction event * Review comment fixes * Review comment fixes Co-authored-by: skocheri <[email protected]>
@SKOCHERI Thanks for your work. I would like to ask you some questions.
Thank you for any helpful information. |
Docs PR: |
Does a bidAdapter have to change the code, in order to support this module? |
* Data Controller Module * Data Controller Module * Correcting typo * Setting hook based on data controller configuration * Setting hook based on data controller configuration * Updating to filter data before startAuction event * Updating to filter data before startAuction event * Review comment fixes * Review comment fixes Co-authored-by: skocheri <[email protected]>
* Data Controller Module * Data Controller Module * Correcting typo * Setting hook based on data controller configuration * Setting hook based on data controller configuration * Updating to filter data before startAuction event * Updating to filter data before startAuction event * Review comment fixes * Review comment fixes Co-authored-by: skocheri <[email protected]>
Type of change
Description of change
Be sure to test the integration with your adserver using the Hello World sample page.
For any changes that affect user-facing APIs or example code documented on http://prebid.org, please provide:
Other information