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

Add Auction Options Config #5787

Merged
merged 4 commits into from
Nov 3, 2020
Merged
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
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 @@ -1252,4 +1252,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');
});
});