From 221fafc4284c13bcc355a65e16d5acaf899f6e61 Mon Sep 17 00:00:00 2001 From: harpere Date: Tue, 1 Feb 2022 10:41:11 -0500 Subject: [PATCH] add a configurable "bidCacheFilterFunction" (#7993) * add a configurable "bidCacheFilterFunction" to determine whether to use a cached bid * tiny != changed to !== Co-authored-by: Eric Harper --- src/targeting.js | 8 +++ test/fixtures/fixtures.js | 3 +- test/spec/unit/core/targeting_spec.js | 93 +++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/targeting.js b/src/targeting.js index 77dcaa3ee9f..b61d2552853 100644 --- a/src/targeting.js +++ b/src/targeting.js @@ -438,7 +438,15 @@ export function newTargeting(auctionManager) { let bidsReceived = auctionManager.getBidsReceived(); if (!config.getConfig('useBidCache')) { + // don't use bid cache (i.e. filter out bids not in the latest auction) bidsReceived = bidsReceived.filter(bid => latestAuctionForAdUnit[bid.adUnitCode] === bid.auctionId) + } else { + // if custom bid cache filter function exists, run for each bid from + // previous auctions. If it returns true, include bid in bid pool + const filterFunction = config.getConfig('bidCacheFilterFunction'); + if (typeof filterFunction === 'function') { + bidsReceived = bidsReceived.filter(bid => latestAuctionForAdUnit[bid.adUnitCode] === bid.auctionId || !!filterFunction(bid)) + } } bidsReceived = bidsReceived diff --git a/test/fixtures/fixtures.js b/test/fixtures/fixtures.js index 908382f8daa..b0fbd7da806 100644 --- a/test/fixtures/fixtures.js +++ b/test/fixtures/fixtures.js @@ -1231,7 +1231,7 @@ export function getCurrencyRates() { }; } -export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, adUnitCode, adId, status, ttl, requestId}) { +export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, adUnitCode, adId, status, ttl, requestId, mediaType}) { let bid = { 'bidderCode': bidder, 'width': '300', @@ -1259,6 +1259,7 @@ export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, ad 'hb_pb': cpm, 'foobar': '300x250' }), + 'mediaType': mediaType, 'netRevenue': true, 'currency': 'USD', 'ttl': (!ttl) ? 300 : ttl diff --git a/test/spec/unit/core/targeting_spec.js b/test/spec/unit/core/targeting_spec.js index 4eaf414bf85..53aa3b90fa8 100644 --- a/test/spec/unit/core/targeting_spec.js +++ b/test/spec/unit/core/targeting_spec.js @@ -228,6 +228,8 @@ describe('targeting tests', function () { let sandbox; let enableSendAllBids = false; let useBidCache; + let bidCacheFilterFunction; + let undef; beforeEach(function() { sandbox = sinon.sandbox.create(); @@ -242,12 +244,16 @@ describe('targeting tests', function () { if (key === 'useBidCache') { return useBidCache; } + if (key === 'bidCacheFilterFunction') { + return bidCacheFilterFunction; + } return origGetConfig.apply(config, arguments); }); }); afterEach(function () { sandbox.restore(); + bidCacheFilterFunction = undef; }); describe('getAllTargeting', function () { @@ -901,6 +907,93 @@ describe('targeting tests', function () { expect(bids[0].adId).to.equal('adid-2'); }); + it('should use bidCacheFilterFunction', function() { + auctionManagerStub.returns([ + createBidReceived({bidder: 'appnexus', cpm: 7, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1', mediaType: 'banner'}), + createBidReceived({bidder: 'appnexus', cpm: 5, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-0', adId: 'adid-2', mediaType: 'banner'}), + createBidReceived({bidder: 'appnexus', cpm: 6, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-1', adId: 'adid-3', mediaType: 'banner'}), + createBidReceived({bidder: 'appnexus', cpm: 8, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-1', adId: 'adid-4', mediaType: 'banner'}), + createBidReceived({bidder: 'appnexus', cpm: 27, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-2', adId: 'adid-5', mediaType: 'video'}), + createBidReceived({bidder: 'appnexus', cpm: 25, auctionId: 2, responseTimestamp: 102, adUnitCode: 'code-2', adId: 'adid-6', mediaType: 'video'}), + createBidReceived({bidder: 'appnexus', cpm: 26, auctionId: 1, responseTimestamp: 101, adUnitCode: 'code-3', adId: 'adid-7', mediaType: 'video'}), + createBidReceived({bidder: 'appnexus', cpm: 28, auctionId: 2, responseTimestamp: 103, adUnitCode: 'code-3', adId: 'adid-8', mediaType: 'video'}), + ]); + + let adUnitCodes = ['code-0', 'code-1', 'code-2', 'code-3']; + targetingInstance.setLatestAuctionForAdUnit('code-0', 2); + targetingInstance.setLatestAuctionForAdUnit('code-1', 2); + targetingInstance.setLatestAuctionForAdUnit('code-2', 2); + targetingInstance.setLatestAuctionForAdUnit('code-3', 2); + + // Bid Caching On, No Filter Function + useBidCache = true; + bidCacheFilterFunction = undef; + let bids = targetingInstance.getWinningBids(adUnitCodes); + + expect(bids.length).to.equal(4); + expect(bids[0].adId).to.equal('adid-1'); + expect(bids[1].adId).to.equal('adid-4'); + expect(bids[2].adId).to.equal('adid-5'); + expect(bids[3].adId).to.equal('adid-8'); + + // Bid Caching Off, No Filter Function + useBidCache = false; + bidCacheFilterFunction = undef; + bids = targetingInstance.getWinningBids(adUnitCodes); + + expect(bids.length).to.equal(4); + expect(bids[0].adId).to.equal('adid-2'); + expect(bids[1].adId).to.equal('adid-4'); + expect(bids[2].adId).to.equal('adid-6'); + expect(bids[3].adId).to.equal('adid-8'); + + // Bid Caching On AGAIN, No Filter Function (should be same as first time) + useBidCache = true; + bidCacheFilterFunction = undef; + bids = targetingInstance.getWinningBids(adUnitCodes); + + expect(bids.length).to.equal(4); + expect(bids[0].adId).to.equal('adid-1'); + expect(bids[1].adId).to.equal('adid-4'); + expect(bids[2].adId).to.equal('adid-5'); + expect(bids[3].adId).to.equal('adid-8'); + + // Bid Caching On, with Filter Function to Exclude video + useBidCache = true; + let bcffCalled = 0; + bidCacheFilterFunction = bid => { + bcffCalled++; + return bid.mediaType !== 'video'; + } + bids = targetingInstance.getWinningBids(adUnitCodes); + + expect(bids.length).to.equal(4); + expect(bids[0].adId).to.equal('adid-1'); + expect(bids[1].adId).to.equal('adid-4'); + expect(bids[2].adId).to.equal('adid-6'); + expect(bids[3].adId).to.equal('adid-8'); + // filter function should have been called for each cached bid (4 times) + expect(bcffCalled).to.equal(4); + + // Bid Caching Off, with Filter Function to Exclude video + // - should not use cached bids or call the filter function + useBidCache = false; + bcffCalled = 0; + bidCacheFilterFunction = bid => { + bcffCalled++; + return bid.mediaType !== 'video'; + } + bids = targetingInstance.getWinningBids(adUnitCodes); + + expect(bids.length).to.equal(4); + expect(bids[0].adId).to.equal('adid-2'); + expect(bids[1].adId).to.equal('adid-4'); + expect(bids[2].adId).to.equal('adid-6'); + expect(bids[3].adId).to.equal('adid-8'); + // filter function should not have been called + expect(bcffCalled).to.equal(0); + }); + it('should not use rendered bid to get winning bid', function () { let bidsReceived = [ createBidReceived({bidder: 'appnexus', cpm: 8, auctionId: 1, responseTimestamp: 100, adUnitCode: 'code-0', adId: 'adid-1', status: 'rendered'}),