Skip to content
Open
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
28 changes: 25 additions & 3 deletions modules/yieldmoBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,10 @@ export const spec = {
*/
interpretResponse: function (serverResponse, bidRequest) {
const bids = [];
const data = serverResponse.body;
const data = serverResponse && serverResponse.body;
if (!data) {
return bids;
}
if (data.length > 0) {
data.forEach(response => {
if (response.cpm > 0) {
Expand All @@ -206,7 +209,12 @@ export const spec = {
}
if (data.seatbid) {
const seatbids = data.seatbid.reduce((acc, seatBid) => acc.concat(seatBid.bid), []);
seatbids.forEach(bid => bids.push(createNewVideoBid(bid, bidRequest)));
seatbids.forEach(bid => {
const videoBid = createNewVideoBid(bid, bidRequest);
if (videoBid) {
bids.push(videoBid);
}
});
}
return bids;
},
Expand Down Expand Up @@ -328,6 +336,14 @@ function createNewBannerBid(response) {
function createNewVideoBid(response, bidRequest) {
const imp = (deepAccess(bidRequest, 'data.imp') || []).find(imp => imp.id === response.impid);

// A response bid whose impid doesn't match a requested imp can't be built
// (imp.id / imp.video.* would throw). Skip it so one malformed bid doesn't
// abort interpretResponse and drop every other bid in the response; the
// caller filters out this null.
if (!imp) {
return null;
}

const result = {
dealId: response.dealid,
requestId: imp.id,
Expand Down Expand Up @@ -525,7 +541,13 @@ function getBidFloor(bidRequest, mediaType) {
let floorInfo = {};

if (typeof bidRequest.getFloor === 'function') {
floorInfo = bidRequest.getFloor({ currency: CURRENCY, mediaType, size: '*' });
// getFloor can throw or return undefined (e.g. no matching floor rule);
// guard both so a floors misconfig can't drop the bid.
try {
floorInfo = bidRequest.getFloor({ currency: CURRENCY, mediaType, size: '*' }) || {};
} catch (e) {
logError('Yieldmo: getFloor threw; falling back to params bidfloor', e);
}
}

return floorInfo.floor || bidRequest.params.bidfloor || bidRequest.params.bidFloor || 0;
Expand Down
55 changes: 55 additions & 0 deletions test/spec/modules/yieldmoBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,16 @@ describe('YieldmoAdapter', function () {
expect(placementsData[1].bidFloor).to.equal(1.23);
});

it('should fall back to params bidFloor when getFloor throws or returns undefined (no throw)', function () {
const throwing = mockBannerBid({ getFloor: () => { throw new Error('boom'); } }, { bidFloor: 0.42 });
expect(() => build([throwing])).to.not.throw();
expect(JSON.parse(buildAndGetPlacementInfo([throwing]))[0].bidFloor).to.equal(0.42);

const undef = mockBannerBid({ getFloor: () => undefined }, { bidFloor: 0.42 });
expect(() => build([undef])).to.not.throw();
expect(JSON.parse(buildAndGetPlacementInfo([undef]))[0].bidFloor).to.equal(0.42);
});

it('should use bidFloor if no floors module is available', function() {
const placementsData = JSON.parse(buildAndGetPlacementInfo([
mockBannerBid({}, { bidFloor: 1.2 }),
Expand Down Expand Up @@ -920,6 +930,44 @@ describe('YieldmoAdapter', function () {
});
});

it('should skip a video bid whose impid does not match a requested imp, keeping the rest', function () {
const response = mockServerResponse();
const seatbid = [
{
bid: {
adm: '<?xml version="1.0" encoding="UTF-8"?>',
adomain: ['www.example.com'],
crid: 'unmatched-crid',
impid: 'no-such-imp',
price: 1.5,
dealid: 'YMO_456'
},
},
{
bid: {
adm: '<?xml version="1.0" encoding="UTF-8"?>',
adomain: ['www.example.com'],
crid: 'matched-crid',
impid: '91ea8bba1',
price: 2.0,
dealid: 'YMO_789'
},
},
];
const bidRequest = {
data: { imp: [{ id: '91ea8bba1', video: { h: 250, w: 300 } }] },
};
response.body.seatbid = seatbid;

// The unmatched bid must be skipped (not throw), and must not drop the
// matching video bid.
const newResponse = spec.interpretResponse(response, bidRequest);
const videoBids = newResponse.filter(b => b.mediaType === 'video');
expect(videoBids.length).to.equal(1);
expect(videoBids[0].requestId).to.equal('91ea8bba1');
expect(videoBids[0].creativeId).to.equal('matched-crid');
});

it('should not add responses if the cpm is 0 or null', function () {
const response = mockServerResponse();
response.body[0].cpm = 0;
Expand All @@ -928,6 +976,13 @@ describe('YieldmoAdapter', function () {
response.body[0].cpm = null;
expect(spec.interpretResponse(response)).to.deep.equal([]);
});

it('should return [] (not throw) when the response body is missing or null', function () {
expect(spec.interpretResponse({ body: null })).to.deep.equal([]);
expect(spec.interpretResponse({ body: undefined })).to.deep.equal([]);
expect(spec.interpretResponse({})).to.deep.equal([]);
expect(() => spec.interpretResponse(undefined)).to.not.throw();
});
});

describe('getUserSync', function () {
Expand Down
Loading