diff --git a/src/adapterManager.js b/src/adapterManager.js index 297b8d81536..7480f2a56f6 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'; @@ -173,21 +174,43 @@ 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: (isStr(gdprDataHandler.consentData.vendorData.tcString)) ? gdprDataHandler.consentData.vendorData.tcString.length : 0, + generatedAt: 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, + generatedAt: uspDataHandler.generatedTime + } + } } }; diff --git a/src/prebid.js b/src/prebid.js index e27d73c40b0..7c8420cac29 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..c3ee30afc7d 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.generatedAt).to.be.above(1644367751709); + }); }); }); }); diff --git a/test/spec/modules/consentManagement_spec.js b/test/spec/modules/consentManagement_spec.js index d7ae3c58a85..52454937d68 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,33 @@ 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.generatedAt).to.be.above(1644367751709); + }); + it('performs lookup check and stores consentData for a valid existing user with additional consent', function () { let testConsentData = { tcString: 'abc12345234',