diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 766ece90fce..2d7157bc674 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -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', @@ -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. @@ -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. @@ -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, @@ -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) { @@ -664,6 +727,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { r.imp = []; r.ext.ixdiag.msd = 0; r.ext.ixdiag.msi = 0; + isFpdAdded = false; } } diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 4c24af6c082..53bfc74af7f 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -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;