Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IndexExchange Bid Adapter: Added support for netID, ID+ and FabrickId userId #6286

Merged
merged 14 commits into from
Feb 17, 2021
Merged
78 changes: 33 additions & 45 deletions modules/ixBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,36 +212,35 @@ function getBidRequest(id, impressions) {
}

/**
* Adds a User ID module's response into user Eids array.
*
* @param {array} userEids An array of objects containing user ids,
* will be attached to bid request later.
* @param {object} seenIdPartners An object with Identity partners names already added,
* updated with new partner name.
* @param {*} id The id obtained from User ID module.
* @param {string} source The URL of the User ID module.
* @param {string} ixlPartnerName The name of the Identity Partner in IX Library.
* @param {string} rtiPartner The name of the User ID provider in Prebid.
* @return {boolean} True if successfully added the ID to the userEids, false otherwise.
* 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
* @return {object} contains toSend (eids to send to the adserver) and seenSources (used to filter
* identity info from IX Library)
*/
function addUserEids(userEids, seenIdPartners, id, source, ixlPartnerName, rtiPartner) {
if (id) {
// mark the partnername that IX RTI uses
seenIdPartners[ixlPartnerName] = 1;
userEids.push({
source: source,
uids: [{
id: id,
ext: {
rtiPartner: rtiPartner
}
}]
});
return true;
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'
};
var toSend = [];
var 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]
};
delete allEids[i].uids[0].atype;
toSend.push(allEids[i]);
}
}
}

utils.logWarn('Tried to add a user ID from Prebid, the ID received was null');
return false;
return { toSend: toSend, seenSources: seenSources };
}

