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

new NO_BID event and "no bids" available from auction #3286

Merged
merged 1 commit into from
Dec 17, 2018
Merged
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
2 changes: 2 additions & 0 deletions src/AnalyticsAdapter.js
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ const {
BID_REQUESTED,
BID_TIMEOUT,
BID_RESPONSE,
NO_BID,
BID_WON,
BID_ADJUSTMENT,
BIDDER_DONE,
@@ -100,6 +101,7 @@ export default function AnalyticsAdapter({ url, analyticsType, global, handler }
_handlers = {
[BID_REQUESTED]: args => this.enqueue({ eventType: BID_REQUESTED, args }),
[BID_RESPONSE]: args => this.enqueue({ eventType: BID_RESPONSE, args }),
[NO_BID]: args => this.enqueue({ eventType: NO_BID, args }),
[BID_TIMEOUT]: args => this.enqueue({ eventType: BID_TIMEOUT, args }),
[BID_WON]: args => this.enqueue({ eventType: BID_WON, args }),
[BID_ADJUSTMENT]: args => this.enqueue({ eventType: BID_ADJUSTMENT, args }),
14 changes: 9 additions & 5 deletions src/adaptermanager.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** @module adaptermanger */

import { flatten, getBidderCodes, getDefinedParams, shuffle, timestamp } from './utils';
import { flatten, getBidderCodes, getDefinedParams, shuffle, timestamp, getBidderRequest } from './utils';
import { getLabels, resolveStatus } from './sizeMapping';
import { processNativeAdUnitParams, nativeAdapters } from './native';
import { newBidder } from './adapters/bidderFactory';
@@ -340,7 +340,7 @@ exports.callBids = (adUnits, bidRequests, addBidResponse, doneCb, requestCallbac
if (s2sBidRequest.ad_units.length) {
let doneCbs = serverBidRequests.map(bidRequest => {
bidRequest.start = timestamp();
return doneCb;
return doneCb.bind(bidRequest);
});

// only log adapters that actually have adUnit bids
@@ -360,7 +360,12 @@ exports.callBids = (adUnits, bidRequests, addBidResponse, doneCb, requestCallbac
s2sAdapter.callBids(
s2sBidRequest,
serverBidRequests,
addBidResponse,
function(adUnitCode, bid) {
let bidderRequest = getBidderRequest(serverBidRequests, bid.bidderCode, adUnitCode);
if (bidderRequest) {
addBidResponse.call(bidderRequest, adUnitCode, bid)
}
},
() => doneCbs.forEach(done => done()),
s2sAjax
);
@@ -375,12 +380,11 @@ exports.callBids = (adUnits, bidRequests, addBidResponse, doneCb, requestCallbac
const adapter = _bidderRegistry[bidRequest.bidderCode];
utils.logMessage(`CALLING BIDDER ======= ${bidRequest.bidderCode}`);
events.emit(CONSTANTS.EVENTS.BID_REQUESTED, bidRequest);
bidRequest.doneCbCallCount = 0;
let ajax = ajaxBuilder(requestBidsTimeout, requestCallbacks ? {
request: requestCallbacks.request.bind(null, bidRequest.bidderCode),
done: requestCallbacks.done
} : undefined);
adapter.callBids(bidRequest, addBidResponse, doneCb, ajax);
adapter.callBids(bidRequest, addBidResponse.bind(bidRequest), doneCb.bind(bidRequest), ajax);
});
}

2 changes: 1 addition & 1 deletion src/adapters/bidderFactory.js
Original file line number Diff line number Diff line change
@@ -176,7 +176,7 @@ export function newBidder(spec) {
// After all the responses have come back, call done() and
// register any required usersync pixels.
const responses = [];
function afterAllResponses(bids) {
function afterAllResponses() {
done();
events.emit(CONSTANTS.EVENTS.BIDDER_DONE, bidderRequest);
registerSyncs(responses, bidderRequest.gdprConsent);
57 changes: 38 additions & 19 deletions src/auction.js
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@
* @property {function(): void} callBids - sends requests to all adapters for bids
*/

import { uniques, flatten, timestamp, adUnitsFilter, getBidderRequest, deepAccess, delayExecution, getBidRequest } from './utils';
import { uniques, flatten, timestamp, adUnitsFilter, deepAccess, getBidRequest } from './utils';
import { getPriceBucketString } from './cpmBucketManager';
import { getNativeTargeting } from './native';
import { getCacheUrl, store } from './videoCache';
@@ -95,6 +95,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels})
let _adUnitCodes = adUnitCodes;
let _bidderRequests = [];
let _bidsReceived = [];
let _noBids = [];
let _auctionStart;
let _auctionEnd;
let _auctionId = utils.generateUUID();
@@ -106,6 +107,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels})

function addBidRequests(bidderRequests) { _bidderRequests = _bidderRequests.concat(bidderRequests) };
function addBidReceived(bidsReceived) { _bidsReceived = _bidsReceived.concat(bidsReceived); }
function addNoBid(noBid) { _noBids = _noBids.concat(noBid); }

function getProperties() {
return {
@@ -117,6 +119,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels})
adUnitCodes: _adUnitCodes,
labels: _labels,
bidderRequests: _bidderRequests,
noBids: _noBids,
bidsReceived: _bidsReceived,
winningBids: _winningBids,
timeout: _timeout
@@ -175,7 +178,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels})
}
}

