Skip to content

Commit

Permalink
Allow a bidder to participate on an adunit if it supports at least on…
Browse files Browse the repository at this point in the history
…e of the mediaTypes
  • Loading branch information
matthewlane committed Dec 21, 2017
1 parent 3fa5b33 commit 2d64a45
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 97 deletions.
48 changes: 28 additions & 20 deletions src/prebid.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import { getGlobal } from './prebidGlobal';
import { flatten, uniques, isGptPubadsDefined, adUnitsFilter, removeRequestId } from './utils';
import { videoAdUnit, videoBidder, hasNonVideoBidder } from './video';
import { nativeAdUnit, nativeBidder, hasNonNativeBidder } from './native';
import { listenMessagesFromCreative } from './secureCreatives';
import { userSync } from 'src/userSync.js';
import { loadScript } from './adloader';
Expand Down Expand Up @@ -298,24 +296,34 @@ $$PREBID_GLOBAL$$.requestBids = function ({ bidsBackHandler, timeout, adUnits, a
adUnitCodes = adUnits && adUnits.map(unit => unit.code);
}

// for video-enabled adUnits, only request bids for bidders that support video
adUnits.filter(videoAdUnit).filter(hasNonVideoBidder).forEach(adUnit => {
const nonVideoBidders = adUnit.bids
.filter(bid => !videoBidder(bid))
.map(bid => bid.bidder);

utils.logWarn(utils.unsupportedBidderMessage(adUnit, nonVideoBidders));
adUnit.bids = adUnit.bids.filter(videoBidder);
});

// for native-enabled adUnits, only request bids for bidders that support native
adUnits.filter(nativeAdUnit).filter(hasNonNativeBidder).forEach(adUnit => {
const nonNativeBidders = adUnit.bids
.filter(bid => !nativeBidder(bid))
.map(bid => bid.bidder);

utils.logWarn(utils.unsupportedBidderMessage(adUnit, nonNativeBidders));
adUnit.bids = adUnit.bids.filter(nativeBidder);
/*
* for a given adunit which supports a set of mediaTypes
* and a given bidder which supports a set of mediaTypes
* a bidder is eligible to participate on the adunit
* if it supports at least one of the mediaTypes on the adunit
*/
adUnits.forEach(adUnit => {
// get the adunit's mediaTypes, defaulting to banner if mediaTypes isn't present
const adUnitMediaTypes = Object.keys(adUnit.mediaTypes || {'banner': 'banner'});

// get the bidder's mediaTypes
const bidders = adUnit.bids.map(bid => bid.bidder);
const bidderRegistry = adaptermanager.bidderRegistry;

bidders.forEach(bidder => {
const adapter = bidderRegistry[bidder];
const spec = adapter && adapter.getSpec && adapter.getSpec()
// banner is default if not specified in spec
const bidderMediaTypes = (spec && spec.supportedMediaTypes) || ['banner'];

// check if the bidder's mediaTypes are not in the adUnit's mediaTypes
const bidderEligible = adUnitMediaTypes.some(type => bidderMediaTypes.includes(type));
if (!bidderEligible) {
// drop the bidder from the ad unit if it's not compatible
utils.logWarn(utils.unsupportedBidderMessage(adUnit, bidder));
adUnit.bids = adUnit.bids.filter(bid => bid.bidder !== bidder);
}
});
});

if (!adUnits || adUnits.length === 0) {
Expand Down
11 changes: 5 additions & 6 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -832,17 +832,16 @@ export function isSlotMatchingAdUnitCode(adUnitCode) {
/**
* Constructs warning message for when unsupported bidders are dropped from an adunit
* @param {Object} adUnit ad unit from which the bidder is being dropped
* @param {Array} unSupportedBidders arrary of bidder codes that are not compatible with the adUnit
* @param {string} bidder bidder code that is not compatible with the adUnit
* @return {string} warning message to display when condition is met
*/
export function unsupportedBidderMessage(adUnit, unSupportedBidders) {
const mediaType = adUnit.mediaType || Object.keys(adUnit.mediaTypes).join(', ');
const plural = unSupportedBidders.length === 1 ? 'This bidder' : 'These bidders';
export function unsupportedBidderMessage(adUnit, bidder) {
const mediaType = Object.keys(adUnit.mediaTypes || []).join(', ');

return `
${adUnit.code} is a ${mediaType} ad unit
containing bidders that don't support ${mediaType}: ${unSupportedBidders.join(', ')}.
${plural} won't fetch demand.
containing bidders that don't support ${mediaType}: ${bidder}.
This bidder won't fetch demand.
`;
}

Expand Down
89 changes: 18 additions & 71 deletions test/spec/unit/pbjs_api_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -897,54 +897,22 @@ describe('Unit: Prebid Module', function () {
});
});

describe.skip('#video', () => {
describe('multiformat requests', () => {
let spyCallBids;
let createAuctionStub;
let adUnits;

before(() => {
beforeEach(() => {
adUnits = [{
code: 'adUnit-code',
mediaType: 'video',
mediaTypes: {
banner: {},
native: {},
},
sizes: [[300, 250], [300, 600]],
bids: [
{bidder: 'appnexus', params: {placementId: 'id'}},
{bidder: 'sampleBidder', params: {placementId: 'id'}}
]
}];
adUnitCodes = ['adUnit-code'];
let auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: timeout});
spyCallBids = sinon.spy(adaptermanager, 'callBids');
createAuctionStub = sinon.stub(auctionModule, 'newAuction');
createAuctionStub.returns(auction);
});

after(() => {
auctionModule.newAuction.restore();
adaptermanager.callBids.restore();
});

it('should not callBids if a video adUnit has non-video bidders', () => {
const videoAdaptersBackup = adaptermanager.videoAdapters;
adaptermanager.videoAdapters = ['appnexus'];
$$PREBID_GLOBAL$$.requestBids({adUnits});
sinon.assert.notCalled(adaptermanager.callBids);
adaptermanager.videoAdapters = videoAdaptersBackup;
});
});

describe('#video', () => {
let spyCallBids;
let createAuctionStub;
let adUnits;

before(() => {
adUnits = [{
code: 'adUnit-code',
mediaType: 'video',
sizes: [[300, 250], [300, 600]],
bids: [
{bidder: 'appnexus', params: {placementId: 'id'}}
{bidder: 'sampleBidder', params: {placementId: 'banner-only-bidder'}}
]
}];
adUnitCodes = ['adUnit-code'];
Expand All @@ -954,53 +922,32 @@ describe('Unit: Prebid Module', function () {
createAuctionStub.returns(auction);
})

after(() => {
afterEach(() => {
auctionModule.newAuction.restore();
adaptermanager.callBids.restore();
});

it('should callBids if a video adUnit has all video bidders', () => {
const videoAdaptersBackup = adaptermanager.videoAdapters;
adaptermanager.videoAdapters = ['appnexus'];
it('bidders that support one of the declared formats are allowed to participate', () => {
$$PREBID_GLOBAL$$.requestBids({adUnits});
sinon.assert.calledOnce(adaptermanager.callBids);
adaptermanager.videoAdapters = videoAdaptersBackup;
});
});

describe('#native', () => {
let spyCallBids;
let createAuctionStub;
let adUnits;
const spyArgs = adaptermanager.callBids.getCall(0);
const biddersCalled = spyArgs.args[0][0].bids;

before(() => {
adUnits = [{
code: 'adUnit-code',
mediaType: 'native',
sizes: [[300, 250], [300, 600]],
bids: [
{bidder: 'appnexus', params: {placementId: 'id'}},
{bidder: 'sampleBidder', params: {placementId: 'id'}}
]
}];
adUnitCodes = ['adUnit-code'];
let auction = auctionModule.newAuction({adUnits, adUnitCodes, callback: function() {}, cbTimeout: timeout});
spyCallBids = sinon.spy(adaptermanager, 'callBids');
createAuctionStub = sinon.stub(auctionModule, 'newAuction');
createAuctionStub.returns(auction);
// appnexus and sampleBidder both support banner
expect(biddersCalled.length).to.equal(2);
});

after(() => {
auctionModule.newAuction.restore();
adaptermanager.callBids.restore();
});
it('bidders that do not support one of the declared formats are dropped', () => {
delete adUnits[0].mediaTypes.banner;

it('should only request native bidders on native adunits', () => {
// appnexus is a native bidder, appnexus is not
$$PREBID_GLOBAL$$.requestBids({adUnits});
sinon.assert.calledOnce(adaptermanager.callBids);

const spyArgs = adaptermanager.callBids.getCall(0);
const biddersCalled = spyArgs.args[0][0].bids;

// only appnexus supports native
expect(biddersCalled.length).to.equal(1);
});
});
Expand Down

0 comments on commit 2d64a45

Please sign in to comment.