diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 595b7585cba..0d6b0ca189e 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -203,10 +203,6 @@ function bidToImp(bid) { imp.ext.sid = String(bid.params.id); } - const dfpAdUnitCode = deepAccess(bid, 'ortb2Imp.ext.data.adserver.adslot'); - if (dfpAdUnitCode) { - imp.ext.dfp_ad_unit_code = dfpAdUnitCode; - } return imp; } @@ -488,6 +484,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { // Get ids from Prebid User ID Modules let eidInfo = getEidInfo(deepAccess(validBidRequests, '0.userIdAsEids'), deepAccess(validBidRequests, '0.userId.flocId')); let userEids = eidInfo.toSend; + const pageUrl = getPageUrl() || deepAccess(bidderRequest, 'refererInfo.referer'); // RTI ids will be included in the bid request if the function getIdentityInfo() is loaded // and if the data for the partner exist @@ -586,8 +583,8 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { deepSetValue(r, 'regs.ext.us_privacy', bidderRequest.uspConsent); } - if (bidderRequest.refererInfo) { - r.site.page = bidderRequest.refererInfo.referer; + if (pageUrl) { + r.site.page = pageUrl; } } @@ -684,9 +681,10 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { currentImpressionSize = encodeURIComponent(JSON.stringify({ impressionObjects })).length; } + const gpid = impressions[transactionIds[adUnitIndex]].gpid; + const dfpAdUnitCode = impressions[transactionIds[adUnitIndex]].dfp_ad_unit_code; if (impressionObjects.length && BANNER in impressionObjects[0]) { - const { id, banner: { topframe }, ext } = impressionObjects[0]; - const gpid = impressions[transactionIds[adUnitIndex]].gpid; + const { id, banner: { topframe } } = impressionObjects[0]; const _bannerImpression = { id, banner: { @@ -695,9 +693,9 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { }, } - if (ext.dfp_ad_unit_code || gpid) { + if (dfpAdUnitCode || gpid) { _bannerImpression.ext = {}; - _bannerImpression.ext.dfp_ad_unit_code = ext.dfp_ad_unit_code; + _bannerImpression.ext.dfp_ad_unit_code = dfpAdUnitCode; _bannerImpression.ext.gpid = gpid; } @@ -713,6 +711,8 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) { r.ext.ixdiag.msd += missingCount; r.ext.ixdiag.msi += missingBannerImpressions.length; } else { + // set imp.ext.gpid to resolved gpid for each imp + impressionObjects.forEach(imp => deepSetValue(imp, 'ext.gpid', gpid)); r.imp.push(...impressionObjects); } @@ -889,6 +889,7 @@ function createVideoImps(validBidRequest, videoImps) { videoImps[validBidRequest.transactionId] = {}; videoImps[validBidRequest.transactionId].ixImps = []; videoImps[validBidRequest.transactionId].ixImps.push(imp); + videoImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); } } @@ -909,16 +910,19 @@ function createBannerImps(validBidRequest, missingBannerSizes, bannerImps) { const bannerSizeDefined = includesSize(deepAccess(validBidRequest, 'mediaTypes.banner.sizes'), deepAccess(validBidRequest, 'params.size')); + if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { + bannerImps[validBidRequest.transactionId] = {}; + } + + bannerImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); + bannerImps[validBidRequest.transactionId].dfp_ad_unit_code = deepAccess(validBidRequest, 'ortb2Imp.ext.data.adserver.adslot'); + // Create IX imps from params.size if (bannerSizeDefined) { - if (!bannerImps.hasOwnProperty(validBidRequest.transactionId)) { - bannerImps[validBidRequest.transactionId] = {}; - } if (!bannerImps[validBidRequest.transactionId].hasOwnProperty('ixImps')) { bannerImps[validBidRequest.transactionId].ixImps = [] } bannerImps[validBidRequest.transactionId].ixImps.push(imp); - bannerImps[validBidRequest.transactionId].gpid = deepAccess(validBidRequest, 'ortb2Imp.ext.gpid'); } if (ixConfig.hasOwnProperty('detectMissingSizes') && ixConfig.detectMissingSizes) { @@ -926,6 +930,20 @@ function createBannerImps(validBidRequest, missingBannerSizes, bannerImps) { } } +/** + * Returns the `pageUrl` set by publisher on the page if it is an valid url + */ +function getPageUrl() { + const pageUrl = config.getConfig('pageUrl'); + try { + const url = new URL(pageUrl); + return url.href; + } catch (_) { + logWarn(`IX Bid Adapter: invalid pageUrl config property value set: ${pageUrl}`); + return undefined; + } +} + /** * Determines IX configuration type based on IX params * @param {object} valid IX configured param @@ -1330,7 +1348,7 @@ export const spec = { bid = parseBid(innerBids[j], responseBody.cur, bidRequest); if (!deepAccess(bid, 'mediaTypes.video.renderer') && deepAccess(bid, 'mediaTypes.video.context') === 'outstream') { - bid.mediaTypes.video.renderer = createRenderer(innerBids[j].bidId); + bid.renderer = createRenderer(innerBids[j].bidId); } bids.push(bid); diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 339240d3e6a..0e1a854b67c 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -173,6 +173,26 @@ describe('IndexexchangeAdapter', function () { } ]; + const DEFAULT_BANNER_VALID_BID_PARAM_NO_SIZE = [ + { + bidder: 'ix', + params: { + siteId: '123' + }, + mediaTypes: { + banner: { + sizes: [[300, 250], [300, 600]] + } + }, + adUnitCode: 'div-gpt-ad-1460505748561-0', + transactionId: '173f49a8-7549-4218-a23c-e7ba59b47229', + bidId: '1a2b3c4d', + bidderRequestId: '11a22b33c44d', + auctionId: '1aa2bb3cc4dd', + schain: SAMPLE_SCHAIN + } + ]; + const DEFAULT_VIDEO_VALID_BID = [ { bidder: 'ix', @@ -675,6 +695,22 @@ describe('IndexexchangeAdapter', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); }); + it('should set bid[].renderer if renderer not defined at mediaType.video level', function () { + const bid = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { + data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: DEFAULT_MULTIFORMAT_BANNER_VALID_BID + }); + expect(bid[0].renderer).to.be.exist; + }); + + it('should not set bid[].renderer if renderer defined at mediaType.video level', function () { + const outstreamAdUnit = DEFAULT_MULTIFORMAT_BANNER_VALID_BID; + outstreamAdUnit[0].mediaTypes.video.renderer = {} + const bid = spec.interpretResponse({ body: DEFAULT_VIDEO_BID_RESPONSE }, { + data: DEFAULT_BIDDER_REQUEST_DATA, validBidRequests: DEFAULT_MULTIFORMAT_BANNER_VALID_BID + }); + expect(bid[0].renderer).to.be.undefined; + }); + it('should return false when there is only bidFloor', function () { const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]); bid.params.bidFloor = 50; @@ -1375,6 +1411,7 @@ describe('IndexexchangeAdapter', function () { delete bidWithoutSchain[0].schain; const requestWithoutSchain = spec.buildRequests(bidWithoutSchain, DEFAULT_OPTION)[0]; const queryWithoutSchain = requestWithoutSchain.data; + const GPID = '/19968336/some-adunit-path'; it('request should be made to IX endpoint with GET method', function () { expect(requestMethod).to.equal('GET'); @@ -1414,7 +1451,6 @@ describe('IndexexchangeAdapter', function () { }); it('should send gpid in request if ortb2Imp.ext.gpid exists', function () { - const GPID = '/19968336/some-adunit-path'; const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); validBids[0].ortb2Imp = { ext: { @@ -1426,6 +1462,18 @@ describe('IndexexchangeAdapter', function () { expect(gpid).to.equal(GPID); }); + it('should send gpid in request if ortb2Imp.ext.gpid exists when no size present', function () { + const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID_PARAM_NO_SIZE); + validBids[0].ortb2Imp = { + ext: { + gpid: GPID + } + }; + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const { ext: { gpid } } = JSON.parse(requests[0].data.r).imp[0]; + expect(gpid).to.equal(GPID); + }); + it('should not send dfp_adunit_code in request if ortb2Imp.ext.data.adserver.adslot does not exists', function () { const GPID = '/19968336/some-adunit-path'; const validBids = utils.deepClone(DEFAULT_BANNER_VALID_BID); @@ -1490,6 +1538,14 @@ describe('IndexexchangeAdapter', function () { expect(payload.imp).to.have.lengthOf(1); }); + it('payload should set site.page to pageUrl when it exists in config object', function () { + const url = 'https://example.com/index.html'; + config.setConfig({ pageUrl: url }); + const request = spec.buildRequests(DEFAULT_BANNER_VALID_BID, DEFAULT_OPTION)[0].data; + const payload = JSON.parse(request.r); + expect(payload.site.page).to.contains(url); + }); + it('payload should have correct format and value for r.id when bidderRequestId is a number ', function () { const bidWithIntId = utils.deepClone(DEFAULT_BANNER_VALID_BID); bidWithIntId[0].bidderRequestId = 123456; @@ -1699,6 +1755,10 @@ describe('IndexexchangeAdapter', function () { }); describe('first party data', () => { + beforeEach(() => { + config.resetConfig(); + }); + it('should add first party data to page url in bid request if it exists in config', function () { config.setConfig({ ix: { @@ -2104,6 +2164,19 @@ describe('IndexexchangeAdapter', function () { expect(impression.video.api).to.equal(2); expect(impression.video.mimes[0]).to.equal('video/mp4'); }); + + it('should send gpid in request if ortb2Imp.ext.gpid exists', function () { + const GPID = '/19968336/some-adunit-path'; + const validBids = utils.deepClone(DEFAULT_VIDEO_VALID_BID); + validBids[0].ortb2Imp = { + ext: { + gpid: GPID + } + }; + const requests = spec.buildRequests(validBids, DEFAULT_OPTION); + const { ext: { gpid } } = JSON.parse(requests[0].data.r).imp[0]; + expect(gpid).to.equal(GPID); + }); }); describe('buildRequestMultiFormat', function () {