Skip to content

Commit 07ec2bc

Browse files
authored
Add Auction Options Config (#5787)
* feature/auction-timing * rename to auctionOptions * move filtering outside of loop and organized logic. * remove auctionOptions test page
1 parent 024cac8 commit 07ec2bc

File tree

4 files changed

+194
-2
lines changed

4 files changed

+194
-2
lines changed

src/auction.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import { config } from './config.js';
6666
import { userSync } from './userSync.js';
6767
import { hook } from './hook.js';
6868
import find from 'core-js-pure/features/array/find.js';
69+
import includes from 'core-js-pure/features/array/includes.js';
6970
import { OUTSTREAM } from './video.js';
7071
import { VIDEO } from './mediaTypes.js';
7172

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

398399
function adapterDone() {
399400
let bidderRequest = this;
401+
let bidderRequests = auctionInstance.getBidRequests();
402+
const auctionOptionsConfig = config.getConfig('auctionOptions');
400403

401404
bidderRequestsDone.add(bidderRequest);
402-
allAdapterCalledDone = auctionInstance.getBidRequests()
403-
.every(bidderRequest => bidderRequestsDone.has(bidderRequest));
405+
406+
if (auctionOptionsConfig && !utils.isEmpty(auctionOptionsConfig)) {
407+
const secondaryBidders = auctionOptionsConfig.secondaryBidders;
408+
if (secondaryBidders && !bidderRequests.every(bidder => includes(secondaryBidders, bidder.bidderCode))) {
409+
bidderRequests = bidderRequests.filter(request => !includes(secondaryBidders, request.bidderCode));
410+
}
411+
}
412+
413+
allAdapterCalledDone = bidderRequests.every(bidderRequest => bidderRequestsDone.has(bidderRequest));
404414

405415
bidderRequest.bids.forEach(bid => {
406416
if (!bidResponseMap[bid.bidId]) {

src/config.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,16 @@ export function newConfig() {
199199
set disableAjaxTimeout(val) {
200200
this._disableAjaxTimeout = val;
201201
},
202+
203+
_auctionOptions: {},
204+
get auctionOptions() {
205+
return this._auctionOptions;
206+
},
207+
set auctionOptions(val) {
208+
if (validateauctionOptions(val)) {
209+
this._auctionOptions = val;
210+
}
211+
},
202212
};
203213

204214
if (config) {
@@ -237,6 +247,30 @@ export function newConfig() {
237247
}
238248
return true;
239249
}
250+
251+
function validateauctionOptions(val) {
252+
if (!utils.isPlainObject(val)) {
253+
utils.logWarn('Auction Options must be an object')
254+
return false
255+
}
256+
257+
for (let k of Object.keys(val)) {
258+
if (k !== 'secondaryBidders') {
259+
utils.logWarn(`Auction Options given an incorrect param: ${k}`)
260+
return false
261+
}
262+
if (k === 'secondaryBidders') {
263+
if (!utils.isArray(val[k])) {
264+
utils.logWarn(`Auction Options ${k} must be of type Array`);
265+
return false
266+
} else if (!val[k].every(utils.isStr)) {
267+
utils.logWarn(`Auction Options ${k} must be only string`);
268+
return false
269+
}
270+
}
271+
}
272+
return true;
273+
}
240274
}
241275

242276
/**

test/spec/auctionmanager_spec.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,4 +1282,119 @@ describe('auctionmanager.js', function () {
12821282
assert.equal(doneSpy.callCount, 1);
12831283
})
12841284
});
1285+
1286+
describe('auctionOptions', function() {
1287+
let bidRequests;
1288+
let doneSpy;
1289+
let clock;
1290+
let auction = {
1291+
getBidRequests: () => bidRequests,
1292+
getAuctionId: () => '1',
1293+
addBidReceived: () => true,
1294+
getTimeout: () => 1000
1295+
}
1296+
let requiredBidder = BIDDER_CODE;
1297+
let requiredBidder1 = BIDDER_CODE1;
1298+
let secondaryBidder = 'doNotWaitForMe';
1299+
1300+
beforeEach(() => {
1301+
clock = sinon.useFakeTimers();
1302+
doneSpy = sinon.spy();
1303+
config.setConfig({
1304+
'auctionOptions': {
1305+
secondaryBidders: [ secondaryBidder ]
1306+
}
1307+
})
1308+
});
1309+
1310+
afterEach(() => {
1311+
doneSpy.resetHistory();
1312+
config.resetConfig();
1313+
clock.restore();
1314+
});
1315+
1316+
it('should not wait to call auction done for secondary bidders', function () {
1317+
let bids1 = [mockBid({ bidderCode: requiredBidder })];
1318+
let bids2 = [mockBid({ bidderCode: requiredBidder1 })];
1319+
let bids3 = [mockBid({ bidderCode: secondaryBidder })];
1320+
bidRequests = [
1321+
mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }),
1322+
mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }),
1323+
mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }),
1324+
];
1325+
let cbs = auctionCallbacks(doneSpy, auction);
1326+
// required bidder responds immeaditely to auction
1327+
cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE1, bids1[0]);
1328+
cbs.adapterDone.call(bidRequests[0]);
1329+
assert.equal(doneSpy.callCount, 0);
1330+
1331+
// auction waits for second required bidder to respond
1332+
clock.tick(100);
1333+
cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids2[0]);
1334+
cbs.adapterDone.call(bidRequests[1]);
1335+
1336+
// auction done is reported and does not wait for secondaryBidder request
1337+
assert.equal(doneSpy.callCount, 1);
1338+
1339+
cbs.addBidResponse.call(bidRequests[2], ADUNIT_CODE1, bids3[0]);
1340+
cbs.adapterDone.call(bidRequests[2]);
1341+
});
1342+
1343+
it('should wait for all bidders if they are all secondary', function () {
1344+
config.setConfig({
1345+
'auctionOptions': {
1346+
secondaryBidders: [requiredBidder, requiredBidder1, secondaryBidder]
1347+
}
1348+
})
1349+
let bids1 = [mockBid({ bidderCode: requiredBidder })];
1350+
let bids2 = [mockBid({ bidderCode: requiredBidder1 })];
1351+
let bids3 = [mockBid({ bidderCode: secondaryBidder })];
1352+
bidRequests = [
1353+
mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }),
1354+
mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }),
1355+
mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }),
1356+
];
1357+
let cbs = auctionCallbacks(doneSpy, auction);
1358+
cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE1, bids1[0]);
1359+
cbs.adapterDone.call(bidRequests[0]);
1360+
clock.tick(100);
1361+
assert.equal(doneSpy.callCount, 0)
1362+
1363+
cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids2[0]);
1364+
cbs.adapterDone.call(bidRequests[1]);
1365+
clock.tick(100);
1366+
assert.equal(doneSpy.callCount, 0);
1367+
1368+
cbs.addBidResponse.call(bidRequests[2], ADUNIT_CODE1, bids3[0]);
1369+
cbs.adapterDone.call(bidRequests[2]);
1370+
assert.equal(doneSpy.callCount, 1);
1371+
});
1372+
1373+
it('should allow secondaryBidders to respond in auction before is is done', function () {
1374+
let bids1 = [mockBid({ bidderCode: requiredBidder })];
1375+
let bids2 = [mockBid({ bidderCode: requiredBidder1 })];
1376+
let bids3 = [mockBid({ bidderCode: secondaryBidder })];
1377+
bidRequests = [
1378+
mockBidRequest(bids1[0], { adUnitCode: ADUNIT_CODE1 }),
1379+
mockBidRequest(bids2[0], { adUnitCode: ADUNIT_CODE1 }),
1380+
mockBidRequest(bids3[0], { adUnitCode: ADUNIT_CODE1 }),
1381+
];
1382+
let cbs = auctionCallbacks(doneSpy, auction);
1383+
// secondaryBidder is first to respond
1384+
cbs.addBidResponse.call(bidRequests[2], ADUNIT_CODE1, bids3[0]);
1385+
cbs.adapterDone.call(bidRequests[2]);
1386+
clock.tick(100);
1387+
assert.equal(doneSpy.callCount, 0);
1388+
1389+
cbs.addBidResponse.call(bidRequests[1], ADUNIT_CODE1, bids2[0]);
1390+
cbs.adapterDone.call(bidRequests[1]);
1391+
clock.tick(100);
1392+
assert.equal(doneSpy.callCount, 0);
1393+
1394+
// first required bidder takes longest to respond, auction isn't marked as done until this occurs
1395+
cbs.addBidResponse.call(bidRequests[0], ADUNIT_CODE1, bids1[0]);
1396+
cbs.adapterDone.call(bidRequests[0]);
1397+
assert.equal(doneSpy.callCount, 1);
1398+
});
1399+
});
12851400
});

test/spec/config_spec.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,37 @@ describe('config API', function () {
211211
setConfig({ bidderSequence: 'random' });
212212
expect(logWarnSpy.called).to.equal(false);
213213
});
214+
215+
it('sets auctionOptions', function () {
216+
const auctionOptionsConfig = {
217+
'secondaryBidders': ['rubicon', 'appnexus']
218+
}
219+
setConfig({ auctionOptions: auctionOptionsConfig });
220+
expect(getConfig('auctionOptions')).to.eql(auctionOptionsConfig);
221+
});
222+
223+
it('should log warning for the wrong value passed to auctionOptions', function () {
224+
setConfig({ auctionOptions: '' });
225+
expect(logWarnSpy.calledOnce).to.equal(true);
226+
const warning = 'Auction Options must be an object';
227+
assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged');
228+
});
229+
230+
it('should log warning for invalid auctionOptions bidder values', function () {
231+
setConfig({ auctionOptions: {
232+
'secondaryBidders': 'appnexus, rubicon',
233+
}});
234+
expect(logWarnSpy.calledOnce).to.equal(true);
235+
const warning = 'Auction Options secondaryBidders must be of type Array';
236+
assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged');
237+
});
238+
239+
it('should log warning for invalid properties to auctionOptions', function () {
240+
setConfig({ auctionOptions: {
241+
'testing': true
242+
}});
243+
expect(logWarnSpy.calledOnce).to.equal(true);
244+
const warning = 'Auction Options given an incorrect param: testing';
245+
assert.ok(logWarnSpy.calledWith(warning), 'expected warning was logged');
246+
});
214247
});

0 commit comments

Comments
 (0)