Skip to content

Commit 209fd1f

Browse files
dgirardirenebaudisch
authored andcommitted
Multiple modules: automatically fill in PPID for DFP video URLs (prebid#8365)
prebid#8151
1 parent 8415a8c commit 209fd1f

File tree

5 files changed

+102
-16
lines changed

5 files changed

+102
-16
lines changed

modules/dfpAdServerVideo.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { auctionManager } from '../src/auctionManager.js';
1111
import { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js';
1212
import * as events from '../src/events.js';
1313
import CONSTANTS from '../src/constants.json';
14+
import {getPPID} from '../src/adserver.js';
1415

1516
/**
1617
* @typedef {Object} DfpVideoParams
@@ -118,6 +119,13 @@ export function buildDfpVideoUrl(options) {
118119
const uspConsent = uspDataHandler.getConsentData();
119120
if (uspConsent) { queryParams.us_privacy = uspConsent; }
120121

122+
if (!queryParams.ppid) {
123+
const ppid = getPPID();
124+
if (ppid != null) {
125+
queryParams.ppid = ppid;
126+
}
127+
}
128+
121129
return buildUrl(Object.assign({
122130
protocol: 'https',
123131
host: 'securepubads.g.doubleclick.net',

modules/userId/index.js

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ import {
151151
timestamp,
152152
isEmpty
153153
} from '../../src/utils.js';
154+
import {getPPID as coreGetPPID} from '../../src/adserver.js';
154155

155156
const MODULE_NAME = 'User ID';
156157
const COOKIE = 'cookie';
@@ -650,6 +651,19 @@ function idSystemInitializer({delay = delayFor} = {}) {
650651

651652
let initIdSystem;
652653

654+
function getPPID() {
655+
// userSync.ppid should be one of the 'source' values in getUserIdsAsEids() eg pubcid.org or id5-sync.com
656+
const matchingUserId = ppidSource && (getUserIdsAsEids() || []).find(userID => userID.source === ppidSource);
657+
if (matchingUserId && typeof deepAccess(matchingUserId, 'uids.0.id') === 'string') {
658+
const ppidValue = matchingUserId.uids[0].id.replace(/[\W_]/g, '');
659+
if (ppidValue.length >= 32 && ppidValue.length <= 150) {
660+
return ppidValue;
661+
} else {
662+
logWarn(`User ID - Googletag Publisher Provided ID for ${ppidSource} is not between 32 and 150 characters - ${ppidValue}`);
663+
}
664+
}
665+
}
666+
653667
/**
654668
* Hook is executed before adapters, but after consentManagement. Consent data is requied because
655669
* this module requires GDPR consent with Purpose #1 to save data locally.
@@ -666,23 +680,16 @@ export function requestBidsHook(fn, reqBidsConfigObj, {delay = delayFor} = {}) {
666680
]).then(() => {
667681
// pass available user id data to bid adapters
668682
addIdDataToAdUnitBids(reqBidsConfigObj.adUnits || getGlobal().adUnits, initializedSubmodules);
669-
670-
// userSync.ppid should be one of the 'source' values in getUserIdsAsEids() eg pubcid.org or id5-sync.com
671-
const matchingUserId = ppidSource && (getUserIdsAsEids() || []).find(userID => userID.source === ppidSource);
672-
if (matchingUserId && typeof deepAccess(matchingUserId, 'uids.0.id') === 'string') {
673-
const ppidValue = matchingUserId.uids[0].id.replace(/[\W_]/g, '');
674-
if (ppidValue.length >= 32 && ppidValue.length <= 150) {
675-
if (isGptPubadsDefined()) {
676-
window.googletag.pubads().setPublisherProvidedId(ppidValue);
677-
} else {
678-
window.googletag = window.googletag || {};
679-
window.googletag.cmd = window.googletag.cmd || [];
680-
window.googletag.cmd.push(function() {
681-
window.googletag.pubads().setPublisherProvidedId(ppidValue);
682-
});
683-
}
683+
const ppid = getPPID();
684+
if (ppid) {
685+
if (isGptPubadsDefined()) {
686+
window.googletag.pubads().setPublisherProvidedId(ppid);
684687
} else {
685-
logWarn(`User ID - Googletag Publisher Provided ID for ${ppidSource} is not between 32 and 150 characters - ${ppidValue}`);
688+
window.googletag = window.googletag || {};
689+
window.googletag.cmd = window.googletag.cmd || [];
690+
window.googletag.cmd.push(function() {
691+
window.googletag.pubads().setPublisherProvidedId(ppid);
692+
});
686693
}
687694
}
688695

@@ -980,6 +987,7 @@ function updateSubmodules() {
980987
if (!addedUserIdHook && submodules.length) {
981988
// priority value 40 will load after consentManagement with a priority of 50
982989
getGlobal().requestBids.before(requestBidsHook, 40);
990+
coreGetPPID.after((next) => next(getPPID()));
983991
logInfo(`${MODULE_NAME} - usersync config updated for ${submodules.length} submodules: `, submodules.map(a => a.submodule.name));
984992
addedUserIdHook = true;
985993
}

src/adserver.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { formatQS } from './utils.js';
22
import { targeting } from './targeting.js';
3+
import {hook} from './hook.js';
34

45
// Adserver parent class
56
const AdServer = function(attr) {
@@ -11,6 +12,7 @@ const AdServer = function(attr) {
1112
};
1213

1314
// DFP ad server
15+
// TODO: this seems to be unused?
1416
export function dfpAdserver(options, urlComponents) {
1517
var adserver = new AdServer(options);
1618
adserver.urlComponents = urlComponents;
@@ -53,3 +55,8 @@ export function dfpAdserver(options, urlComponents) {
5355

5456
return adserver;
5557
};
58+
59+
/**
60+
* return the GAM PPID, if available (eid for the userID configured with `userSync.ppidSource`)
61+
*/
62+
export const getPPID = hook('sync', () => undefined);

test/spec/modules/dfpAdServerVideo_spec.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import { auctionManager } from 'src/auctionManager.js';
1010
import { gdprDataHandler, uspDataHandler } from 'src/adapterManager.js';
1111
import * as adpod from 'modules/adpod.js';
1212
import { server } from 'test/mocks/xhr.js';
13+
import * as adServer from 'src/adserver.js';
14+
import {deepClone} from 'src/utils.js';
15+
import {hook} from '../../../src/hook.js';
1316

1417
const bid = {
1518
videoCacheKey: 'abc',
@@ -20,6 +23,10 @@ const bid = {
2023
};
2124

2225
describe('The DFP video support module', function () {
26+
before(() => {
27+
hook.ready();
28+
});
29+
2330
it('should make a legal request URL when given the required params', function () {
2431
const url = parse(buildDfpVideoUrl({
2532
adUnit: adUnit,
@@ -226,6 +233,44 @@ describe('The DFP video support module', function () {
226233
gdprDataHandlerStub.restore();
227234
});
228235

236+
describe('GAM PPID', () => {
237+
let ppid;
238+
let getPPIDStub;
239+
beforeEach(() => {
240+
getPPIDStub = sinon.stub(adServer, 'getPPID').callsFake(() => ppid);
241+
});
242+
afterEach(() => {
243+
getPPIDStub.restore();
244+
});
245+
246+
Object.entries({
247+
'params': {params: {'iu': 'mock/unit'}},
248+
'url': {url: 'https://video.adserver.mock/', params: {'iu': 'mock/unit'}}
249+
}).forEach(([t, opts]) => {
250+
describe(`when using ${t}`, () => {
251+
function buildUrlAndGetParams() {
252+
const url = parse(buildDfpVideoUrl(Object.assign({
253+
adUnit: adUnit,
254+
bid: deepClone(bid),
255+
}, opts)));
256+
return utils.parseQS(url.query);
257+
}
258+
259+
it('should be included if available', () => {
260+
ppid = 'mockPPID';
261+
const q = buildUrlAndGetParams();
262+
expect(q.ppid).to.equal('mockPPID');
263+
});
264+
265+
it('should not be included if not available', () => {
266+
ppid = undefined;
267+
const q = buildUrlAndGetParams();
268+
expect(q.hasOwnProperty('ppid')).to.be.false;
269+
})
270+
})
271+
})
272+
})
273+
229274
describe('special targeting unit test', function () {
230275
const allTargetingData = {
231276
'hb_format': 'video',

test/spec/modules/userId_spec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import * as mockGpt from '../integration/faker/googletag.js';
5252
import 'src/prebid.js';
5353
import {hook} from '../../../src/hook.js';
5454
import {mockGdprConsent} from '../../helpers/consentData.js';
55+
import {getPPID} from '../../../src/adserver.js';
5556

5657
let assert = require('chai').assert;
5758
let expect = require('chai').expect;
@@ -445,6 +446,23 @@ describe('User ID', function () {
445446
});
446447
});
447448

449+
it('should make PPID available to core', () => {
450+
init(config);
451+
setSubmoduleRegistry([sharedIdSystemSubmodule]);
452+
const id = 'thishastobelongerthan32characters';
453+
config.setConfig({
454+
userSync: {
455+
ppid: 'pubcid.org',
456+
userIds: [
457+
{ name: 'pubCommonId', value: {'pubcid': id} },
458+
]
459+
}
460+
});
461+
return getGlobal().refreshUserIds().then(() => {
462+
expect(getPPID()).to.eql(id);
463+
})
464+
});
465+
448466
describe('refreshing before init is complete', () => {
449467
const MOCK_ID = {'MOCKID': '1111'};
450468
let mockIdCallback;

0 commit comments

Comments
 (0)