Skip to content

Commit

Permalink
Rubicon analytics supress floor data from other providers (#5552)
Browse files Browse the repository at this point in the history
  • Loading branch information
robertrmartinez authored Jul 29, 2020
1 parent 7f901c6 commit b36f11a
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 19 deletions.
47 changes: 30 additions & 17 deletions modules/rubiconAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const cache = {
timeouts: {},
};

const validFloorProviders = ['rubicon', 'rubi', 'magnite', 'mgni'];

export function getHostNameFromReferer(referer) {
try {
rubiconAdapter.referrerHostname = utils.parseUrl(referer, {noDecodeWholeURL: true}).hostname;
Expand Down Expand Up @@ -195,15 +197,22 @@ function sendMessage(auctionId, bidWonId) {

// pick our of top level floor data we want to send!
if (auctionCache.floorData) {
auction.floors = utils.pick(auctionCache.floorData, [
'location',
'modelName', () => auctionCache.floorData.modelVersion,
'skipped',
'enforcement', () => utils.deepAccess(auctionCache.floorData, 'enforcements.enforceJS'),
'dealsEnforced', () => utils.deepAccess(auctionCache.floorData, 'enforcements.floorDeals'),
'skipRate', skipRate => !isNaN(skipRate) ? skipRate : 0,
'fetchStatus'
]);
if (auctionCache.floorData.location === 'noData') {
auction.floors = utils.pick(auctionCache.floorData, [
'location',
'fetchStatus'
]);
} else {
auction.floors = utils.pick(auctionCache.floorData, [
'location',
'modelName', () => auctionCache.floorData.modelVersion,
'skipped',
'enforcement', () => utils.deepAccess(auctionCache.floorData, 'enforcements.enforceJS'),
'dealsEnforced', () => utils.deepAccess(auctionCache.floorData, 'enforcements.floorDeals'),
'skipRate',
'fetchStatus'
]);
}
}

if (serverConfig) {
Expand Down Expand Up @@ -269,14 +278,14 @@ function getBidPrice(bid) {
}
}

export function parseBidResponse(bid, previousBidResponse) {
export function parseBidResponse(bid, previousBidResponse, auctionFloorData) {
// The current bidResponse for this matching requestId/bidRequestId
let responsePrice = getBidPrice(bid)
// we need to compare it with the previous one (if there was one)
if (previousBidResponse && previousBidResponse.bidPriceUSD > responsePrice) {
return previousBidResponse;
}
return utils.pick(bid, [
let bidResponse = utils.pick(bid, [
'bidPriceUSD', () => responsePrice,
'dealId',
'status',
Expand All @@ -286,9 +295,12 @@ export function parseBidResponse(bid, previousBidResponse) {
'height'
]),
'seatBidId',
'floorValue', () => utils.deepAccess(bid, 'floorData.floorValue'),
'floorRule', () => utils.debugTurnedOn() ? utils.deepAccess(bid, 'floorData.floorRule') : undefined
]);
if (auctionFloorData) {
bidResponse.floorValue = utils.deepAccess(bid, 'floorData.floorValue');
bidResponse.floorRule = utils.debugTurnedOn() ? utils.deepAccess(bid, 'floorData.floorRule') : undefined
}
return bidResponse;
}

let samplingFactor = 1;
Expand Down Expand Up @@ -368,8 +380,9 @@ let rubiconAdapter = Object.assign({}, baseAdapter, {
cacheEntry.bids = {};
cacheEntry.bidsWon = {};
cacheEntry.referrer = args.bidderRequests[0].refererInfo.referer;
if (utils.deepAccess(args, 'bidderRequests.0.bids.0.floorData')) {
cacheEntry.floorData = {...utils.deepAccess(args, 'bidderRequests.0.bids.0.floorData')};
const floorProvider = utils.deepAccess(args, 'bidderRequests.0.bids.0.floorData.floorProvider');
if (validFloorProviders.indexOf(floorProvider) !== -1) {
cacheEntry.floorData = {...args.bidderRequests[0].bids[0].floorData};
}
cache.auctions[args.auctionId] = cacheEntry;
break;
Expand Down Expand Up @@ -455,7 +468,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, {
bid.adUnit.adSlot = args.floorData.matchedFields.gptSlot;
}
// if we have not set enforcements yet set it
if (!utils.deepAccess(auctionEntry, 'floorData.enforcements') && utils.deepAccess(args, 'floorData.enforcements')) {
if (auctionEntry.floorData && !auctionEntry.floorData.enforcements && utils.deepAccess(args, 'floorData.enforcements')) {
auctionEntry.floorData.enforcements = {...args.floorData.enforcements};
}
if (!bid) {
Expand All @@ -479,7 +492,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, {
};
}
bid.clientLatencyMillis = Date.now() - cache.auctions[args.auctionId].timestamp;
bid.bidResponse = parseBidResponse(args, bid.bidResponse);
bid.bidResponse = parseBidResponse(args, bid.bidResponse, auctionEntry.floorData);
break;
case BIDDER_DONE:
args.bids.forEach(bid => {
Expand Down
36 changes: 34 additions & 2 deletions test/spec/modules/rubiconAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -659,14 +659,15 @@ describe('rubicon analytics adapter', function () {
expect(message).to.deep.equal(ANALYTICS_MESSAGE);
});

it('should capture price floor information correctly', function () {
function performFloorAuction(provider) {
let auctionInit = utils.deepClone(MOCK.AUCTION_INIT);
auctionInit.bidderRequests[0].bids[0].floorData = {
skipped: false,
modelVersion: 'someModelName',
location: 'setConfig',
skipRate: 15,
fetchStatus: 'error'
fetchStatus: 'error',
floorProvider: provider
};
let flooredResponse = {
...BID,
Expand Down Expand Up @@ -727,6 +728,11 @@ describe('rubicon analytics adapter', function () {

let message = JSON.parse(server.requests[0].requestBody);
validate(message);
return message;
}

it('should capture price floor information correctly', function () {
let message = performFloorAuction('rubicon')

// verify our floor stuff is passed
// top level floor info
Expand Down Expand Up @@ -759,6 +765,32 @@ describe('rubicon analytics adapter', function () {
expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52);
});

it('should not send floor info if provider is not rubicon', function () {
let message = performFloorAuction('randomProvider')

// verify our floor stuff is passed
// top level floor info
expect(message.auctions[0].floors).to.be.undefined;
// first adUnit's adSlot
expect(message.auctions[0].adUnits[0].adSlot).to.equal('12345/sports');
// since no other bids, we set adUnit status to no-bid
expect(message.auctions[0].adUnits[0].status).to.equal('no-bid');
// first adUnits bid is rejected
expect(message.auctions[0].adUnits[0].bids[0].status).to.equal('rejected');
expect(message.auctions[0].adUnits[0].bids[0].bidResponse.floorValue).to.be.undefined;
// if bid rejected should take cpmAfterAdjustments val
expect(message.auctions[0].adUnits[0].bids[0].bidResponse.bidPriceUSD).to.equal(2.1);

// second adUnit's adSlot
expect(message.auctions[0].adUnits[1].adSlot).to.equal('12345/news');
// top level adUnit status is success
expect(message.auctions[0].adUnits[1].status).to.equal('success');
// second adUnits bid is success
expect(message.auctions[0].adUnits[1].bids[0].status).to.equal('success');
expect(message.auctions[0].adUnits[1].bids[0].bidResponse.floorValue).to.be.undefined;
expect(message.auctions[0].adUnits[1].bids[0].bidResponse.bidPriceUSD).to.equal(1.52);
});

it('should correctly overwrite bidId if seatBidId is on the bidResponse', function () {
// Only want one bid request in our mock auction
let bidRequested = utils.deepClone(MOCK.BID_REQUESTED);
Expand Down

0 comments on commit b36f11a

Please sign in to comment.