function auctionDone(bidderCount) {
function auctionDone() {
// when all bidders have called done callback atleast once it means auction is complete
utils.logInfo(`Bids Received for Auction with id: ${_auctionId}`, _bidsReceived);
_auctionStatus = AUCTION_COMPLETED;
@@ -208,10 +211,12 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels})
events.emit(CONSTANTS.EVENTS.AUCTION_INIT, getProperties());

let callbacks = auctionCallbacks(auctionDone, this);
let boundObj = {
auctionAddBidResponse: callbacks.addBidResponse
};
adaptermanager.callBids(_adUnits, bidRequests, addBidResponse.bind(boundObj), callbacks.adapterDone, {
adaptermanager.callBids(_adUnits, bidRequests, function(...args) {
addBidResponse.apply({
dispatch: callbacks.addBidResponse,
bidderRequest: this
}, args)
}, callbacks.adapterDone, {
request(source, origin) {
increment(outstandingRequests, origin);
increment(requests, source);
@@ -296,6 +301,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels})

return {
addBidReceived,
addNoBid,
executeCallback,
callBids,
addWinningBid,
@@ -308,20 +314,19 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels})
getAdUnitCodes: () => _adUnitCodes,
getBidRequests: () => _bidderRequests,
getBidsReceived: () => _bidsReceived,
getNoBids: () => _noBids
}
}

export const addBidResponse = createHook('asyncSeries', function(adUnitCode, bid) {
this.auctionAddBidResponse(adUnitCode, bid);
this.dispatch.call(this.bidderRequest, adUnitCode, bid);
}, 'addBidResponse');

export function auctionCallbacks(auctionDone, auctionInstance) {
let outstandingBidsAdded = 0;
let allAdapterCalledDone = false;

let onAllAdapterDone = delayExecution(() => {
allAdapterCalledDone = true;
}, auctionInstance.getBidRequests().length);
let bidderRequestsDone = new Set();
let bidResponseMap = {};

function afterBidAdded() {
outstandingBidsAdded--;
@@ -331,23 +336,37 @@ export function auctionCallbacks(auctionDone, auctionInstance) {
}

function addBidResponse(adUnitCode, bid) {
let bidderRequest = this;

bidResponseMap[bid.requestId] = true;

outstandingBidsAdded++;
let bidRequests = auctionInstance.getBidRequests();
let auctionId = auctionInstance.getAuctionId();

let bidRequest = getBidderRequest(bidRequests, bid.bidderCode, adUnitCode);
let bidResponse = getPreparedBidForAuction({adUnitCode, bid, bidRequest, auctionId});
let bidResponse = getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId});

if (bidResponse.mediaType === 'video') {
tryAddVideoBid(auctionInstance, bidResponse, bidRequest, afterBidAdded);
tryAddVideoBid(auctionInstance, bidResponse, bidderRequest, afterBidAdded);
} else {
addBidToAuction(auctionInstance, bidResponse);
afterBidAdded();
}
}

