From d63074ed33355045191da5f90ccdca19e76aa0f5 Mon Sep 17 00:00:00 2001 From: Ian Meyers Date: Wed, 9 Feb 2022 10:34:00 -0800 Subject: [PATCH 1/5] Adding support for a global return of consent metadata with light test coverage. --- src/adapterManager.js | 25 +++++++- src/prebid.js | 19 ++++++ .../spec/modules/consentManagementUsp_spec.js | 27 +++++++++ test/spec/modules/consentManagement_spec.js | 60 +++++++++++++++++++ 4 files changed, 129 insertions(+), 2 deletions(-) diff --git a/src/adapterManager.js b/src/adapterManager.js index c06a9b25786..a377dbacf91 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -173,21 +173,42 @@ function getAdUnitCopyForClientAdapters(adUnits) { export let gdprDataHandler = { consentData: null, - setConsentData: function(consentInfo) { + generatedTime: null, + setConsentData: function(consentInfo, time = timestamp()) { gdprDataHandler.consentData = consentInfo; + gdprDataHandler.generatedTime = time; }, getConsentData: function() { return gdprDataHandler.consentData; + }, + getConsentMeta: function() { + if (gdprDataHandler.consentData && gdprDataHandler.consentData.vendorData && gdprDataHandler.generatedTime) { + return { + gdprApplies: gdprDataHandler.consentData.gdprApplies, + consentStringSize: gdprDataHandler.consentData.vendorData.tcString.length, + timestamp: gdprDataHandler.generatedTime, + apiVersion: gdprDataHandler.consentData.apiVersion + }; + } } }; export let uspDataHandler = { consentData: null, - setConsentData: function(consentInfo) { + generatedTime: null, + setConsentData: function(consentInfo, time = timestamp()) { uspDataHandler.consentData = consentInfo; + uspDataHandler.generatedTime = time; }, getConsentData: function() { return uspDataHandler.consentData; + }, + getConsentMeta: function() { + if (uspDataHandler.consentData && uspDataHandler.generatedTime) { + return { + usp: uspDataHandler.consentData, + timestamp: uspDataHandler.generatedTime} + } } }; diff --git a/src/prebid.js b/src/prebid.js index 23d153fccd4..d7703ec6e11 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -20,6 +20,7 @@ import { executeRenderer, isRendererRequired } from './Renderer.js'; import { createBid } from './bidfactory.js'; import { storageCallbacks } from './storageManager.js'; import { emitAdRenderSucceeded, emitAdRenderFail } from './adRendering.js'; +import {gdprDataHandler, uspDataHandler} from './adapterManager.js' const $$PREBID_GLOBAL$$ = getGlobal(); const CONSTANTS = require('./constants.json'); @@ -268,6 +269,24 @@ $$PREBID_GLOBAL$$.getAdserverTargeting = function (adUnitCode) { return targeting.getAllTargeting(adUnitCode); }; +/** + * returns all consent data + * @return {Object} Map of consent types and data + * @alias module:pbjs.getConsentData + */ +function getConsentMetadata() { + return { + gdpr: gdprDataHandler.getConsentMeta(), + usp: uspDataHandler.getConsentMeta(), + coppa: !!(config.getConfig('coppa')) + } +} + +$$PREBID_GLOBAL$$.getConsentMetadata = function () { + logInfo('Invoking $$PREBID_GLOBAL$$.getConsentMetadata'); + return getConsentMetadata(); +}; + function getBids(type) { const responses = auctionManager[type]() .filter(bind.call(adUnitsFilter, this, auctionManager.getAdUnitCodes())); diff --git a/test/spec/modules/consentManagementUsp_spec.js b/test/spec/modules/consentManagementUsp_spec.js index 7d3cd48a8e4..d006d93b7b9 100644 --- a/test/spec/modules/consentManagementUsp_spec.js +++ b/test/spec/modules/consentManagementUsp_spec.js @@ -58,6 +58,12 @@ describe('consentManagement', function () { sinon.assert.notCalled(utils.logInfo); }); + it('should not produce any USP metadata', function() { + setConsentConfig({}); + let consentMeta = uspDataHandler.getConsentMeta(); + expect(consentMeta).to.be.undefined; + }); + it('should exit the consent manager if only config.gdpr is an object', function() { setConsentConfig({ gdpr: { cmpApi: 'iab' } }); expect(consentAPI).to.be.undefined; @@ -366,6 +372,27 @@ describe('consentManagement', function () { expect(didHookReturn).to.be.true; expect(consent).to.equal(testConsentData.uspString); }); + + it('returns USP consent metadata', function () { + let testConsentData = { + uspString: '1NY' + }; + + uspapiStub = sinon.stub(window, '__uspapi').callsFake((...args) => { + args[2](testConsentData, true); + }); + + setConsentConfig(goodConfig); + requestBidsHook(() => { didHookReturn = true; }, {}); + + let consentMeta = uspDataHandler.getConsentMeta(); + + sinon.assert.notCalled(utils.logWarn); + sinon.assert.notCalled(utils.logError); + + expect(consentMeta.usp).to.equal(testConsentData.uspString); + expect(consentMeta.timestamp).to.be.above(1644367751709); + }); }); }); }); diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index d7ae3c58a85..58e565a848d 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -45,6 +45,13 @@ describe('consentManagement', function () { expect(userCMP).to.be.undefined; sinon.assert.calledOnce(utils.logWarn); }); + + it('should not produce any consent metadata', function() { + setConsentConfig(undefined) + let consentMetadata = gdprDataHandler.getConsentMeta(); + expect(consentMetadata).to.be.undefined; + sinon.assert.calledOnce(utils.logWarn); + }) }); describe('valid setConsentConfig value', function () { @@ -667,6 +674,59 @@ describe('consentManagement', function () { expect(consent.apiVersion).to.equal(2); }); + it('produces gdpr metadata', function () { + let testConsentData = { + tcString: 'abc12345234', + gdprApplies: true, + purposeOneTreatment: false, + eventStatus: 'tcloaded', + vendorData: { + tcString: 'abc12345234' + } + }; + cmpStub = sinon.stub(window, '__tcfapi').callsFake((...args) => { + args[2](testConsentData, true); + }); + + setConsentConfig(goodConfigWithAllowAuction); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consentMeta = gdprDataHandler.getConsentMeta(); + sinon.assert.notCalled(utils.logError); + expect(consentMeta.consentStringSize).to.be.above(0) + expect(consentMeta.gdprApplies).to.be.true; + expect(consentMeta.apiVersion).to.equal(2); + expect(consentMeta.timestamp).to.be.above(1644367751709); + }); + + it('performs lookup check and stores consentData for a valid existing user with additional consent', function () { + let testConsentData = { + tcString: 'abc12345234', + addtlConsent: 'superduperstring', + gdprApplies: true, + purposeOneTreatment: false, + eventStatus: 'tcloaded' + }; + cmpStub = sinon.stub(window, '__tcfapi').callsFake((...args) => { + args[2](testConsentData, true); + }); + + setConsentConfig(goodConfigWithAllowAuction); + + requestBidsHook(() => { + didHookReturn = true; + }, {}); + let consent = gdprDataHandler.getConsentData(); + sinon.assert.notCalled(utils.logError); + expect(didHookReturn).to.be.true; + expect(consent.consentString).to.equal(testConsentData.tcString); + expect(consent.addtlConsent).to.equal(testConsentData.addtlConsent); + expect(consent.gdprApplies).to.be.true; + expect(consent.apiVersion).to.equal(2); + }); + it('performs lookup check and stores consentData for a valid existing user with additional consent', function () { let testConsentData = { tcString: 'abc12345234', From f79092fa0f756626612d409c9526d3431381c2ec Mon Sep 17 00:00:00 2001 From: Ian Meyers Date: Tue, 15 Feb 2022 08:54:14 -0800 Subject: [PATCH 2/5] removing duplicate test --- test/spec/modules/consentManagement_spec.js | 26 --------------------- 1 file changed, 26 deletions(-) diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index 58e565a848d..94a7fdfb29b 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -727,32 +727,6 @@ describe('consentManagement', function () { expect(consent.apiVersion).to.equal(2); }); - it('performs lookup check and stores consentData for a valid existing user with additional consent', function () { - let testConsentData = { - tcString: 'abc12345234', - addtlConsent: 'superduperstring', - gdprApplies: true, - purposeOneTreatment: false, - eventStatus: 'tcloaded' - }; - cmpStub = sinon.stub(window, '__tcfapi').callsFake((...args) => { - args[2](testConsentData, true); - }); - - setConsentConfig(goodConfigWithAllowAuction); - - requestBidsHook(() => { - didHookReturn = true; - }, {}); - let consent = gdprDataHandler.getConsentData(); - sinon.assert.notCalled(utils.logError); - expect(didHookReturn).to.be.true; - expect(consent.consentString).to.equal(testConsentData.tcString); - expect(consent.addtlConsent).to.equal(testConsentData.addtlConsent); - expect(consent.gdprApplies).to.be.true; - expect(consent.apiVersion).to.equal(2); - }); - it('throws an error when processCmpData check fails + does not call requestBids callbcack even when allowAuction is true', function () { let testConsentData = {}; let bidsBackHandlerReturn = false; From 9cd6231a7c456262928637efbead8be197d58034 Mon Sep 17 00:00:00 2001 From: Ian Meyers Date: Thu, 17 Feb 2022 09:55:33 -0800 Subject: [PATCH 3/5] updates for code consistency, timestamp naming --- src/adapterManager.js | 7 ++++--- src/prebid.js | 2 +- test/spec/modules/consentManagementUsp_spec.js | 2 +- test/spec/modules/consentManagement_spec.js | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/adapterManager.js b/src/adapterManager.js index a377dbacf91..1ecb08ed9b0 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -182,11 +182,11 @@ export let gdprDataHandler = { return gdprDataHandler.consentData; }, getConsentMeta: function() { - if (gdprDataHandler.consentData && gdprDataHandler.consentData.vendorData && gdprDataHandler.generatedTime) { + if (gdprDataHandler.consentData && gdprDataHandler.consentData.vendorData && gdprDataHandler.consentData.vendorData.tcString && gdprDataHandler.generatedTime) { return { gdprApplies: gdprDataHandler.consentData.gdprApplies, consentStringSize: gdprDataHandler.consentData.vendorData.tcString.length, - timestamp: gdprDataHandler.generatedTime, + generatedAt: gdprDataHandler.generatedTime, apiVersion: gdprDataHandler.consentData.apiVersion }; } @@ -207,7 +207,8 @@ export let uspDataHandler = { if (uspDataHandler.consentData && uspDataHandler.generatedTime) { return { usp: uspDataHandler.consentData, - timestamp: uspDataHandler.generatedTime} + generatedAt: uspDataHandler.generatedTime + } } } }; diff --git a/src/prebid.js b/src/prebid.js index d7703ec6e11..13619e6633c 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -20,7 +20,7 @@ import { executeRenderer, isRendererRequired } from './Renderer.js'; import { createBid } from './bidfactory.js'; import { storageCallbacks } from './storageManager.js'; import { emitAdRenderSucceeded, emitAdRenderFail } from './adRendering.js'; -import {gdprDataHandler, uspDataHandler} from './adapterManager.js' +import { gdprDataHandler, uspDataHandler } from './adapterManager.js' const $$PREBID_GLOBAL$$ = getGlobal(); const CONSTANTS = require('./constants.json'); diff --git a/test/spec/modules/consentManagementUsp_spec.js b/test/spec/modules/consentManagementUsp_spec.js index d006d93b7b9..c3ee30afc7d 100644 --- a/test/spec/modules/consentManagementUsp_spec.js +++ b/test/spec/modules/consentManagementUsp_spec.js @@ -391,7 +391,7 @@ describe('consentManagement', function () { sinon.assert.notCalled(utils.logError); expect(consentMeta.usp).to.equal(testConsentData.uspString); - expect(consentMeta.timestamp).to.be.above(1644367751709); + expect(consentMeta.generatedAt).to.be.above(1644367751709); }); }); }); diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index 94a7fdfb29b..52454937d68 100644 --- a/test/spec/modules/consentManagement_spec.js +++ b/test/spec/modules/consentManagement_spec.js @@ -698,7 +698,7 @@ describe('consentManagement', function () { expect(consentMeta.consentStringSize).to.be.above(0) expect(consentMeta.gdprApplies).to.be.true; expect(consentMeta.apiVersion).to.equal(2); - expect(consentMeta.timestamp).to.be.above(1644367751709); + expect(consentMeta.generatedAt).to.be.above(1644367751709); }); it('performs lookup check and stores consentData for a valid existing user with additional consent', function () { From 4d6671c13bcc599f92ec217652e923827a81d22c Mon Sep 17 00:00:00 2001 From: Ian Meyers Date: Thu, 17 Feb 2022 10:35:03 -0800 Subject: [PATCH 4/5] move tcstring check down, return a zero if not a string --- src/adapterManager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/adapterManager.js b/src/adapterManager.js index 1ecb08ed9b0..d79fad23fa4 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -182,10 +182,10 @@ export let gdprDataHandler = { return gdprDataHandler.consentData; }, getConsentMeta: function() { - if (gdprDataHandler.consentData && gdprDataHandler.consentData.vendorData && gdprDataHandler.consentData.vendorData.tcString && gdprDataHandler.generatedTime) { + if (gdprDataHandler.consentData && gdprDataHandler.consentData.vendorData && gdprDataHandler.generatedTime) { return { gdprApplies: gdprDataHandler.consentData.gdprApplies, - consentStringSize: gdprDataHandler.consentData.vendorData.tcString.length, + consentStringSize: (isStr(gdprDataHandler.consentData.vendorData.tcString)) ? gdprDataHandler.consentData.vendorData.tcString.length : 0, generatedAt: gdprDataHandler.generatedTime, apiVersion: gdprDataHandler.consentData.apiVersion }; From ba2194c2b891cf05249af962573492fb40b17386 Mon Sep 17 00:00:00 2001 From: Ian Meyers Date: Fri, 18 Feb 2022 07:56:12 -0800 Subject: [PATCH 5/5] forgot util import! --- src/adapterManager.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/adapterManager.js b/src/adapterManager.js index d79fad23fa4..058173cfafc 100644 --- a/src/adapterManager.js +++ b/src/adapterManager.js @@ -19,7 +19,8 @@ import { logMessage, logWarn, shuffle, - timestamp + timestamp, + isStr } from './utils.js'; import {getLabels, resolveStatus} from './sizeMapping.js'; import {decorateAdUnitsWithNativeParams, nativeAdapters} from './native.js';