Skip to content

Commit

Permalink
Extended ID permissions supported by bidder (#6112)
Browse files Browse the repository at this point in the history
* User id bidder permission scheme

* styling

* prebidServer support

* -

* fix

* prebidServerBidAdapter take eidPermissions directly from userId module

* -

* unit tests

* -

* -

* update

* -

* -

* changed pbjs_bidders to pbjsBidders

* changed pbjsBidders to bidders
ext.prebid.data.eidPermissions to ext.prebid.data.eidpermissions

* rerun circleci

* rerun circleci

* omitting eidPermission entry if 'bidders' is not configured

Co-authored-by: myerkovich <myerkovich@rubiconproject.com>
Co-authored-by: Marko Yerkovich <marko_yerkovich@MacBook-Pro-Marko.local>
  • Loading branch information
3 people authored Jan 28, 2021
1 parent 5f56b18 commit a926dee
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 41 deletions.
1 change: 1 addition & 0 deletions integrationExamples/gpt/userId_example.html
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@
},
{
name: "sharedId",
// bidders: ["rubicon", "sampleBidders"], // to allow this ID for specific bidders
params: {
syncTime: 60 // in seconds, default is 24 hours
},
Expand Down
17 changes: 15 additions & 2 deletions modules/prebidServerBidAdapter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import includes from 'core-js-pure/features/array/includes.js';
import { S2S_VENDORS } from './config.js';
import { ajax } from '../../src/ajax.js';
import find from 'core-js-pure/features/array/find.js';
import { getEidPermissions } from '../userId/index.js';

const getConfig = config.getConfig;

Expand Down Expand Up @@ -455,7 +456,7 @@ export function resetWurlMap() {
}

const OPEN_RTB_PROTOCOL = {
buildRequest(s2sBidRequest, bidRequests, adUnits, s2sConfig) {
buildRequest(s2sBidRequest, bidRequests, adUnits, s2sConfig, requestedBidders) {
let imps = [];
let aliases = {};
const firstBidRequest = bidRequests[0];
Expand Down Expand Up @@ -700,6 +701,18 @@ const OPEN_RTB_PROTOCOL = {
utils.deepSetValue(request, 'user.ext.eids', bidUserIdAsEids);
}

const eidPermissions = getEidPermissions();
if (utils.isArray(eidPermissions) && eidPermissions.length > 0) {
if (requestedBidders && utils.isArray(requestedBidders)) {
eidPermissions.forEach(i => {
if (i.bidders) {
i.bidders = i.bidders.filter(bidder => requestedBidders.includes(bidder))
}
});
}
utils.deepSetValue(request, 'ext.prebid.data.eidpermissions', eidPermissions);
}

if (bidRequests) {
if (firstBidRequest.gdprConsent) {
// note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module
Expand Down Expand Up @@ -958,7 +971,7 @@ export function PrebidServer() {
queueSync(syncBidders, gdprConsent, uspConsent, s2sBidRequest.s2sConfig);
}

const request = OPEN_RTB_PROTOCOL.buildRequest(s2sBidRequest, bidRequests, validAdUnits, s2sBidRequest.s2sConfig);
const request = OPEN_RTB_PROTOCOL.buildRequest(s2sBidRequest, bidRequests, validAdUnits, s2sBidRequest.s2sConfig, requestedBidders);
const requestJson = request && JSON.stringify(request);
if (request && requestJson) {
ajax(
Expand Down
22 changes: 22 additions & 0 deletions modules/userId/eids.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,25 @@ export function createEidsArray(bidRequestUserId) {
}
return eids;
}

/**
* @param {SubmoduleContainer[]} submodules
*/
export function buildEidPermissions(submodules) {
let eidPermissions = [];
submodules.filter(i => utils.isPlainObject(i.idObj) && Object.keys(i.idObj).length)
.forEach(i => {
Object.keys(i.idObj).forEach(key => {
if (utils.deepAccess(i, 'config.bidders') && Array.isArray(i.config.bidders) &&
utils.deepAccess(USER_IDS_CONFIG, key + '.source')) {
eidPermissions.push(
{
source: USER_IDS_CONFIG[key].source,
bidders: i.config.bidders
}
);
}
});
});
return eidPermissions;
}
47 changes: 35 additions & 12 deletions modules/userId/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ import { getGlobal } from '../../src/prebidGlobal.js';
import { gdprDataHandler } from '../../src/adapterManager.js';
import CONSTANTS from '../../src/constants.json';
import { module, hook } from '../../src/hook.js';
import { createEidsArray } from './eids.js';
import { createEidsArray, buildEidPermissions } from './eids.js';
import { getCoreStorageManager } from '../../src/storageManager.js';

const MODULE_NAME = 'User ID';
Expand Down Expand Up @@ -214,6 +214,10 @@ export function setStoredValue(submodule, value) {
}
}

export function getEidPermissions() {
return buildEidPermissions(initializedSubmodules);
}

/**
* @param {SubmoduleStorage} storage
* @param {String|undefined} key optional key of the value
Expand Down Expand Up @@ -433,6 +437,26 @@ function getCombinedSubmoduleIds(submodules) {
return combinedSubmoduleIds;
}

/**
* This function will create a combined object for bidder with allowed subModule Ids
* @param {SubmoduleContainer[]} submodules
* @param {string} bidder
*/
function getCombinedSubmoduleIdsForBidder(submodules, bidder) {
if (!Array.isArray(submodules) || !submodules.length || !bidder) {
return {};
}
return submodules
.filter(i => !i.config.bidders || !utils.isArray(i.config.bidders) || i.config.bidders.includes(bidder))
.filter(i => utils.isPlainObject(i.idObj) && Object.keys(i.idObj).length)
.reduce((carry, i) => {
Object.keys(i.idObj).forEach(key => {
carry[key] = i.idObj[key];
});
return carry;
}, {});
}

/**
* @param {AdUnit[]} adUnits
* @param {SubmoduleContainer[]} submodules
Expand All @@ -441,19 +465,18 @@ function addIdDataToAdUnitBids(adUnits, submodules) {
if ([adUnits].some(i => !Array.isArray(i) || !i.length)) {
return;
}
const combinedSubmoduleIds = getCombinedSubmoduleIds(submodules);
const combinedSubmoduleIdsAsEids = createEidsArray(combinedSubmoduleIds);
if (Object.keys(combinedSubmoduleIds).length) {
adUnits.forEach(adUnit => {
if (adUnit.bids && utils.isArray(adUnit.bids)) {
adUnit.bids.forEach(bid => {
adUnits.forEach(adUnit => {
if (adUnit.bids && utils.isArray(adUnit.bids)) {
adUnit.bids.forEach(bid => {
const combinedSubmoduleIds = getCombinedSubmoduleIdsForBidder(submodules, bid.bidder);
if (Object.keys(combinedSubmoduleIds).length) {
// create a User ID object on the bid,
bid.userId = combinedSubmoduleIds;
bid.userIdAsEids = combinedSubmoduleIdsAsEids;
});
}
});
}
bid.userIdAsEids = createEidsArray(combinedSubmoduleIds);
}
});
}
});
}

/**
Expand Down
3 changes: 2 additions & 1 deletion plugins/eslint/validateImports.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ function flagErrors(context, node, importPath) {
if (
path.dirname(absImportPath) === absModulePath || (
absImportPath.startsWith(absModulePath) &&
path.basename(absImportPath) === 'index.js'
path.basename(absImportPath) === 'index.js' &&
path.basename(absFileDir) !== 'prebidServerBidAdapter'
)
) {
context.report(node, `import "${importPath}" cannot require module entry point`);
Expand Down
46 changes: 21 additions & 25 deletions test/spec/modules/prebidServerBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -953,17 +953,15 @@ describe('S2S Adapter', function () {
adapter.callBids(request, BID_REQUESTS, addBidResponse, done, ajax);

const requestBid = JSON.parse(server.requests[0].requestBody);

expect(requestBid.ext).to.deep.equal({
prebid: {
aliases: {
brealtime: 'appnexus'
},
auctiontimestamp: 1510852447530,
targeting: {
includebidderkeys: false,
includewinners: true
}
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.include({
aliases: {
brealtime: 'appnexus'
},
auctiontimestamp: 1510852447530,
targeting: {
includebidderkeys: false,
includewinners: true
}
});
});
Expand All @@ -985,17 +983,15 @@ describe('S2S Adapter', function () {
adapter.callBids(request, BID_REQUESTS, addBidResponse, done, ajax);

const requestBid = JSON.parse(server.requests[0].requestBody);

expect(requestBid.ext).to.deep.equal({
prebid: {
aliases: {
[alias]: 'appnexus'
},
auctiontimestamp: 1510852447530,
targeting: {
includebidderkeys: false,
includewinners: true
}
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.include({
aliases: {
[alias]: 'appnexus'
},
auctiontimestamp: 1510852447530,
targeting: {
includebidderkeys: false,
includewinners: true
}
});
});
Expand Down Expand Up @@ -1376,7 +1372,7 @@ describe('S2S Adapter', function () {

expect(requestBid).to.haveOwnProperty('ext');
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.equal({
expect(requestBid.ext.prebid).to.deep.include({
auctiontimestamp: 1510852447530,
foo: 'bar',
targeting: {
Expand Down Expand Up @@ -1410,7 +1406,7 @@ describe('S2S Adapter', function () {

expect(requestBid).to.haveOwnProperty('ext');
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.equal({
expect(requestBid.ext.prebid).to.deep.include({
auctiontimestamp: 1510852447530,
targeting: {
includewinners: false,
Expand Down Expand Up @@ -1446,7 +1442,7 @@ describe('S2S Adapter', function () {

expect(requestBid).to.haveOwnProperty('ext');
expect(requestBid.ext).to.haveOwnProperty('prebid');
expect(requestBid.ext.prebid).to.deep.equal({
expect(requestBid.ext.prebid).to.deep.include({
auctiontimestamp: 1510852447530,
cache: {
vastxml: 'vastxml-set-though-extPrebid.cache.vastXml'
Expand Down
117 changes: 116 additions & 1 deletion test/spec/modules/userId_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
attachIdSystem,
auctionDelay,
coreStorage,
getEidPermissions,
init,
requestBidsHook,
setStoredConsentData,
Expand Down Expand Up @@ -70,7 +71,7 @@ describe('User ID', function () {
code,
mediaTypes: {banner: {}, native: {}},
sizes: [[300, 200], [300, 600]],
bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}]
bids: [{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}, {bidder: 'anotherSampleBidder', params: {placementId: 'banner-only-bidder'}}]
};
}

Expand Down Expand Up @@ -1196,6 +1197,120 @@ describe('User ID', function () {
}, {adUnits});
});

it('eidPermissions fun with bidders', function (done) {
coreStorage.setCookie('sharedid', JSON.stringify({
'id': 'test222',
'ts': 1590525289611
}), (new Date(Date.now() + 5000).toUTCString()));

setSubmoduleRegistry([sharedIdSubmodule]);
init(config);
config.setConfig({
userSync: {
syncDelay: 0,
userIds: [
{
name: 'sharedId',
bidders: [
'sampleBidder'
],
storage: {
type: 'cookie',
name: 'sharedid',
expires: 28
}
}
]
}
});

requestBidsHook(function () {
const eidPermissions = getEidPermissions();
expect(eidPermissions).to.deep.equal(
[
{source: 'sharedid.org', bidders: ['sampleBidder']}
]
);
adUnits.forEach(unit => {
unit.bids.forEach(bid => {
if (bid.bidder === 'sampleBidder') {
expect(bid).to.have.deep.nested.property('userId.sharedid');
expect(bid.userId.sharedid.id).to.equal('test222');
expect(bid.userIdAsEids[0]).to.deep.equal({
source: 'sharedid.org',
uids: [
{
id: 'test222',
atype: 1,
ext: {
third: 'test222'
}
}
]
});
}
if (bid.bidder === 'anotherSampleBidder') {
expect(bid).to.not.have.deep.nested.property('userId.sharedid');
expect(bid).to.not.have.property('userIdAsEids');
}
});
});
coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE);
done();
}, {adUnits});
});

it('eidPermissions fun without bidders', function (done) {
coreStorage.setCookie('sharedid', JSON.stringify({
'id': 'test222',
'ts': 1590525289611
}), (new Date(Date.now() + 5000).toUTCString()));

setSubmoduleRegistry([sharedIdSubmodule]);
init(config);
config.setConfig({
userSync: {
syncDelay: 0,
userIds: [
{
name: 'sharedId',
storage: {
type: 'cookie',
name: 'sharedid',
expires: 28
}
}
]
}
});

requestBidsHook(function () {
const eidPermissions = getEidPermissions();
expect(eidPermissions).to.deep.equal(
[]
);
adUnits.forEach(unit => {
unit.bids.forEach(bid => {
expect(bid).to.have.deep.nested.property('userId.sharedid');
expect(bid.userId.sharedid.id).to.equal('test222');
expect(bid.userIdAsEids[0]).to.deep.equal({
source: 'sharedid.org',
uids: [
{
id: 'test222',
atype: 1,
ext: {
third: 'test222'
}
}]
});
});
});
coreStorage.setCookie('sharedid', '', EXPIRED_COOKIE_DATE);
done();
}, {adUnits});
});

it('test hook from pubProvidedId config params', function (done) {
setSubmoduleRegistry([pubProvidedIdSubmodule]);
init(config);
Expand Down

0 comments on commit a926dee

Please sign in to comment.