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

IX Bid Adapter: First Party Data Support #7265

Merged
merged 7 commits into from
Aug 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 66 additions & 2 deletions modules/ixBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ 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 FIRST_PARTY_DATA = {
SITE: [
'id', 'name', 'domain', 'cat', 'sectioncat', 'pagecat', 'page', 'ref', 'search', 'mobile',
'privacypolicy', 'publisher', 'content', 'keywords', 'ext'
],
USER: ['id', 'buyeruid', 'yob', 'gender', 'keywords', 'customdata', 'geo', 'data', 'ext']
};

const SOURCE_RTI_MAPPING = {
'liveramp.com': 'idl',
'netid.de': 'NETID',
Expand Down Expand Up @@ -566,6 +575,8 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
}

let currentRequestSize = baseRequestSize;
let fpdRequestSize = 0;
let isFpdAdded = false;

if (otherIxConfig) {
// Append firstPartyData to r.site.page if firstPartyData exists.
Expand All @@ -579,7 +590,18 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
}
firstPartyString = firstPartyString.slice(0, -1);

r.site.page += firstPartyString;
fpdRequestSize = encodeURIComponent(firstPartyString).length;

if (fpdRequestSize < MAX_REQUEST_SIZE) {
if ('page' in r.site) {
r.site.page += firstPartyString;
} else {
r.site.page = firstPartyString;
}
currentRequestSize += fpdRequestSize;
} else {
utils.logError('ix bidder: IX config FPD request size has exceeded maximum request size.');
}
}

// Create t in payload if timeout is configured.
Expand Down Expand Up @@ -617,7 +639,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
currentImpressionSize = encodeURIComponent(JSON.stringify({ impressionObjects })).length;
}

if (BANNER in impressionObjects[0]) {
if (impressionObjects.length && BANNER in impressionObjects[0]) {
const { id, banner: { topframe } } = impressionObjects[0];
const _bannerImpression = {
id,
Expand All @@ -642,6 +664,47 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
r.imp.push(...impressionObjects);
}

currentRequestSize += currentImpressionSize;

const fpd = config.getConfig('ortb2') || {};

if (!utils.isEmpty(fpd) && !isFpdAdded) {
r.ext.ixdiag.fpd = true;

const site = { ...(fpd.site || fpd.context) };

Object.keys(site).forEach(key => {
if (FIRST_PARTY_DATA.SITE.indexOf(key) === -1) {
delete site[key];
}
});

const user = { ...fpd.user };

Object.keys(user).forEach(key => {
if (FIRST_PARTY_DATA.USER.indexOf(key) === -1) {
delete user[key];
}
});

const clonedRObject = utils.deepClone(r);

clonedRObject.site = utils.mergeDeep({}, clonedRObject.site, site);
clonedRObject.user = utils.mergeDeep({}, clonedRObject.user, user);

const requestSize = `${baseUrl}${utils.parseQueryStringParameters({ ...payload, r: JSON.stringify(clonedRObject) })}`.length;

if (requestSize < MAX_REQUEST_SIZE) {
r.site = utils.mergeDeep({}, r.site, site);
r.user = utils.mergeDeep({}, r.user, user);
isFpdAdded = true;
const fpdRequestSize = encodeURIComponent(JSON.stringify({ ...site, ...user })).length;
currentRequestSize += fpdRequestSize;
} else {
utils.logError('ix bidder: FPD request size has exceeded maximum request size.');
}
}

const isLastAdUnit = adUnitIndex === transactionIds.length - 1;

if (wasAdUnitImpressionsTrimmed || isLastAdUnit) {
Expand All @@ -664,6 +727,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
r.imp = [];
r.ext.ixdiag.msd = 0;
r.ext.ixdiag.msi = 0;
isFpdAdded = false;
}
}

Expand Down
129 changes: 129 additions & 0 deletions test/spec/modules/ixBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,135 @@ describe('IndexexchangeAdapter', function () {
});
});

describe('First party data', function () {
afterEach(function() {
config.setConfig({
ortb2: {}
})
});

it('should not set ixdiag.fpd value if not defined', function () {
config.setConfig({
ortb2: {}
});

const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0];
const r = JSON.parse(request.data.r);

expect(r.ext.ixdiag.fpd).to.be.undefined;
});

it('should set ixdiag.fpd value if it exists using fpd', function () {
config.setConfig({
fpd: {
site: {
data: {
pageType: 'article'
}
}
}
});

const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0];
const r = JSON.parse(request.data.r);

expect(r.ext.ixdiag.fpd).to.exist;
});

it('should set ixdiag.fpd value if it exists using ortb2', function () {
config.setConfig({
ortb2: {
site: {
ext: {
data: {
pageType: 'article'
}
}
}
}
});

const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0];
const r = JSON.parse(request.data.r);

expect(r.ext.ixdiag.fpd).to.exist;
});

it('should not send information that is not part of openRTB spec v2.5 using fpd', function () {
config.setConfig({
fpd: {
site: {
keywords: 'power tools, drills',
search: 'drill',
testProperty: 'test_string'
},
user: {
keywords: ['a'],
testProperty: 'test_string'
}
}
});

const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0];
const r = JSON.parse(request.data.r);

expect(r.site.keywords).to.exist;
expect(r.site.search).to.exist;
expect(r.site.testProperty).to.be.undefined;
expect(r.user.keywords).to.exist;
expect(r.user.testProperty).to.be.undefined;
});

it('should not send information that is not part of openRTB spec v2.5 using ortb2', function () {
config.setConfig({
ortb2: {
site: {
keywords: 'power tools, drills',
search: 'drill',
testProperty: 'test_string'
},
user: {
keywords: ['a'],
testProperty: 'test_string'
}
}
});

const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID)[0];
const r = JSON.parse(request.data.r);

expect(r.site.keywords).to.exist;
expect(r.site.search).to.exist;
expect(r.site.testProperty).to.be.undefined;
expect(r.user.keywords).to.exist;
expect(r.user.testProperty).to.be.undefined;
});

it('should not add fpd data to r object if it exceeds maximum request', function () {
config.setConfig({
ortb2: {
site: {
keywords: 'power tools, drills',
search: 'drill',
},
user: {
keywords: Array(1000).join('#'),
}
}
});

const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
bid.mediaTypes.banner.sizes = LARGE_SET_OF_SIZES;

const request = spec.buildRequests([bid])[0];
const r = JSON.parse(request.data.r);

expect(r.site.ref).to.exist;
expect(r.site.keywords).to.be.undefined;
expect(r.user).to.be.undefined;
});
});

describe('buildRequests', function () {
let request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0];
const requestUrl = request.url;
Expand Down