/**
Expand All @@ -255,21 +254,12 @@ function addUserEids(userEids, seenIdPartners, id, source, ixlPartnerName, rtiPa
*
*/
function buildRequest(validBidRequests, bidderRequest, impressions, version) {
const userEids = [];

// Always use secure HTTPS protocol.
let baseUrl = SECURE_BID_URL;

// Dict for identity partners already populated from prebid
let seenIdPartners = {};

// Get ids from Prebid User ID Modules
const userId = validBidRequests[0].userId;
if (userId && typeof userId === 'object') {
if (userId.idl_env) {
addUserEids(userEids, seenIdPartners, userId.idl_env, 'liveramp.com', 'LiveRampIp', 'idl');
}
}
var eidInfo = getEidInfo(utils.deepAccess(validBidRequests, '0.userIdAsEids'));
var 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 All @@ -278,12 +268,10 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
if (identityInfo && typeof identityInfo === 'object') {
for (const partnerName in identityInfo) {
if (identityInfo.hasOwnProperty(partnerName)) {
// check if not already populated by prebid cache
if (!seenIdPartners.hasOwnProperty(partnerName)) {
let response = identityInfo[partnerName];
if (!response.responsePending && response.data && typeof response.data === 'object' && Object.keys(response.data).length) {
userEids.push(response.data);
}
let response = identityInfo[partnerName];
if (!response.responsePending && response.data && typeof response.data === 'object' &&
Object.keys(response.data).length && !eidInfo.seenSources[response.data.source]) {
userEids.push(response.data);
}
}
}
Expand Down
90 changes: 83 additions & 7 deletions test/spec/modules/ixBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { config } from 'src/config.js';
import { expect } from 'chai';
import { newBidder } from 'src/adapters/bidderFactory.js';
import { spec } from 'modules/ixBidAdapter.js';
import { createEidsArray } from 'modules/userId/eids.js';

describe('IndexexchangeAdapter', function () {
const IX_SECURE_ENDPOINT = 'https://htlb.casalemedia.com/cygnus';
Expand Down Expand Up @@ -351,8 +352,13 @@ describe('IndexexchangeAdapter', function () {

const DEFAULT_USERID_DATA = {
idl_env: '1234-5678-9012-3456', // Liveramp
netId: 'testnetid123', // NetId
IDP: 'userIDP000', // IDP
fabrickId: 'fabrickId9000', // FabrickId
};

const DEFAULT_USERIDASEIDS_DATA = createEidsArray(DEFAULT_USERID_DATA);

const DEFAULT_USERID_PAYLOAD = [
{
source: 'liveramp.com',
Expand All @@ -362,6 +368,30 @@ describe('IndexexchangeAdapter', function () {
rtiPartner: 'idl'
}
}]
}, {
source: 'netid.de',
uids: [{
id: DEFAULT_USERID_DATA.netId,
ext: {
rtiPartner: 'NETID'
}
}]
}, {
source: 'neustar.biz',
uids: [{
id: DEFAULT_USERID_DATA.fabrickId,
ext: {
rtiPartner: 'fabrickId'
}
}]
}, {
source: 'zeotap.com',
uids: [{
id: DEFAULT_USERID_DATA.IDP,
ext: {
rtiPartner: 'zeotapIdPlus'
}
}]
}
];

Expand Down Expand Up @@ -761,14 +791,18 @@ describe('IndexexchangeAdapter', function () {
delete window.headertag;
});

it('IX adapter reads LiveRamp IDL envelope from Prebid and adds it to Video', function () {
it('IX adapter reads supported user modules from Prebid and adds it to Video', function () {
const cloneValidBid = utils.deepClone(DEFAULT_VIDEO_VALID_BID);
cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA);
// cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA);
cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_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.have.lengthOf(4);
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]);
});

it('We continue to send in IXL identity info and Prebid takes precedence over IXL', function () {
Expand Down Expand Up @@ -822,11 +856,45 @@ describe('IndexexchangeAdapter', function () {
}
}
]
},
NetIdIp: {
source: 'netid.de',
uids: [
{
id: 'testnetid',
ext: {
rtiPartner: 'NETID'
}
}
]
},
NeustarIp: {
source: 'neustar.biz',
uids: [
{
id: 'testfabrick',
ext: {
rtiPartner: 'fabrickId'
}
}
]
},
ZeotapIp: {
source: 'zeotap.com',
uids: [
{
id: 'testzeotap',
ext: {
rtiPartner: 'zeotapIdPlus'
}
}
]
}
};

const cloneValidBid = utils.deepClone(DEFAULT_BANNER_VALID_BID);
cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA)
// cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA);
cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA);

const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0];
const payload = JSON.parse(request.data.r);
Expand Down Expand Up @@ -867,10 +935,14 @@ describe('IndexexchangeAdapter', function () {
})

expect(payload.user).to.exist;
expect(payload.user.eids).to.have.lengthOf(3);
expect(payload.user.eids).to.have.lengthOf(6);

expect(payload.user.eids).to.deep.include(validUserIdPayload[0]);
expect(payload.user.eids).to.deep.include(validUserIdPayload[1]);
expect(payload.user.eids).to.deep.include(validUserIdPayload[2]);
expect(payload.user.eids).to.deep.include(validUserIdPayload[3]);
expect(payload.user.eids).to.deep.include(validUserIdPayload[4]);
expect(payload.user.eids).to.deep.include(validUserIdPayload[5]);
});

it('IXL and Prebid are mutually exclusive', function () {
Expand All @@ -892,7 +964,8 @@ describe('IndexexchangeAdapter', function () {
};

const cloneValidBid = utils.deepClone(DEFAULT_VIDEO_VALID_BID);
cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA);
// cloneValidBid[0].userId = utils.deepClone(DEFAULT_USERID_DATA);
cloneValidBid[0].userIdAsEids = utils.deepClone(DEFAULT_USERIDASEIDS_DATA);

const request = spec.buildRequests(cloneValidBid, DEFAULT_OPTION)[0];

Expand All @@ -910,9 +983,12 @@ describe('IndexexchangeAdapter', function () {
});

const payload = JSON.parse(request.data.r);
expect(payload.user.eids).to.have.lengthOf(2);
expect(payload.user.eids).to.have.lengthOf(5);
expect(payload.user.eids).to.deep.include(validUserIdPayload[0]);
expect(payload.user.eids).to.deep.include(validUserIdPayload[1]);
expect(payload.user.eids).to.deep.include(validUserIdPayload[2]);
expect(payload.user.eids).to.deep.include(validUserIdPayload[3]);
expect(payload.user.eids).to.deep.include(validUserIdPayload[4]);
});
});

Expand Down