Skip to content

Commit

Permalink
Index Exchange Bid Adapter: adds support for floc (#6758)
Browse files Browse the repository at this point in the history
  • Loading branch information
umakajan authored and idettman committed May 21, 2021
1 parent 14e37c1 commit de15feb
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 41 deletions.
92 changes: 52 additions & 40 deletions modules/ixBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,33 @@ const PRICE_TO_DOLLAR_FACTOR = {
const USER_SYNC_URL = 'https://js-sec.indexww.com/um/ixmatch.html';

const FLOOR_SOURCE = { PBJS: 'p', IX: 'x' };
// determines which eids we send and the rtiPartner field in ext
const SOURCE_RTI_MAPPING = {
'liveramp.com': 'idl',
'netid.de': 'NETID',
'neustar.biz': 'fabrickId',
'zeotap.com': 'zeotapIdPlus',
'uidapi.com': 'UID2',
'adserver.org': 'TDID'
};

const PROVIDERS = [
'britepoolid',
'id5id',
'lipbid',
'haloId',
'criteoId',
'lotamePanoramaId',
'merkleId',
'parrableId',
'connectid',
'sharedid',
'tapadId',
'quantcastId',
'pubcid',
'TDID',
'flocId'
]

/**
* Transform valid bid request config object to banner impression object that will be sent to ad server.
Expand Down Expand Up @@ -285,35 +312,37 @@ function getBidRequest(id, impressions) {
* From the userIdAsEids array, filter for the ones our adserver can use, and modify them
* for our purposes, e.g. add rtiPartner
* @param {array} allEids userIdAsEids passed in by prebid
* @param {object} flocId flocId passed in by prebid
* @return {object} contains toSend (eids to send to the adserver) and seenSources (used to filter
* identity info from IX Library)
*/
function getEidInfo(allEids) {
// determines which eids we send and the rtiPartner field in ext
var sourceRTIMapping = {
'liveramp.com': 'idl',
'netid.de': 'NETID',
'neustar.biz': 'fabrickId',
'zeotap.com': 'zeotapIdPlus',
'uidapi.com': 'UID2'
};
var toSend = [];
var seenSources = {};
function getEidInfo(allEids, flocData) {
let toSend = [];
let seenSources = {};
if (utils.isArray(allEids)) {
for (var i = 0; i < allEids.length; i++) {
if (sourceRTIMapping[allEids[i].source] && utils.deepAccess(allEids[i], 'uids.0')) {
seenSources[allEids[i].source] = 1;
allEids[i].uids[0].ext = {
rtiPartner: sourceRTIMapping[allEids[i].source]
for (const eid of allEids) {
if (SOURCE_RTI_MAPPING[eid.source] && utils.deepAccess(eid, 'uids.0')) {
seenSources[eid.source] = true;
eid.uids[0].ext = {
rtiPartner: SOURCE_RTI_MAPPING[eid.source]
};
delete allEids[i].uids[0].atype;
toSend.push(allEids[i]);
delete eid.uids[0].atype;
toSend.push(eid);
}
}
}
return { toSend: toSend, seenSources: seenSources };
}
const isValidFlocId = flocData && flocData.id && flocData.version;
if (isValidFlocId) {
const flocEid = {
'source': 'chrome.com',
'uids': [{ 'id': flocData.id, 'ext': { 'rtiPartner': 'flocId', 'ver': flocData.version } }]
};
toSend.push(flocEid);
seenSources['chrome.com'] = true;
}

return { toSend, seenSources };
}
/**
* Builds a request object to be sent to the ad server based on bid requests.
*
Expand All @@ -327,10 +356,9 @@ function getEidInfo(allEids) {
function buildRequest(validBidRequests, bidderRequest, impressions, version) {
// Always use secure HTTPS protocol.
let baseUrl = SECURE_BID_URL;

// Get ids from Prebid User ID Modules
var eidInfo = getEidInfo(utils.deepAccess(validBidRequests, '0.userIdAsEids'));
var userEids = eidInfo.toSend;
let eidInfo = getEidInfo(utils.deepAccess(validBidRequests, '0.userIdAsEids'), utils.deepAccess(validBidRequests, '0.userId.flocId'));
let userEids = eidInfo.toSend;

// RTI ids will be included in the bid request if the function getIdentityInfo() is loaded
// and if the data for the partner exist
Expand Down Expand Up @@ -570,23 +598,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
function _getUserIds(bidRequest) {
const userIds = bidRequest.userId || {};

const PROVIDERS = [
'britepoolid',
'id5id',
'lipbid',
'haloId',
'criteoId',
'lotamePanoramaId',
'merkleId',
'parrableId',
'connectid',
'sharedid',
'tapadId',
'quantcastId',
'pubcid'
]

return PROVIDERS.filter(provider => utils.deepAccess(userIds, provider))
return PROVIDERS.filter(provider => userIds[provider]);
}

/**
Expand Down
106 changes: 105 additions & 1 deletion test/spec/modules/ixBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,9 +462,23 @@ describe('IndexexchangeAdapter', function () {
];

const DEFAULT_USERID_BID_DATA = {
lotamePanoramaId: 'bd738d136bdaa841117fe9b331bb4'
lotamePanoramaId: 'bd738d136bdaa841117fe9b331bb4',
flocId: {id: '1234', version: 'chrome.1.2'}
};

const DEFAULT_FLOC_USERID_PAYLOAD = [
{
source: 'chrome.com',
uids: [{
id: DEFAULT_USERID_BID_DATA.flocId.id,
ext: {
rtiPartner: 'flocId',
ver: DEFAULT_USERID_BID_DATA.flocId.version
}
}]
}
];

describe('inherited functions', function () {
it('should exists and is a function', function () {
const adapter = newBidder(spec);
Expand Down Expand Up @@ -903,6 +917,96 @@ describe('IndexexchangeAdapter', function () {
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]);
});

it('IX adapter reads floc id from prebid userId and adds it to eids when there is not other eids', function() {
const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID);
cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_BID_DATA);
const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0];
const payload = JSON.parse(request.data.r);

expect(payload.user.eids).to.have.lengthOf(1);
expect(payload.user.eids).to.deep.include(DEFAULT_FLOC_USERID_PAYLOAD[0]);
});

it('IX adapter reads floc id from prebid userId and appends it to eids', function() {
const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID);
cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA);
cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_BID_DATA);
const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0];
const payload = JSON.parse(request.data.r);

expect(payload.user.eids).to.have.lengthOf(6);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]);
expect(payload.user.eids).to.deep.include(DEFAULT_FLOC_USERID_PAYLOAD[0]);
});

it('IX adapter reads empty floc obj from prebid userId it, floc is not added to eids', function() {
const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID);
cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA);
cloneValidBid[0].userId = {'flocId': {}}
const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0];
const payload = JSON.parse(request.data.r);

expect(payload.user.eids).to.have.lengthOf(5);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]);
expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]);
});

it('IX adapter reads floc obj from prebid userId it version is missing, floc is not added to eids', function() {
const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID);
cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA);
cloneValidBid[0].userId = {'flocId': {'id': 'abcd'}}
const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0];
const payload = JSON.parse(request.data.r);

expect(payload.user.eids).to.have.lengthOf(5);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]);
expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]);
});

it('IX adapter reads floc obj from prebid userId it ID is missing, floc is not added to eids', function() {
const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID);
cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA);
cloneValidBid[0].userId = {'flocId': {'version': 'chrome.a.b.c'}}
const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0];
const payload = JSON.parse(request.data.r);

expect(payload.user.eids).to.have.lengthOf(5);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]);
expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]);
});

it('IX adapter reads floc id with empty id from prebid userId and it does not added to eids', function() {
const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID);
cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA);
cloneValidBid[0].userId = {flocID: {id: '', ver: 'chrome.1.2.3'}};
const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0];
const payload = JSON.parse(request.data.r);

expect(payload.user.eids).to.have.lengthOf(5);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[0]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[1]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[2]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[3]);
expect(payload.user.eids).to.deep.include(DEFAULT_USERID_PAYLOAD[4]);
expect(payload.user.eids).should.not.include(DEFAULT_FLOC_USERID_PAYLOAD[0]);
});

it('We continue to send in IXL identity info and Prebid takes precedence over IXL', function () {
validIdentityResponse = {
AdserverOrgIp: {
Expand Down

0 comments on commit de15feb

Please sign in to comment.