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

Core & multiple modules: introduce new bidRejected event, and refactor bid rejection logic #9013

Merged
merged 10 commits into from
Oct 11, 2022
8 changes: 4 additions & 4 deletions modules/categoryTranslation.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ export const registerAdserver = hook('async', function(adServer) {
}, 'registerAdserver');
registerAdserver();

export const getAdserverCategoryHook = timedBidResponseHook('categoryTranslation', function getAdserverCategoryHook(fn, adUnitCode, bid) {
export const getAdserverCategoryHook = timedBidResponseHook('categoryTranslation', function getAdserverCategoryHook(fn, adUnitCode, bid, reject) {
if (!bid) {
return fn.call(this, adUnitCode); // if no bid, call original and let it display warnings
return fn.call(this, adUnitCode, bid, reject); // if no bid, call original and let it display warnings
}

if (!config.getConfig('adpod.brandCategoryExclusion')) {
return fn.call(this, adUnitCode, bid);
return fn.call(this, adUnitCode, bid, reject);
}

let localStorageKey = (config.getConfig('brandCategoryTranslation.translationFile')) ? DEFAULT_IAB_TO_FW_MAPPING_KEY_PUB : DEFAULT_IAB_TO_FW_MAPPING_KEY;
Expand All @@ -63,7 +63,7 @@ export const getAdserverCategoryHook = timedBidResponseHook('categoryTranslation
logError('Translation mapping data not found in local storage');
}
}
fn.call(this, adUnitCode, bid);
fn.call(this, adUnitCode, bid, reject);
});

export function initTranslation(url, localStorageKey) {
Expand Down
22 changes: 11 additions & 11 deletions modules/currency.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { logInfo, logWarn, logError, logMessage } from '../src/utils.js';
import { getGlobal } from '../src/prebidGlobal.js';
import { createBid } from '../src/bidfactory.js';
import {logError, logInfo, logMessage, logWarn} from '../src/utils.js';
import {getGlobal} from '../src/prebidGlobal.js';
import CONSTANTS from '../src/constants.json';
import { ajax } from '../src/ajax.js';
import { config } from '../src/config.js';
import { getHook } from '../src/hook.js';
import {ajax} from '../src/ajax.js';
import {config} from '../src/config.js';
import {getHook} from '../src/hook.js';
import {defer} from '../src/utils/promise.js';
import {timedBidResponseHook} from '../src/utils/perfMetrics.js';

Expand Down Expand Up @@ -181,9 +180,9 @@ function resetCurrency() {
bidderCurrencyDefault = {};
}

export const addBidResponseHook = timedBidResponseHook('currency', function addBidResponseHook(fn, adUnitCode, bid) {
export const addBidResponseHook = timedBidResponseHook('currency', function addBidResponseHook(fn, adUnitCode, bid, reject) {
if (!bid) {
return fn.call(this, adUnitCode); // if no bid, call original and let it display warnings
return fn.call(this, adUnitCode, bid, reject); // if no bid, call original and let it display warnings
}

let bidder = bid.bidderCode || bid.bidder;
Expand All @@ -209,10 +208,10 @@ export const addBidResponseHook = timedBidResponseHook('currency', function addB

// execute immediately if the bid is already in the desired currency
if (bid.currency === adServerCurrency) {
return fn.call(this, adUnitCode, bid);
return fn.call(this, adUnitCode, bid, reject);
}

bidResponseQueue.push(wrapFunction(fn, this, [adUnitCode, bid]));
bidResponseQueue.push(wrapFunction(fn, this, [adUnitCode, bid, reject]));
if (!currencySupportEnabled || currencyRatesLoaded) {
processBidResponseQueue();
} else {
Expand All @@ -239,7 +238,8 @@ function wrapFunction(fn, context, params) {
}
} catch (e) {
logWarn('Returning NO_BID, getCurrencyConversion threw error: ', e);
params[1] = createBid(CONSTANTS.STATUS.NO_BID, bid.getIdentifiers());
// TODO: in v8, this should not continue with a "NO_BID"
params[1] = params[2](CONSTANTS.REJECTION_REASON.CANNOT_CONVERT_CURRENCY);
}
}
return fn.apply(context, params);
Expand Down
4 changes: 2 additions & 2 deletions modules/dchain.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function isValidDchain(bid) {
}
}

export const addBidResponseHook = timedBidResponseHook('dchain', function addBidResponseHook(fn, adUnitCode, bid) {
export const addBidResponseHook = timedBidResponseHook('dchain', function addBidResponseHook(fn, adUnitCode, bid, reject) {
const basicDchain = {
ver: '1.0',
complete: 0,
Expand Down Expand Up @@ -140,7 +140,7 @@ export const addBidResponseHook = timedBidResponseHook('dchain', function addBid
bid.meta.dchain = basicDchain;
}

fn(adUnitCode, bid);
fn(adUnitCode, bid, reject);
});

export function init() {
Expand Down
4 changes: 2 additions & 2 deletions modules/debugging/legacy.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function applyBidOverrides(overrideObj, bidObj, bidType, logger) {
}, bidObj);
}

export function addBidResponseHook(next, adUnitCode, bid) {
export function addBidResponseHook(next, adUnitCode, bid, reject) {
const {overrides, logger} = this;

if (bidderExcluded(overrides.bidders, bid.bidderCode)) {
Expand All @@ -70,7 +70,7 @@ export function addBidResponseHook(next, adUnitCode, bid) {
});
}

next(adUnitCode, bid);
next(adUnitCode, bid, reject);
}

export function addBidderRequestsHook(next, bidderRequests) {
Expand Down
4 changes: 2 additions & 2 deletions modules/mass.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export function updateRenderers() {
/**
* Before hook for 'addBidResponse'.
*/
export const addBidResponseHook = timedBidResponseHook('mass', function addBidResponseHook(next, adUnitCode, bid, {index = auctionManager.index} = {}) {
export const addBidResponseHook = timedBidResponseHook('mass', function addBidResponseHook(next, adUnitCode, bid, reject, {index = auctionManager.index} = {}) {
let renderer;
for (let i = 0; i < renderers.length; i++) {
if (renderers[i].match(bid)) {
Expand All @@ -104,7 +104,7 @@ export const addBidResponseHook = timedBidResponseHook('mass', function addBidRe
addListenerOnce();
}

next(adUnitCode, bid);
next(adUnitCode, bid, reject);
});

/**
Expand Down
10 changes: 5 additions & 5 deletions modules/multibid/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,15 @@ export function adjustBidderRequestsHook(fn, bidderRequests) {
* @param {String} ad unit code for bid
* @param {Object} bid object
*/
export const addBidResponseHook = timedBidResponseHook('multibid', function addBidResponseHook(fn, adUnitCode, bid) {
export const addBidResponseHook = timedBidResponseHook('multibid', function addBidResponseHook(fn, adUnitCode, bid, reject) {
let floor = deepAccess(bid, 'floorData.floorValue');

if (!config.getConfig('multibid')) resetMultiConfig();
// Checks if multiconfig exists and bid bidderCode exists within config and is an adpod bid
// Else checks if multiconfig exists and bid bidderCode exists within config
// Else continue with no modifications
if (hasMultibid && multiConfig[bid.bidderCode] && deepAccess(bid, 'video.context') === 'adpod') {
fn.call(this, adUnitCode, bid);
fn.call(this, adUnitCode, bid, reject);
} else if (hasMultibid && multiConfig[bid.bidderCode]) {
// Set property multibidPrefix on bid
if (multiConfig[bid.bidderCode].prefix) bid.multibidPrefix = multiConfig[bid.bidderCode].prefix;
Expand All @@ -127,7 +127,7 @@ export const addBidResponseHook = timedBidResponseHook('multibid', function addB
if (multiConfig[bid.bidderCode].prefix) bid.targetingBidder = multiConfig[bid.bidderCode].prefix + length;
if (length === multiConfig[bid.bidderCode].maxbids) multibidUnits[adUnitCode][bid.bidderCode].maxReached = true;

fn.call(this, adUnitCode, bid);
fn.call(this, adUnitCode, bid, reject);
} else {
logWarn(`Filtering multibid received from bidder ${bid.bidderCode}: ` + ((multibidUnits[adUnitCode][bid.bidderCode].maxReached) ? `Maximum bid limit reached for ad unit code ${adUnitCode}` : 'Bid cpm under floors value.'));
}
Expand All @@ -137,10 +137,10 @@ export const addBidResponseHook = timedBidResponseHook('multibid', function addB
deepSetValue(multibidUnits, [adUnitCode, bid.bidderCode], {ads: [bid]});
if (multibidUnits[adUnitCode][bid.bidderCode].ads.length === multiConfig[bid.bidderCode].maxbids) multibidUnits[adUnitCode][bid.bidderCode].maxReached = true;

fn.call(this, adUnitCode, bid);
fn.call(this, adUnitCode, bid, reject);
}
} else {
fn.call(this, adUnitCode, bid);
fn.call(this, adUnitCode, bid, reject);
}
});

Expand Down
16 changes: 10 additions & 6 deletions modules/prebidServerBidAdapter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -919,10 +919,6 @@ Object.assign(ORTB2.prototype, {
(seatbid.bid || []).forEach(bid => {
let bidRequest = this.getBidRequest(bid.impid, seatbid.seat);
if (bidRequest == null) {
if (!s2sConfig.allowUnknownBidderCodes) {
logWarn(`PBS adapter received bid from unknown bidder (${seatbid.seat}), but 's2sConfig.allowUnknownBidderCodes' is not set. Ignoring bid.`);
return;
}
// for stored impression, a request was made with bidder code `null`. Pick it up here so that NO_BID, BID_WON, etc events
// can work as expected (otherwise, the original request will always result in NO_BID).
bidRequest = this.getBidRequest(bid.impid, null);
Expand All @@ -937,6 +933,7 @@ Object.assign(ORTB2.prototype, {
transactionId: this.adUnitsByImp[bid.impid].transactionId,
auctionId: this.auctionId,
});
bidObject.requestBidder = bidRequest?.bidder;
bidObject.requestTimestamp = this.requestTimestamp;
bidObject.cpm = cpm;
if (bid?.ext?.prebid?.meta?.adaptercode) {
Expand Down Expand Up @@ -1127,8 +1124,15 @@ export function PrebidServer() {
onBid: function ({adUnit, bid}) {
const metrics = bid.metrics = s2sBidRequest.metrics.fork().renameWith();
metrics.checkpoint('addBidResponse');
if (metrics.measureTime('addBidResponse.validate', () => isValid(adUnit, bid))) {
addBidResponse(adUnit, bid);
if ((bid.requestId == null || bid.requestBidder == null) && !s2sBidRequest.s2sConfig.allowUnknownBidderCodes) {
logWarn(`PBS adapter received bid from unknown bidder (${bid.bidder}), but 's2sConfig.allowUnknownBidderCodes' is not set. Ignoring bid.`);
addBidResponse.reject(adUnit, bid, CONSTANTS.REJECTION_REASON.BIDDER_DISALLOWED);
} else {
if (metrics.measureTime('addBidResponse.validate', () => isValid(adUnit, bid))) {
addBidResponse(adUnit, bid);
} else {
addBidResponse.reject(adUnit, bid, CONSTANTS.REJECTION_REASON.INVALID);
}
}
}
})
Expand Down
30 changes: 8 additions & 22 deletions modules/priceFloors.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {ajaxBuilder} from '../src/ajax.js';
import * as events from '../src/events.js';
import CONSTANTS from '../src/constants.json';
import {getHook} from '../src/hook.js';
import {createBid} from '../src/bidfactory.js';
import {find} from '../src/polyfill.js';
import {getRefererInfo} from '../src/refererDetection.js';
import {bidderSettings} from '../src/bidderSettings.js';
Expand Down Expand Up @@ -694,11 +693,11 @@ function shouldFloorBid(floorData, floorInfo, bid) {
* @summary The main driving force of floors. On bidResponse we hook in and intercept bidResponses.
* And if the rule we find determines a bid should be floored we will do so.
*/
export const addBidResponseHook = timedBidResponseHook('priceFloors', function addBidResponseHook(fn, adUnitCode, bid) {
export const addBidResponseHook = timedBidResponseHook('priceFloors', function addBidResponseHook(fn, adUnitCode, bid, reject) {
let floorData = _floorDataForAuction[bid.auctionId];
// if no floor data then bail
if (!floorData || !bid || floorData.skipped) {
return fn.call(this, adUnitCode, bid);
return fn.call(this, adUnitCode, bid, reject);
}

const matchingBidRequest = auctionManager.index.getBidRequest(bid);
Expand All @@ -708,7 +707,7 @@ export const addBidResponseHook = timedBidResponseHook('priceFloors', function a

if (!floorInfo.matchingFloor) {
logWarn(`${MODULE_NAME}: unable to determine a matching price floor for bidResponse`, bid);
return fn.call(this, adUnitCode, bid);
return fn.call(this, adUnitCode, bid, reject);
}

// determine the base cpm to use based on if the currency matches the floor currency
Expand All @@ -724,7 +723,7 @@ export const addBidResponseHook = timedBidResponseHook('priceFloors', function a
adjustedCpm = getGlobal().convertCurrency(bid.cpm, bidResponseCurrency.toUpperCase(), floorCurrency);
} catch (err) {
logError(`${MODULE_NAME}: Unable do get currency conversion for bidResponse to Floor Currency. Do you have Currency module enabled? ${bid}`);
return fn.call(this, adUnitCode, bid);
return fn.call(this, adUnitCode, bid, reject);
}
}

Expand All @@ -737,25 +736,12 @@ export const addBidResponseHook = timedBidResponseHook('priceFloors', function a
// now do the compare!
if (shouldFloorBid(floorData, floorInfo, bid)) {
// bid fails floor -> throw it out
// create basic bid no-bid with necessary data fro analytics adapters
let flooredBid = createBid(CONSTANTS.STATUS.NO_BID, bid.getIdentifiers());
Object.assign(flooredBid, pick(bid, [
'floorData',
'width',
'height',
'mediaType',
'currency',
'originalCpm',
'originalCurrency',
'getCpmInNewCurrency',
]));
flooredBid.status = CONSTANTS.BID_STATUS.BID_REJECTED;
// if floor not met update bid with 0 cpm so it is not included downstream and marked as no-bid
flooredBid.cpm = 0;
// continue with a "NO_BID" bid, TODO: remove this in v8
const flooredBid = reject(CONSTANTS.REJECTION_REASON.FLOOR_NOT_MET);
logWarn(`${MODULE_NAME}: ${flooredBid.bidderCode}'s Bid Response for ${adUnitCode} was rejected due to floor not met (adjusted cpm: ${bid?.floorData?.cpmAfterAdjustments}, floor: ${floorInfo?.matchingFloor})`, bid);
return fn.call(this, adUnitCode, flooredBid);
return fn.call(this, adUnitCode, flooredBid, reject);
}
return fn.call(this, adUnitCode, bid);
return fn.call(this, adUnitCode, bid, reject);
});

config.getConfig('floors', config => handleSetFloorsConfig(config.floors));
24 changes: 13 additions & 11 deletions src/adapters/bidderFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ export function newBidder(spec) {
adUnitCodesHandled[adUnitCode] = true;
if (metrics.measureTime('addBidResponse.validate', () => isValid(adUnitCode, bid))) {
addBidResponse(adUnitCode, bid);
} else {
addBidResponse.reject(adUnitCode, bid, CONSTANTS.REJECTION_REASON.INVALID)
}
}

Expand Down Expand Up @@ -262,6 +264,7 @@ export function newBidder(spec) {
bid.adapterCode = bidRequest.bidder;
if (isInvalidAlternateBidder(bid.bidderCode, bidRequest.bidder)) {
logWarn(`${bid.bidderCode} is not a registered partner or known bidder of ${bidRequest.bidder}, hence continuing without bid. If you wish to support this bidder, please mark allowAlternateBidderCodes as true in bidderSettings.`);
addBidResponse.reject(bidRequest.adUnitCode, bid, CONSTANTS.REJECTION_REASON.BIDDER_DISALLOWED)
return;
}
// creating a copy of original values as cpm and currency are modified later
Expand All @@ -272,6 +275,7 @@ export function newBidder(spec) {
addBidWithCode(bidRequest.adUnitCode, prebidBid);
} else {
logWarn(`Bidder ${spec.code} made bid for unknown request ID: ${bid.requestId}. Ignoring.`);
addBidResponse.reject(null, bid, CONSTANTS.REJECTION_REASON.INVALID_REQUEST_ID);
}
},
onCompletion: afterAllResponses,
Expand Down Expand Up @@ -541,24 +545,22 @@ export function getIabSubCategory(bidderCode, category) {

// check that the bid has a width and height set
function validBidSize(adUnitCode, bid, {index = auctionManager.index} = {}) {
if ((bid.width || parseInt(bid.width, 10) === 0) && (bid.height || parseInt(bid.height, 10) === 0)) {
bid.width = parseInt(bid.width, 10);
bid.height = parseInt(bid.height, 10);
return true;
}

const bidRequest = index.getBidRequest(bid);
const mediaTypes = index.getMediaTypes(bid);

const sizes = (bidRequest && bidRequest.sizes) || (mediaTypes && mediaTypes.banner && mediaTypes.banner.sizes);
const parsedSizes = parseSizesInput(sizes);
const parsedSizes = parseSizesInput(sizes).map(sz => sz.split('x').map(n => parseInt(n, 10)));

if ((bid.width || parseInt(bid.width, 10) === 0) && (bid.height || parseInt(bid.height, 10) === 0)) {
bid.width = parseInt(bid.width, 10);
bid.height = parseInt(bid.height, 10);
return parsedSizes.length === 0 || parsedSizes.some(([w, h]) => bid.width === w && bid.height === h);
}

// if a banner impression has one valid size, we assign that size to any bid
// response that does not explicitly set width or height
if (parsedSizes.length === 1) {
const [ width, height ] = parsedSizes[0].split('x');
bid.width = parseInt(width, 10);
bid.height = parseInt(height, 10);
([bid.width, bid.height] = parsedSizes[0]);
return true;
}

Expand Down Expand Up @@ -600,7 +602,7 @@ export function isValid(adUnitCode, bid, {index = auctionManager.index} = {}) {
return false;
}
if (bid.mediaType === 'banner' && !validBidSize(adUnitCode, bid, {index})) {
logError(errorMessage(`Banner bids require a width and height`));
logError(errorMessage(`Banner bids require a width and height that match one of the requested sizes`));
return false;
}

Expand Down
Loading