Skip to content

Commit

Permalink
Add Auction Options Config (prebid#5787)
Browse files Browse the repository at this point in the history
* feature/auction-timing

* rename to auctionOptions

* move filtering outside of loop and organized logic.

* remove auctionOptions test page
  • Loading branch information
ncolletti authored Nov 3, 2020
1 parent 024cac8 commit 07ec2bc
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 2 deletions.
14 changes: 12 additions & 2 deletions src/auction.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import { config } from './config.js';
import { userSync } from './userSync.js';
import { hook } from './hook.js';
import find from 'core-js-pure/features/array/find.js';
import includes from 'core-js-pure/features/array/includes.js';
import { OUTSTREAM } from './video.js';
import { VIDEO } from './mediaTypes.js';

Expand Down Expand Up @@ -397,10 +398,19 @@ export function auctionCallbacks(auctionDone, auctionInstance) {

function adapterDone() {
let bidderRequest = this;
let bidderRequests = auctionInstance.getBidRequests();
const auctionOptionsConfig = config.getConfig('auctionOptions');

bidderRequestsDone.add(bidderRequest);
allAdapterCalledDone = auctionInstance.getBidRequests()
.every(bidderRequest => bidderRequestsDone.has(bidderRequest));

if (auctionOptionsConfig && !utils.isEmpty(auctionOptionsConfig)) {
const secondaryBidders = auctionOptionsConfig.secondaryBidders;
if (secondaryBidders && !bidderRequests.every(bidder => includes(secondaryBidders, bidder.bidderCode))) {
bidderRequests = bidderRequests.filter(request => !includes(secondaryBidders, request.bidderCode));
}
}

allAdapterCalledDone = bidderRequests.every(bidderRequest => bidderRequestsDone.has(bidderRequest));

bidderRequest.bids.forEach(bid => {
if (!bidResponseMap[bid.bidId]) {
Expand Down
34 changes: 34 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,16 @@ export function newConfig() {
set disableAjaxTimeout(val) {
this._disableAjaxTimeout = val;
},

_auctionOptions: {},
get auctionOptions() {
return this._auctionOptions;
},
set auctionOptions(val) {
if (validateauctionOptions(val)) {
this._auctionOptions = val;
}
},
};

if (config) {
Expand Down Expand Up @@ -237,6 +247,30 @@ export function newConfig() {
}
return true;
}

function validateauctionOptions(val) {
if (!utils.isPlainObject(val)) {
utils.logWarn('Auction Options must be an object')
return false
}

for (let k of Object.keys(val)) {
if (k !== 'secondaryBidders') {
utils.logWarn(`Auction Options given an incorrect param: ${k}`)
return false
}
if (k === 'secondaryBidders') {
if (!utils.isArray(val[k])) {
utils.logWarn(`Auction Options ${k} must be of type Array`);
return false
} else if (!val[k].every(utils.isStr)) {
utils.logWarn(`Auction Options ${k} must be only string`);
return false
}
}
}
return true;
}
}

/**
Expand Down
115 changes: 115 additions & 0 deletions test/spec/auctionmanager_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1282,4 +1282,119 @@ describe('auctionmanager.js', function () {
assert.equal(doneSpy.callCount, 1);
})
});

describe('auctionOptions', function() {
let bidRequests;
let doneSpy;
let clock;
let auction = {
getBidRequests: () => bidRequests,
getAuctionId: () => '1',
addBidReceived: () => true,
getTimeout: () => 1000
}
let requiredBidder = BIDDER_CODE;
let requiredBidder1 = BIDDER_CODE1;
let secondaryBidder = 'doNotWaitForMe';

beforeEach(() => {
clock = sinon.useFakeTimers();
doneSpy = sinon.spy();
config.setConfig({
'auctionOptions': {
secondaryBidders: [ secondaryBidder ]
}
})
});

afterEach(() => {
doneSpy.resetHistory();
config.resetConfig();
clock.restore();
});

it('should not wait to call auction done for secondary bidders', function () {
let bids1 = [mockBid({ bidderCode: requiredBidder })];
let bids2 = [mockBid({ bidderCode: requiredBidder1 })];
let bids3 = [mockBid({ bidderCode: secondaryBidder })];
bidRequests = [
mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }),
mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }),
mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }),
];
let cbs = auctionCallbacks(doneSpy, auction);
// required bidder responds immeaditely to auction
cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE1, bids1[0]);
cbs.adapterDone.call(bidRequests[0]);
assert.equal(doneSpy.callCount, 0);

// auction waits for second required bidder to respond
clock.tick(100);
cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids2[0]);
cbs.adapterDone.call(bidRequests[1]);

// auction done is reported and does not wait for secondaryBidder request
assert.equal(doneSpy.callCount, 1);

cbs.addBidResponse.call(bidRequests[2], ADUNIT_CODE1, bids3[0]);
cbs.adapterDone.call(bidRequests[2]);
});

it('should wait for all bidders if they are all secondary', function () {
config.setConfig({
'auctionOptions': {
secondaryBidders: [requiredBidder, requiredBidder1, secondaryBidder]
}
})
let bids1 = [mockBid({ bidderCode: requiredBidder })];
let bids2 = [mockBid({ bidderCode: requiredBidder1 })];
let bids3 = [mockBid({ bidderCode: secondaryBidder })];
bidRequests = [
mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }),
mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }),
mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }),
];
let cbs = auctionCallbacks(doneSpy, auction);
cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE1, bids1[0]);
cbs.adapterDone.call(bidRequests[0]);
clock.tick(100);
assert.equal(doneSpy.callCount, 0)

cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids2[0]);
cbs.adapterDone.call(bidRequests[1]);
clock.tick(100);
assert.equal(doneSpy.callCount, 0);

cbs.addBidResponse.call(bidRequests[2], ADUNIT_CODE1, bids3[0]);
cbs.adapterDone.call(bidRequests[2]);
assert.equal(doneSpy.callCount, 1);
});

it('should allow secondaryBidders to respond in auction before is is done', function () {
let bids1 = [mockBid({ bidderCode: requiredBidder })];
let bids2 = [mockBid({ bidderCode: requiredBidder1 })];
let bids3 = [mockBid({ bidderCode: secondaryBidder })];
bidRequests = [
mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }),
mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }),
mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }),
];
let cbs = auctionCallbacks(doneSpy, auction);
// secondaryBidder is first to respond
cbs.addBidResponse.call(bidRequests[2], ADUNIT_CODE1, bids3[0]);
cbs.adapterDone.call(bidRequests[2]);
clock.tick(100);
assert.equal(doneSpy.callCount, 0);

cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids2[0]);
cbs.adapterDone.call(bidRequests[1]);
clock.tick(100);
assert.equal(doneSpy.callCount, 0);

// first required bidder takes longest to respond, auction isn't marked as done until this occurs
cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE1, bids1[0]);
cbs.adapterDone.call(bidRequests[0]);
assert.equal(doneSpy.callCount, 1);
});
});
});
33 changes: 33 additions & 0 deletions test/spec/config_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,4 +211,37 @@ describe('config API', function () {
setConfig({ bidderSequence: 'random' });
expect(logWarnSpy.called).to.equal(false);
});

it('sets auctionOptions', function () {
const auctionOptionsConfig = {
'secondaryBidders': ['rubicon', 'appnexus']
}
setConfig({ auctionOptions: auctionOptionsConfig });
expect(getConfig('auctionOptions')).to.eql(auctionOptionsConfig);
});

it('should log warning for the wrong value passed to auctionOptions', function () {
setConfig({ auctionOptions: '' });
expect(logWarnSpy.calledOnce).to.equal(true);
const warning = 'Auction Options must be an object';
assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged');
});

it('should log warning for invalid auctionOptions bidder values', function () {
setConfig({ auctionOptions: {
'secondaryBidders': 'appnexus, rubicon',
}});
expect(logWarnSpy.calledOnce).to.equal(true);
const warning = 'Auction Options secondaryBidders must be of type Array';
assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged');
});

it('should log warning for invalid properties to auctionOptions', function () {
setConfig({ auctionOptions: {
'testing': true
}});
expect(logWarnSpy.calledOnce).to.equal(true);
const warning = 'Auction Options given an incorrect param: testing';
assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged');
});
});

0 comments on commit 07ec2bc

Please sign in to comment.