Skip to content

Commit

Permalink
Prebid Core: Refine coordination of PBS+PBJS Floors (#8865)
Browse files Browse the repository at this point in the history
* stronger coordination between pbs and pbjs floors

* reverted changes for test page

* spacing change

* refactored code

* reverted change to adunits html

* refactored pbjs to pbs floormin logic

* refactored pbjs to pbs floormin logic

* removed comment

* refactored code

* updated logic

* no longer ignoring currencies

* added fallback to deprecating prebid.floorMin and repositioned where floorMinCur is set

* progress

* updated floorMinCur logic

* reverted code on example pages

* format adjustmnet

* format adjustment

* format adjustment

* updated currency conversion logic

* updated currency conversion logic

* updated floorMinCur default for ortb2

* resolved conflicts

* formatting

* formatting

* added few comments explaining the logic for this change

Co-authored-by: Jason Quaccia <jasonquaccia@RWC-WS233.local>
  • Loading branch information
jlquaccia and Jason Quaccia authored Oct 6, 2022
1 parent a86b60a commit 17b9b32
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 12 deletions.
55 changes: 43 additions & 12 deletions modules/prebidServerBidAdapter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,8 @@ Object.assign(ORTB2.prototype, {
let imps = [];
let aliases = {};
const firstBidRequest = bidRequests[0];
let floorMin = null;
let floorMinCur = null;

// transform ad unit into array of OpenRTB impression objects
let impIds = new Set();
Expand Down Expand Up @@ -671,21 +673,21 @@ Object.assign(ORTB2.prototype, {

mergeDeep(imp, mediaTypes);

const convertCurrency = typeof getGlobal().convertCurrency !== 'function'
? (amount) => amount
: (amount, from, to) => {
if (from === to) return amount;
let result = null;
try {
result = getGlobal().convertCurrency(amount, from, to);
} catch (e) {
}
return result;
}

const floor = (() => {
// we have to pick a floor for the imp - here we attempt to find the minimum floor
// across all bids for this adUnit

const convertCurrency = typeof getGlobal().convertCurrency !== 'function'
? (amount) => amount
: (amount, from, to) => {
if (from === to) return amount;
let result = null;
try {
result = getGlobal().convertCurrency(amount, from, to);
} catch (e) {
}
return result;
}
const s2sCurrency = config.getConfig('currency.adServerCurrency') || DEFAULT_S2S_CURRENCY;

return adUnit.bids
Expand All @@ -696,6 +698,7 @@ Object.assign(ORTB2.prototype, {
const {currency, floor} = bid.getFloor({
currency: s2sCurrency
});

return {
currency,
floor: parseFloat(floor)
Expand All @@ -717,18 +720,40 @@ Object.assign(ORTB2.prototype, {
min.ref = min.min = floor;
} else {
const value = convertCurrency(floor.floor, floor.currency, min.ref.currency);

if (value != null && value < min.ref.floor) {
min.ref.floor = value;
min.min = floor;
}
}

return min;
}, {}).min
})();

if (floor) {
imp.bidfloor = floor.floor;
imp.bidfloorcur = floor.currency;

// logic below relates to https://github.com/prebid/Prebid.js/issues/8749 and does the following:
// 1. check client-side floors (ref bidfloor/bidfloorcur & ortb2Imp floorMin/floorMinCur (if present))
// 2. set pbs req wide floorMinCur to the first floor currency found when iterating over imp's
// (if currency conversion logic present, convert all imp floor values to this currency)
// 3. compare/store ref to lowest floorMin value as each imp is iterated over
// 4. set req wide floorMin and floorMinCur values for pbs after iterations are done
if (floorMinCur == null) { floorMinCur = floor.currency }
const ortb2ImpFloorMin = imp.ext?.prebid?.floors?.floorMin || imp.ext?.prebid?.floorMin;
const ortb2ImpFloorCur = imp.ext?.prebid?.floors?.floorMinCur || imp.ext?.prebid?.floorMinCur || floorMinCur;

const convertedFloorMinValue = convertCurrency(floor.floor, floor.currency, floorMinCur);
const convertedOrtb2ImpFloorMinValue = ortb2ImpFloorMin && ortb2ImpFloorCur ? convertCurrency(ortb2ImpFloorMin, ortb2ImpFloorCur, floorMinCur) : false;

const lowestImpFloorMin = convertedOrtb2ImpFloorMinValue && convertedOrtb2ImpFloorMinValue < convertedFloorMinValue
? convertedOrtb2ImpFloorMinValue
: convertedFloorMinValue;

deepSetValue(imp, 'ext.prebid.floors.floorMin', lowestImpFloorMin);
if (floorMin == null || floorMin > lowestImpFloorMin) { floorMin = lowestImpFloorMin }
}

if (imp.banner || imp.video || imp.native) {
Expand Down Expand Up @@ -903,6 +928,12 @@ Object.assign(ORTB2.prototype, {
addBidderFirstPartyDataToRequest(request, s2sBidRequest.ortb2Fragments?.bidder || {});

request.imp.forEach((imp) => this.impRequested[imp.id] = imp);

if (request.ext?.prebid?.floors?.enabled) {
request.ext.prebid.floors.floorMin = floorMin;
request.ext.prebid.floors.floorMinCur = floorMinCur;
}

return request;
},

Expand Down
156 changes: 156 additions & 0 deletions test/spec/modules/prebidServerBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3480,5 +3480,161 @@ describe('S2S Adapter', function () {
const parsedRequestBody = JSON.parse(server.requests[1].requestBody);
expect(parsedRequestBody.cur).to.deep.equal(['JPY']);
});

it('should correctly set the floorMin key when multiple bids with various bidfloors exist', function () {
const s2sConfig = Object.assign({}, CONFIG, {
extPrebid: {
floors: {
enabled: true
}
},
bidders: ['b1', 'b2']
});

const bidderRequests = [
{
...BID_REQUESTS[0],
bidderCode: 'b1',
bids: [{
bidder: 'b1',
bidId: 1,
getFloor: () => ({
currency: 'US',
floor: 1.23
})
}]
},
{
...BID_REQUESTS[0],
bidderCode: 'b2',
bids: [{
bidder: 'b2',
bidId: 2,
getFloor: () => ({
currency: 'EUR',
floor: 3.21
})
}],
}
];

const adUnits = [
{
code: 'au1',
transactionId: 't1',
mediaTypes: {
banner: {sizes: [1, 1]}
},
bids: [{bidder: 'b1', bid_id: 1}]
},
{
code: 'au2',
transactionId: 't2',
bids: [{bidder: 'b2', bid_id: 2}],
mediaTypes: {
banner: {sizes: [1, 1]}
}
}
];

const _config = {
s2sConfig: s2sConfig,
};

const s2sBidRequest = utils.deepClone(REQUEST);
s2sBidRequest.s2sConfig = s2sConfig;
s2sBidRequest.ad_units = adUnits;
config.setConfig(_config);

adapter.callBids(s2sBidRequest, bidderRequests, addBidResponse, done, ajax);
const requestBid = JSON.parse(server.requests[0].requestBody);
expect(requestBid.imp[0].bidfloor).to.equal(1.23);
expect(requestBid.imp[1].bidfloor).to.equal(3.21);

// first imp floorCur should be set
expect(requestBid.ext.prebid.floors).to.deep.equal({ enabled: true, floorMin: 1.23, floorMinCur: 'US' });
});

it('should correctly set the floorMin key when multiple bids with various bidfloors exist and ortb2Imp contains the lowest floorMin', function () {
const s2sConfig = Object.assign({}, CONFIG, {
extPrebid: {
floors: {
enabled: true
}
},
bidders: ['b1', 'b2']
});

const bidderRequests = [
{
...BID_REQUESTS[0],
bidderCode: 'b1',
bids: [{
bidder: 'b1',
bidId: 1,
getFloor: () => ({
currency: 'CUR',
floor: 1.23
})
}]
},
{
...BID_REQUESTS[0],
bidderCode: 'b2',
bids: [{
bidder: 'b2',
bidId: 2,
getFloor: () => ({
currency: 'CUR',
floor: 3.21
})
}],
}
];

const adUnits = [
{
code: 'au1',
transactionId: 't1',
mediaTypes: {
banner: {sizes: [1, 1]}
},
bids: [{bidder: 'b1', bid_id: 1}]
},
{
code: 'au2',
transactionId: 't2',
bids: [{bidder: 'b2', bid_id: 2}],
mediaTypes: {
banner: {sizes: [1, 1]}
},
ortb2Imp: {
ext: {
prebid: {
floors: {
floorMin: 1
}
}
}
}
}
];

const _config = {
s2sConfig: s2sConfig,
};

const s2sBidRequest = utils.deepClone(REQUEST);
s2sBidRequest.s2sConfig = s2sConfig;
s2sBidRequest.ad_units = adUnits;
config.setConfig(_config);

adapter.callBids(s2sBidRequest, bidderRequests, addBidResponse, done, ajax);
const requestBid = JSON.parse(server.requests[0].requestBody);
expect(requestBid.imp[0].bidfloor).to.equal(1.23);
expect(requestBid.imp[1].bidfloor).to.equal(3.21);

expect(requestBid.ext.prebid.floors).to.deep.equal({ enabled: true, floorMin: 1, floorMinCur: 'CUR' });
});
});
});

0 comments on commit 17b9b32

Please sign in to comment.