Skip to content

Commit

Permalink
Prebid core: enrich FPD by default (prebid#9205)
Browse files Browse the repository at this point in the history
* Move rootDomain

* Move FPD enrichments to core

* Remove fpdEnrichments module

* Cleanup

* Add `site`, `device`, and coppa enrichments

* FPD enrichments: GDPR

* FPD enrichments: USP

* Enrich FPD from requestBids

* Fix typo in package.json
  • Loading branch information
dgirardi authored and JacobKlein26 committed Feb 8, 2023
1 parent bace818 commit 57f4f62
Show file tree
Hide file tree
Showing 24 changed files with 751 additions and 965 deletions.
38 changes: 3 additions & 35 deletions libraries/ortbConverter/processors/default.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {deepSetValue, getDefinedParams, getDNT, mergeDeep} from '../../../src/utils.js';
import {deepSetValue, mergeDeep} from '../../../src/utils.js';
import {bannerResponseProcessor, fillBannerImp} from './banner.js';
import {fillVideoImp, fillVideoResponse} from './video.js';
import {setResponseMediaType} from './mediaType.js';
Expand All @@ -20,14 +20,6 @@ export const DEFAULT_PROCESSORS = {
appFpd: fpdFromTopLevelConfig('app'),
siteFpd: fpdFromTopLevelConfig('site'),
deviceFpd: fpdFromTopLevelConfig('device'),
device: {
// sets device w / h / ua / language
fn: setDevice
},
site: {
// sets site.domain, page, and ref from refererInfo
fn: setSite
},
props: {
// sets request properties id, tmax, test, source.tid
fn(ortbRequest, bidderRequest) {
Expand All @@ -41,15 +33,7 @@ export const DEFAULT_PROCESSORS = {
}
deepSetValue(ortbRequest, 'source.tid', ortbRequest.source?.tid || bidderRequest.auctionId);
}
},
coppa: {
fn(ortbRequest) {
const coppa = config.getConfig('coppa');
if (typeof coppa === 'boolean') {
deepSetValue(ortbRequest, 'regs.coppa', coppa ? 1 : 0);
}
}
},
}
},
[IMP]: {
fpd: {
Expand Down Expand Up @@ -144,24 +128,8 @@ function fpdFromTopLevelConfig(prop) {
fn(ortbRequest) {
const data = config.getConfig(prop);
if (typeof data === 'object') {
ortbRequest[prop] = data;
ortbRequest[prop] = mergeDeep({}, ortbRequest[prop], data);
}
}
}
}

export function setDevice(ortbRequest) {
ortbRequest.device = Object.assign({
w: window.innerWidth,
h: window.innerHeight,
dnt: getDNT() ? 1 : 0,
ua: window.navigator.userAgent,
language: window.navigator.language.split('-').shift()
}, ortbRequest.device);
}

export function setSite(ortbRequest, bidderRequest) {
if (bidderRequest.refererInfo) {
ortbRequest.site = Object.assign(getDefinedParams(bidderRequest.refererInfo, ['page', 'domain', 'ref']), ortbRequest.site);
}
}
1 change: 0 additions & 1 deletion modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@
"zeusPrimeRtdProvider"
],
"fpdModule": [
"enrichmentFpdModule",
"validationFpdModule",
"topicsFpdModule"
],
Expand Down
22 changes: 14 additions & 8 deletions modules/consentManagement.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {gdprDataHandler} from '../src/adapterManager.js';
import {includes} from '../src/polyfill.js';
import {timedAuctionHook} from '../src/utils/perfMetrics.js';
import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js';
import {enrichFPD} from '../src/fpd/enrichment.js';

const DEFAULT_CMP = 'iab';
const DEFAULT_CONSENT_TIMEOUT = 10000;
Expand Down Expand Up @@ -356,22 +357,27 @@ export function setConsentConfig(config) {
}
config.getConfig('consentManagement', config => setConsentConfig(config.consentManagement));