function adapterDone() {
onAllAdapterDone();
let bidderRequest = this;

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

bidderRequest.bids.forEach(bid => {
if (!bidResponseMap[bid.bidId]) {
auctionInstance.addNoBid(bid);
events.emit(CONSTANTS.EVENTS.NO_BID, bid);
}
});

if (allAdapterCalledDone && outstandingBidsAdded === 0) {
auctionDone();
}
@@ -412,8 +431,8 @@ function tryAddVideoBid(auctionInstance, bidResponse, bidRequests, afterBidAdded

// Postprocess the bids so that all the universal properties exist, no matter which bidder they came from.
// This should be called before addBidToAuction().
function getPreparedBidForAuction({adUnitCode, bid, bidRequest, auctionId}) {
const start = bidRequest.start;
function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) {
const start = bidderRequest.start;

let bidObject = Object.assign({}, bid, {
auctionId,
@@ -433,7 +452,7 @@ function getPreparedBidForAuction({adUnitCode, bid, bidRequest, auctionId}) {
events.emit(CONSTANTS.EVENTS.BID_ADJUSTMENT, bidObject);

// a publisher-defined renderer can be used to render bids
const bidReq = bidRequest.bids && find(bidRequest.bids, bid => bid.adUnitCode == adUnitCode);
const bidReq = bidderRequest.bids && find(bidderRequest.bids, bid => bid.adUnitCode == adUnitCode);
const adUnitRenderer = bidReq && bidReq.renderer;

if (adUnitRenderer && adUnitRenderer.url) {
9 changes: 7 additions & 2 deletions src/auctionManager.js
Original file line number Diff line number Diff line change
@@ -39,18 +39,23 @@ export function newAuctionManager() {
} else {
utils.logWarn(`Auction not found when adding winning bid`);
}
}
};

auctionManager.getAllWinningBids = function() {
return _auctions.map(auction => auction.getWinningBids())
.reduce(flatten, []);
}
};

auctionManager.getBidsRequested = function() {
return _auctions.map(auction => auction.getBidRequests())
.reduce(flatten, []);
};

auctionManager.getNoBids = function() {
return _auctions.map(auction => auction.getNoBids())
.reduce(flatten, []);
};

auctionManager.getBidsReceived = function() {
// As of now, an old bid which is not used in auction 1 can be used in auction n.
// To prevent this, bid.ttl (time to live) will be added to this logic and bid pool will also be added
1 change: 1 addition & 0 deletions src/constants.json
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@
"BID_TIMEOUT": "bidTimeout",
"BID_REQUESTED": "bidRequested",
"BID_RESPONSE": "bidResponse",
"NO_BID": "noBid",
"BID_WON": "bidWon",
"BIDDER_DONE": "bidderDone",
"SET_TARGETING": "setTargeting",
33 changes: 24 additions & 9 deletions src/prebid.js
Original file line number Diff line number Diff line change
@@ -114,15 +114,8 @@ $$PREBID_GLOBAL$$.getAdserverTargeting = function (adUnitCode) {
return targeting.getAllTargeting(adUnitCode);
};

/**
* This function returns the bid responses at the given moment.
* @alias module:pbjs.getBidResponses
* @return {Object} map | object that contains the bidResponses
*/

$$PREBID_GLOBAL$$.getBidResponses = function () {
utils.logInfo('Invoking $$PREBID_GLOBAL$$.getBidResponses', arguments);
const responses = auctionManager.getBidsReceived()
function getBids(type) {
const responses = auctionManager[type]()
.filter(adUnitsFilter.bind(this, auctionManager.getAdUnitCodes()));

// find the last auction id to get responses for most recent auction only
@@ -139,6 +132,28 @@ $$PREBID_GLOBAL$$.getBidResponses = function () {
};
})
.reduce((a, b) => Object.assign(a, b), {});
}

/**
* This function returns the bids requests involved in an auction but not bid on
* @alias module:pbjs.getNoBids
* @return {Object} map | object that contains the bidRequests
*/

$$PREBID_GLOBAL$$.getNoBids = function () {
utils.logInfo('Invoking $$PREBID_GLOBAL$$.getNoBids', arguments);
return getBids('getNoBids');
};

/**
* This function returns the bid responses at the given moment.
* @alias module:pbjs.getBidResponses
* @return {Object} map | object that contains the bidResponses
*/

$$PREBID_GLOBAL$$.getBidResponses = function () {
utils.logInfo('Invoking $$PREBID_GLOBAL$$.getBidResponses', arguments);
return getBids('getBidsReceived');
};

/**
4 changes: 4 additions & 0 deletions test/spec/api_spec.js
Original file line number Diff line number Diff line change
@@ -39,6 +39,10 @@ describe('Publisher API', function () {
assert.isFunction($$PREBID_GLOBAL$$.getBidResponses);
});

it('should have function $$PREBID_GLOBAL$$.getBidResponses', function () {
assert.isFunction($$PREBID_GLOBAL$$.getNoBids);
});

it('should have function $$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode', function () {
assert.isFunction($$PREBID_GLOBAL$$.getBidResponsesForAdUnitCode);
});
Loading
Oops, something went wrong.