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: GPID, dfp_ad_unit_code, pageUrl & bid renderer updates #8059

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
54 changes: 39 additions & 15 deletions modules/ixBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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: {
Expand All @@ -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;
}

Expand All @@ -713,6 +711,14 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
r.ext.ixdiag.msd += missingCount;
r.ext.ixdiag.msi += missingBannerImpressions.length;
} else {
impressionObjects.map((imp) => {
if (imp.hasOwnProperty('ext')) {
imp.ext.gpid = gpid;
} else {
imp.ext = {}
imp.ext.gpid = gpid;
}
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably do not need to use .map if you don't care about what the loop returns. Maybe forEach

Also, you could use deepSetValue here if wanted

    else {
      // set imp.ext.gpid to resolved gpid for each imp
      impressionObjects.forEach(imp => deepSetValue(imp, 'ext.gpid', gpid));
      r.imp.push(...impressionObjects);
    }

Just some suggestions, code should work the same either way.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, we don't care about what the loop returns in this case. I've updated the code as per your recommendation.

r.imp.push(...impressionObjects);
}

Expand Down Expand Up @@ -889,6 +895,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');
}
}

Expand All @@ -909,23 +916,40 @@ 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) {
updateMissingSizes(validBidRequest, missingBannerSizes, imp);
}
}

/**
* 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
Expand Down Expand Up @@ -1330,7 +1354,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);
Expand Down
75 changes: 74 additions & 1 deletion test/spec/modules/ixBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -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: {
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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 () {
Expand Down