export function setOrtbGdpr(ortbRequest, bidderRequest) {
const consent = bidderRequest.gdprConsent;
if (consent) {
if (typeof consent.gdprApplies === 'boolean') {
deepSetValue(ortbRequest, 'regs.ext.gdpr', consent.gdprApplies ? 1 : 0);
export function enrichFPDHook(next, fpd) {
return next(fpd.then(ortb2 => {
const consent = gdprDataHandler.getConsentData();
if (consent) {
if (typeof consent.gdprApplies === 'boolean') {
deepSetValue(ortb2, 'regs.ext.gdpr', consent.gdprApplies ? 1 : 0);
}
deepSetValue(ortb2, 'user.ext.consent', consent.consentString);
}
deepSetValue(ortbRequest, 'user.ext.consent', consent.consentString);
}
return ortb2;
}));
}

enrichFPD.before(enrichFPDHook);

export function setOrtbAdditionalConsent(ortbRequest, bidderRequest) {
// this is not a standardized name for addtlConsent, so keep this as an ORTB library processor rather than an FPD enrichment
const addtl = bidderRequest.gdprConsent?.addtlConsent;
if (addtl && typeof addtl === 'string') {
deepSetValue(ortbRequest, 'user.ext.ConsentedProvidersSettings.consented_providers', addtl);
}
}

registerOrtbProcessor({type: REQUEST, name: 'gdpr', fn: setOrtbGdpr});
registerOrtbProcessor({type: REQUEST, name: 'gdprAddtlConsent', fn: setOrtbAdditionalConsent})
16 changes: 10 additions & 6 deletions modules/consentManagementUsp.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import {deepSetValue, isFn, isNumber, isPlainObject, isStr, logError, logInfo, logWarn} from '../src/utils.js';
import {config} from '../src/config.js';
import adapterManager, {uspDataHandler} from '../src/adapterManager.js';
import {registerOrtbProcessor, REQUEST} from '../src/pbjsORTB.js';
import {timedAuctionHook} from '../src/utils/perfMetrics.js';
import {getHook} from '../src/hook.js';
import {enrichFPD} from '../src/fpd/enrichment.js';

const DEFAULT_CONSENT_API = 'iab';
const DEFAULT_CONSENT_TIMEOUT = 50;
Expand Down Expand Up @@ -323,10 +323,14 @@ config.getConfig('consentManagement', config => setConsentConfig(config.consentM

getHook('requestBids').before(requestBidsHook, 50);

export function setOrtbUsp(ortbRequest, bidderRequest) {
if (bidderRequest.uspConsent) {
deepSetValue(ortbRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent);
}
export function enrichFPDHook(next, fpd) {
return next(fpd.then(ortb2 => {
const consent = uspDataHandler.getConsentData();
if (consent) {
deepSetValue(ortb2, 'regs.ext.us_privacy', consent)
}
return ortb2;
}))
}

registerOrtbProcessor({type: REQUEST, name: 'usp', fn: setOrtbUsp});
enrichFPD.before(enrichFPDHook);
192 changes: 2 additions & 190 deletions modules/enrichmentFpdModule.js
Original file line number Diff line number Diff line change
@@ -1,190 +1,2 @@

/**
* This module sets default values and validates ortb2 first part data
* @module modules/firstPartyData
*/
import { timestamp, mergeDeep } from '../src/utils.js';
import { submodule } from '../src/hook.js';
import {getRefererInfo, parseDomain} from '../src/refererDetection.js';
import { getCoreStorageManager } from '../src/storageManager.js';
import {GreedyPromise} from '../src/utils/promise.js';
import {getHighEntropySUA, getLowEntropySUA} from '../libraries/fpd/sua.js';

let ortb2;
let win = (window === window.top) ? window : window.top;
export const coreStorage = getCoreStorageManager('enrichmentFpd');

export const sua = {he: getHighEntropySUA, le: getLowEntropySUA};

/**
* Find the root domain
* @param {string|undefined} fullDomain
* @return {string}
*/
export function findRootDomain(fullDomain = window.location.hostname) {
if (!coreStorage.cookiesAreEnabled()) {
return fullDomain;
}

const domainParts = fullDomain.split('.');
if (domainParts.length == 2) {
return fullDomain;
}
let rootDomain;
let continueSearching;
let startIndex = -2;
const TEST_COOKIE_NAME = `_rdc${Date.now()}`;
const TEST_COOKIE_VALUE = 'writeable';
do {
rootDomain = domainParts.slice(startIndex).join('.');
let expirationDate = new Date(timestamp() + 10 * 1000).toUTCString();

// Write a test cookie
coreStorage.setCookie(
TEST_COOKIE_NAME,
TEST_COOKIE_VALUE,
expirationDate,
'Lax',
rootDomain,
undefined
);

// See if the write was successful
const value = coreStorage.getCookie(TEST_COOKIE_NAME, undefined);
if (value === TEST_COOKIE_VALUE) {
continueSearching = false;
// Delete our test cookie
coreStorage.setCookie(
TEST_COOKIE_NAME,
'',
'Thu, 01 Jan 1970 00:00:01 GMT',
undefined,
rootDomain,
undefined
);
} else {
startIndex += -1;
continueSearching = Math.abs(startIndex) <= domainParts.length;
}
} while (continueSearching);
return rootDomain;
}

/**
* Checks for referer and if exists merges into ortb2 global data
*/
function setReferer() {
if (getRefererInfo().ref) mergeDeep(ortb2, { site: { ref: getRefererInfo().ref } });
}

/**
* Checks for canonical url and if exists merges into ortb2 global data
*/
function setPage() {
if (getRefererInfo().page) mergeDeep(ortb2, { site: { page: getRefererInfo().page } });
}

/**
* Checks for canonical url and if exists retrieves domain and merges into ortb2 global data
*/
function setDomain() {
const domain = parseDomain(getRefererInfo().page, {noLeadingWww: true});
if (domain) {
mergeDeep(ortb2, { site: { domain: domain } });
mergeDeep(ortb2, { site: { publisher: { domain: findRootDomain(domain) } } });
};
}

/**
* Checks for screen/device width and height and sets dimensions
*/
function setDimensions() {
let width;
let height;

try {
width = win.innerWidth || win.document.documentElement.clientWidth || win.document.body.clientWidth;
height = win.innerHeight || win.document.documentElement.clientHeight || win.document.body.clientHeight;
} catch (e) {
width = window.innerWidth || window.document.documentElement.clientWidth || window.document.body.clientWidth;
height = window.innerHeight || window.document.documentElement.clientHeight || window.document.body.clientHeight;
}

mergeDeep(ortb2, { device: { w: width, h: height } });
}

/**
* Scans page for meta keywords, and if exists, merges into site.keywords
*/
function setKeywords() {
let keywords;

try {
keywords = win.document.querySelector("meta[name='keywords']");
} catch (e) {
keywords = window.document.querySelector("meta[name='keywords']");
}

if (keywords && keywords.content) mergeDeep(ortb2, { site: { keywords: keywords.content.replace(/\s/g, '') } });
}

function setDeviceSua(hints) {
let data = Array.isArray(hints) && hints.length === 0
? GreedyPromise.resolve(sua.le())
: sua.he(hints);
return data.then((sua) => {
if (sua != null) {
mergeDeep(ortb2, {device: {sua}});
}
})
}

/**
* Checks the Global Privacy Control status, and if exists and is true, merges into regs.ext.gpc
*/
function setGpc() {
const gpcValue = navigator.globalPrivacyControl;
if (gpcValue) {
mergeDeep(ortb2, { regs: { ext: { gpc: 1 } } })
}
}

/**
* Resets modules global ortb2 data
*/
export const resetEnrichments = () => { ortb2 = null };

function runEnrichments(fpdConf) {
setReferer();
setPage();
setDomain();
setDimensions();
setKeywords();
setGpc();
return setDeviceSua(fpdConf.uaHints).then(() => ortb2);
}

export function processFpd(fpdConf, {global}) {
if (fpdConf.skipEnrichments) {
return {global};
} else {
let ready;
if (ortb2 == null) {
ortb2 = {};
ready = runEnrichments(fpdConf);
} else {
ready = GreedyPromise.resolve();
}
return ready.then(() => ({
global: mergeDeep({}, ortb2, global)
}))
}
}
/** @type {firstPartyDataSubmodule} */
export const enrichmentsSubmodule = {
name: 'enrichments',
queue: 2,
processFpd
}

submodule('firstPartyData', enrichmentsSubmodule)
// Logic from this module was moved into core since approx. 7.27
// TODO: remove this in v8
Loading

0 comments on commit 57f4f62

Please sign in to comment.