Conversation
…re of consent data
modules/consentManagement.js
Outdated
| import { setTimeout } from 'core-js/library/web/timers'; | ||
|
|
||
| // add new CMPs here | ||
| const availCMPs = ['iab']; |
There was a problem hiding this comment.
we should probably call this appnexus and not iab
There was a problem hiding this comment.
updated references of iab to appnexus
modules/consentManagement.js
Outdated
| export let lookUpFailureChoice; | ||
|
|
||
| export function requestBidsHook(config, fn) { | ||
| let adUnits = config.adUnits || $$PREBID_GLOBAL$$.adUnits; |
There was a problem hiding this comment.
should we be using $$PREBID_GLOBAL$$ here?
There was a problem hiding this comment.
I removed the $$PREBID_GLOBAL$$ fall back. I found this type of setup from another module that was hooked on the requestBids function and wanted to keep consistency.
There was a problem hiding this comment.
update I need to have this value in place to actually grab the adUnits list object.
The config attribute that's included in the requestBids has a place for adUnits, but it's dependent on it being passed specifically in the prebid config when the requestBids is invoked. Currently only the bidsBackHandler is passed along with requestBids in most of our example docs, so I expect the adUnits is going to be undefined at this stage of the auction process.
@mkendall07 Please let me know if there are any strong concerns about relying on $$PREBID_GLOBAL$$.adUnits.
modules/consentManagement.js
Outdated
| export let consentTimeout; | ||
| export let lookUpFailureChoice; | ||
|
|
||
| export function requestBidsHook(config, fn) { |
There was a problem hiding this comment.
would like to see JSdocs notation on exported functions.
There was a problem hiding this comment.
Added some more descriptive information to the exported functions.
modules/consentManagement.js
Outdated
| let args = arguments; | ||
| let nextFn = fn; | ||
| let cmpActive = true; | ||
| let _timer; |
There was a problem hiding this comment.
use underscore consistently please. All or none
There was a problem hiding this comment.
Removed the underscore for this var to make it consistent with the others.
modules/consentManagement.js
Outdated
| } | ||
|
|
||
| // start of CMP specific logic | ||
| if (userCMP === 'iab') { |
There was a problem hiding this comment.
can you move this logic to a separate function and define at the top of the file. That will make it easier / cleaner for other CMPs to integrate.
There was a problem hiding this comment.
I have reorganized the functions, please take a look on the next commit and let me know what you think.
| // add new CMPs here | ||
| const availCMPs = ['iab']; | ||
|
|
||
| export let userCMP; |
There was a problem hiding this comment.
I don't think these vars need to be exported?
There was a problem hiding this comment.
I'm exporting these vars so I can read them in the consentManagement_spec.js test file for the setConfig unit tests.
| logWarn(`${MODULE_NAME} module: concurrency has been disabled and "$$PREBID_GLOBAL$$.requestBids" call was queued`); | ||
| } | ||
| }, 100); | ||
| }, 5); |
There was a problem hiding this comment.
what's the impact of this change?
There was a problem hiding this comment.
The number (also relates to the other comment below) acts as a priority level for the hooked functions (when there are multiple hooked functions on the same hook).
The pbjs.requestBids function has 3 hooked functions from 3 different pbjs modules: pre1api, PubCommonId and consentManagement. So lowering this priority level ensures that the pre1api module's hooked function will (generally) go last in the sequence if all these modules were enabled together.
Having the pre1api module go last (specifically after consentManagement) is related to the need for the pre1api to execute right before the pbjs.requestBids so it knows which auction is currently active. The consentManagement module uses/waits on callbacks (to retrieve information from the CMP), and this buffering of the auction process could cause issues for the pre1api knowing which auction is active if there was a time gap because it ran first. So the lower priority helps avoid this scenario.
The 5 specifically is lower than the default priority that's set for any hooked function (which is 10), but still higher than the base function pbjs.requestBids (which is 0). So it should generally always the be last hook to run before the pbjs.requestBids would execute.
modules/consentManagement.js
Outdated
| if (typeof config.waitForConsentTimeout === 'number') { | ||
| consentTimeout = config.waitForConsentTimeout; | ||
| } else { | ||
| consentTimeout = 5000; |
There was a problem hiding this comment.
defaults like these should be made constant values and defined at the top of the file
There was a problem hiding this comment.
I have created default variables and utilized them in the setConfig part of the module code in place of the actual values.
| utils.logInfo(`consentManagement config did not specify lookUpFailureResolution. Using system default setting (${lookUpFailureChoice}).`); | ||
| } | ||
|
|
||
| $$PREBID_GLOBAL$$.requestBids.addHook(requestBidsHook, 50); |
There was a problem hiding this comment.
I always forget what the addHook signature looks like. What's the 50 value signify?
There was a problem hiding this comment.
See comment above for the change in the pre1api file.
|
@mkendall07 I have just pushed in a commit with the changes for the various points you provided. Please take a look again and let me know of any additional feedback. Thanks. |
|
@vzhukovsky Can you please provide a link to the 1.1 spec's documentation that you are referring to? |
|
Unfortunately this spec has not published yet. I am not allowed to share it. But AppNexus is member of the group that defined this spec. Can you reach out to Andrew Sweeney or someone else who might have it? |
|
Hello all, |
|
@jsnellbaker Hi. Are there still no any plans to support GDPR for Prebid legacy? Most our users are still on 0.x. |
|
In lieu of the more recent questions and the upcoming 1.1 CMP spec I wanted to provide an update on the general direction for this module. Below is a series of changes coming but that are not due to the 1.1 CMP spec:
Below is a list of changes due to the 1.1 CMP spec
I am planning to push these changes to the branch either today or tomorrow once I finish some additional testing. |
|
|
||
| if (window.__cmp) { | ||
| window.__cmp('getConsentData', 'vendorConsents', cmpSuccess); | ||
| window.__cmp('getVendorConsents', null, cmpSuccess); |
There was a problem hiding this comment.
if CMP js library is not yet loaded then __cmp will still be an object, not a function, good to have a isFunction check.
|
@jsnellbaker Hi. Anyway, we have already done porting in our fork and will be happy to contribute back if needed. |
modules/consentManagement.js
Outdated
| } | ||
|
|
||
| /** when we get the return message, call the stashed callback */ | ||
| window.addEventListener('message', function(event) { |
There was a problem hiding this comment.
I think this needs to be cleaned up somewhere. There going to be a lot of message listeners lying around if we do multiple auctions.
There was a problem hiding this comment.
I will add in some logic to fire off removeEventListener at proper points in this code.
| * based on the appropriate result. | ||
| * @param {function(string)} cmpSuccess acts as a success callback when CMP returns a value; pass along consentObject (string) from CMP | ||
| * @param {function(string)} cmpError acts as an error callback while interacting with CMP; pass along an error message (string) | ||
| */ |
There was a problem hiding this comment.
This API is much better 👍
|
|
||
| // add new CMPs here, with their dedicated lookup function | ||
| const cmpCallMap = { | ||
| 'iab': lookupIabConsent |
There was a problem hiding this comment.
What do we plan to do when this list grows? A module that contains every implementation doesn't scale. I'm fine with approving this pull-request if we have a solution for this going forward (for when we need scaling).
There was a problem hiding this comment.
Hi @snapwich, I think the idea going forward (when we have several more CMPs integrated into the consentManagement) would be store their code into other smaller modules. The consentManagement module would then import the other modules and they'd be called/managed via this cmpCallMap.
src/utils.js
Outdated
| return this.isA(object, t_Numb); | ||
| }; | ||
|
|
||
| exports.isObject = function(object) { |
There was a problem hiding this comment.
This naming is confusing. Functions and arrays are both objects as well. jQuery and others distinguish objects created with {} or new Object from other builtins using the name isPlainObject which I think is a more apt name.
There was a problem hiding this comment.
I'll rename this function to isPlainObject for better clarity.
cwbeck
left a comment
There was a problem hiding this comment.
Request that CMP is always bound and referenced via top-most window instead of window context in which Prebid is loaded.
|
|
||
| /* Setup up a __cmp function to do the postMessage and stash the callback. | ||
| This function behaves (from the caller's perspective identicially to the in-frame __cmp call */ | ||
| window.__cmp = function(cmd, arg, callback) { |
There was a problem hiding this comment.
This makes the assumption that the CMP is bound to the window in which Prebid loads. For safety, we have always loaded Prebid (and our libraries too) inside of a friendly frame, so in our case, we would need this to reference the top-most window.
export function requestCMP(): void {
let topWindow = WindowElement.getTopMostParentWindow();
let createHandler = () => {
let commandQueue: any = [];
let cmp = function(command: any, parameter: any, callback: any){
commandQueue.push({
command: command,
parameter: parameter,
callback: callback
});
};
(cmp as any).commandQueue = commandQueue;
(cmp as any).receiveMessage = (event: any) => {
let data = event && event.data && event.data.__cmp;
if (data) {
commandQueue.push({
callId: data.callId,
command: data.command,
parameter: data.parameter,
event: event
});
}
};
(cmp as any).config = {
"globalConsentLocation": "https://acdn.adnxs.com/cmp/docs/portal.html",
"storeConsentGlobally": true,
"storePublisherData": false
};
return cmp;
};
(topWindow as any).__cmp = createHandler();
topWindow.addEventListener('message', (event) => {
(topWindow as any).__cmp.receiveMessage(event);
});
(topWindow as any).__cmp('showConsentTool');
Async.loadJSIn(topWindow, "https://acdn.adnxs.com/cmp/docs/../cmp.bundle.js");
}As the CMP will always be in the top-most window, can we change this?
There was a problem hiding this comment.
Hi @cwbeck,
The part of the code you referenced is actually part of the code where we have already determined that the CMP is not in the same window as the prebid code. So it's not making one direction assumption as to where the CMP exists, as this can vary per publisher's implementation.
To provide some additional context on the function in question:
This function is part of the stock IAB code that's part of the 1.1 CMP spec to use the CMP's iframe locator (ie __cmpLocator) to detect the exact frame that the CMP is located.
The IAB's code works basically in 3 parts:
- locates the proper window/frame that's housing the CMP code (by looking for the parent window that contains the
__cmpLocatoriframe) - it then writes it's own local version of the
window.__cmpAPI to handle the natural API call and encase it in apostMessagecall to bypass the iframe. - it finally adds an
addEventListenerto wait for themessageresponse from the CMP to grab the consent data and do the next steps.
Please let me know if you have any further questions on this point.
There was a problem hiding this comment.
@jsnellbaker makes sense, thanks for the clarification
* bid response adId same as bidId * test * update adform bid adapter * update unit tests * Added adform adapter description file * updated tests * Another tests update * Add auctionId * Update adapter for auctionId * add auctionId to adformBidAdapter * Final updates to fit 1.0 version * update docs and integration example * Do not mutate original validBidRequests * use atob and btoa instead of custom made module * Renaming one query string parameter * XDomainRequest.send exception fix (#1942) * Added YIELDONE Bid Adapter for Prebid.js 1.0 (#1900) * Added YIELDONE Bid Adapter for Prebid.js 1.0 * Update yieldoneBidAdapter.md change placementId to 44082 * Changed to get size from bid.sizes * fix sizes array * Add user-facing docs reminder to PR template (#1956) * allow non-mappable sizes to be passed and used in rubicon adapter (#1893) * Typo correction of YIELDONE md file (#1954) * Added YIELDONE Bid Adapter for Prebid.js 1.0 * Update yieldoneBidAdapter.md change placementId to 44082 * Changed to get size from bid.sizes * fix sizes array * Fix a typo * Serverbid bid adapter: update alias config (#1963) * use auctionId instead of requestId (#1968) * Add freewheel ssp bidder adapter for prebid 1.0 (#1793) * add stickyadsTV bidder adapter * init unit test file * ad some unit tests * fix unit test on ad format with parameters * add some unit tests * add unit tests on getBid method * add some test cases in unit tests * minor fix on component id tag. * remove adapters-sticky.json test file * use top most accessible window instead of window.top * Pass in the bid request in the createBid call. * use top most accessible window instead of window.top * add unit tests * update unit tests * fix unit test. * fix CI build * add alias freewheel-ssp * update unit tests on bidderCode value * fix component id values and add unit tests * allws to use any outstream format. * fix ASLoader on futur outstream format versions * minor: fix code format. * update unit tests * minor fix code format * minor: add missing new line at eof * replace StickyAdsTVAdapter by freewheel ssp bd adapter (for prebid 1.0) * remove old stickyadstv unittest spec. * fix server response parsing if sent as object with 'body' field * use the vastXml field for video mediatype * add user sync pixel in freewheel ssp adapter * remove all console log calls (replaced using util helper) * remove useless bidderCode (automatically added by the bidderFactory) * Return the SYNC pixel to be added in the page by Prebid.js * remove instance level properties to enable concurrent bids with the same adapter instance. * fix the request apss through and corresponding unit tests * fix 'freeheelssp' typo * + fixed endpoint request data property names - width to w and height to h (#1955) + updated unit test for the adapter to comply with the property name changes * Added iQM Bid Adapter for Prebid.js 1.0 (#1880) * Added iQM Bid Adapter for Prebid.js 1.0 * Modified URL from http to https * Removed geo function which was fetching user location. * Remove stray console.log (#1975) * Remove duplicate request id and fix empty response from getHighesCpmBids, getAdserverTargeting (#1970) * Removed requestId and added auctionId * Updated module fixtures to use auctionId and not requestId * remove request id from external bid object and fix bug for empty result in public api * use auctionId instead of requestId * fixed lint errors * [Add BidAdapter] rxrtb adapter for Perbid.js 1.0 (#1950) * Add: rxrtb prebidAdapter * Update: params for test * Update: code format * Update: code format * Update: code format * ServerBid Server BidAdapter (#1819) * ServerBid Server BidAdapter Allow S2S configuration with ServerBid. * Updates to meet 1.0 callBids/config changes. * Fix linting issues. * added hb_source to default keys (#1969) * added hb_source * dropped function to add hb_source since it is now default key * fixed lint error * Prebid 1.1.0 Release * Increment pre version * S2s defaults fix in serverbidServerBidAdapter (#1986) * removed s2s defaults * start timestamp was missing on s2s requests * remove hardcoded localhost port for tests (#1988) * Fixes unit tests in browsers other than chrome (#1987) * Fixes unit tests in browsers other than chrome * fixed lint errors * Prebid 1.1.1 Release * Add note about docs needed before merge (#1959) * Add note about docs needed before merge * Update pr_review.md * Update pr_review.md * Update pr_review.md * Adding optional width and height to display parameters (#1998) * adding optional size * no tabs * TrustX adapter update (#1979) * Add trustx adapter and tests for it * update integration example * Update trustx adapter * Post-review fixes of Trustx adapter * Code improvement for trustx adapter: changed default price type from gross to net * Update TrustX adapter to support the 1.0 version * Make requested changes for TrustX adapter * Updated markdown file for TrustX adapter * Fix TrustX adapter and spec file * Update TrustX adapter: r parameter was added to ad request as cache buster * Serverbid Bid Adapter: Add new ad sizes (#1983) * Added dynamic ttl property for One Display and One Mobile. (#2004) * pin gulp-connect at non-broken version (#2008) * pin gulp-connect at non-broken version * updated yarn.lock to specify pinned gulp-connect * Gjirafa Bidder Adapter (#1944) * Added Gjirafa adapter * Add gjirafa adapter unit test * adapter update * New line * Requested changes * change hello_world.html to one bid * change hello_world.html to one bid * Dropping changes in gitignore and hello_world example * hello_world changes * Drop hello_world and gitignore * multiformat size validation checks (#1964) * initial commit for multiformat size validation checks * adding unit tests and changes to checkBidRequestSizes function * updates to appnexusBidAdapter * Upgrade Admixer adapter for Prebid 1.0 (#1755) * Migrating to Prebid 1.0 * Migrating to Prebid 1.0 * Fix spec * Add NasmediaAdmixer adapter for Perbid.js 1.0 (#1937) * add NasmediaAdmixer adapter for Perbid.js 1.0 * add NasmediaAdmixer adapter for Perbid.js 1.0 * add NasmediaAdmixer adapter for Perbid.js 1.0 * add NasmediaAdmixer adapter for Perbid.js 1.0 * add NasmediaAdmixer adapter for Perbid.js 1.0 * add NasmediaAdmixer adapter for Perbid.js 1.0 * Added gdpr to adform adapter * Added unit tests * Updated spacing * Update gdprConsent object due to changes in spec
|
@jsnellbaker |
* Added GDPR support for AOL adapter. * Added unit tests for AOL GDPR changes. * Added utils for resolving object type and undefined. * Fixed issues caused by merge. * Made changes in AOL adapter to support gdprApplies flag. * Removed bid floor value from test bid config.
|
FYI this will most likely get merged by EOD. |
mkendall07
left a comment
There was a problem hiding this comment.
LGTM. Please drop the iframe example
|
Just wondering when this is going to be tagged? Is this going to be rolled out as part of a larger release? |
|
Now that this is merged - this is going to be included in the 1.9 release of Prebid.js, which is expected to happen later today. |
|
@vzhukovsky has there been any further discussion wrt porting the module into legacy? If not, would you share link to your port? |
DO NOT MERGE THIS PR IMMEDIATELY. Please consult with @mkendall07 first.
Type of change
Description of change
This change includes:
appnexusBidAdapter,prebidServerBidAdapterandadaptermanagerclasses to utilize this new module.pre1apimoduleI have organized the overall changes for the first three items listed above into their own sections.
GDPR consentManagement Module
This module works in the following high level steps:
pbjs.setConfig()) and initializes the module. This initialization sets some config variables for the module and adds a hook to the$$PREBID_GLOBAL$$.requestBidsfunction in theprebid.jsfile.pbjs.requestBidsfunction, the hook is executed first (ie prior to the auction properly starting) - which starts the rest of the module.postMessageto access the CMP framework that's outside the iframe. This is handled automatically by the module's code.gdprConsent) which gets stored into agdprDataHandlerobject (imported fromadaptermanager.js). After the value is stored, the hook ends and returns back to the originalpbjs.requestBidsfunction so it can run as normal.gdprConsentobject (sample show further below) eventually gets added to thebidRequestobject in theadaptermanager.jsfile'smakeBidRequestfunction so that it's made available to any adapter (standard or server-side) to read the consent information and pass it along to their system. It uses thegdprDataHandlerto read the stored value and insert it into thebidRequestobject.A few additional notes to the above process:
setConfig(), it is effectively disabled and won't create any of the additionalgdprConsentdata object in theadUnitsorbidRequestauction objects.undefinedvalue for the user'sconsentStringattribute in thegdprConsentobject). This decision is controlled through theallowAuctionWithoutConsentoption in the config;truemeans the auction will go through andfalsemeans the auction will be canceled entirely.gdprConsentobject that gets added to thebidRequestobject, there is a second value calledconsentRequired. This boolean value has no default value and is only populated in one of two ways. Either the publisher would set an override value in the config part of the module, or they would leave it up to the adapters to set their appropriate value (as some of them may have the ability to derive the ideal value for the end-user).Samples of objects used/produced by the module
Available options for the consentManagement module in the
setConfig():The
gdprConsentobject in thebidRequestobject(s):Adapter changes
As part of this PR, I've also implemented the changes for the
appnexusBidAdapterandprebidServerBidAdapterfiles to read thegdprConsentinformation and pass it along to their respective systems in the proper manner. Details on these implementations is summarized below:appnexusBidAdapter
Per the current planned implementation approach for the /ut handler, we're storing the consent data in the
datafield of the POST request that the adapter executes as part of it'sbuildRequestsfunction. The consent information is stored under a top level object similar in structure to how it's presented in the originalbidRequestobject. Below is a sample of how it looks (note the syntax difference of the object keys):prebidServerBidAdapter
As per the IAB OpenRTB GDPR document (link at bottom), the consent information is stored under two different ORTB2.5 objects in its
bidRequestobject:user.ext.consentfor theconsentStringregs.ext.gdprfor theconsentReqiredboolean.Note the ORTB spec states to pass either a
1or0to represent thetrueorfalsesettings (respectively). There is no backup value automatically set by the adapter whenconsentRequiredis undefined. This undefined value is merely carried through in the ORTB request.The prebid GDPR module does not support the earlier sub-versions of the ORTB2 spec (which would pass the consent data in different locations in the request object).
pre1api change
Also included as part of this PR is a small change to lower the priority of the shared requestBids hook used by the
pre1apimodule.This change was recommended by @snapwich to allow the different modules share the same hook effectively. My testing around the change seems to show the modules are working fine when they're running together. This testing also included the PubCommonId module (which also shares the same hook on the
$$PREBID_GLOBAL$$.requestBidsfunction).gdpr consentManagement module docs prebid.github.io#645
Other information
Information on AppNexus CMP framework:
http://acdn.adnxs.com/cmp/docs/#/
Information on IAB CMP framework:
https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework
IAB OpenRTB GDPR document:
https://iabtechlab.com/wp-content/uploads/2018/02/OpenRTB_Advisory_GDPR_2018-02.pdf