Skip to content

Commit

Permalink
Medianet Analytics Adapter: ADD bid properties in logs and small fix …
Browse files Browse the repository at this point in the history
…in bidTimeoutHandler (#12526)
  • Loading branch information
vivekyadav15 authored Dec 3, 2024
1 parent d014440 commit 0175c36
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 29 deletions.
116 changes: 96 additions & 20 deletions modules/medianetAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
logError,
logInfo,
triggerPixel,
uniques
uniques,
deepSetValue
} from '../src/utils.js';
import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js';
import adapterManager from '../src/adapterManager.js';
Expand All @@ -18,6 +19,7 @@ import {getRefererInfo} from '../src/refererDetection.js';
import {AUCTION_COMPLETED, AUCTION_IN_PROGRESS, getPriceGranularity} from '../src/auction.js';
import {includes} from '../src/polyfill.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {convertCurrency} from '../libraries/currencyUtils/currency.js';

const analyticsType = 'endpoint';
const ENDPOINT = 'https://pb-logs.media.net/log?logid=kfk&evtid=prebid_analytics_events_client';
Expand Down Expand Up @@ -112,6 +114,7 @@ class Configure {
pbv: PREBID_VERSION,
pbav: ANALYTICS_VERSION,
flt: 1,
enableDbf: 1
}
}

Expand Down Expand Up @@ -303,21 +306,17 @@ class BidWrapper {
this.bidObjs.push(bidObj);
}

getAdSlotBids(adSlot) {
const bidResponses = this.getAdSlotBidObjs(adSlot);
return bidResponses.map((bid) => bid.getLoggingData());
getAdSlotBidRequests(adSlot) {
return this.bidReqs.filter((bid) => bid.adUnitCode === adSlot);
}

getAdSlotBidObjs(adSlot) {
const bidResponses = this.bidObjs
.filter((bid) => bid.adUnitCode === adSlot);
const remResponses = this.bidReqs.filter(bid => !bid.used && bid.adUnitCode === adSlot);
return [...bidResponses, ...remResponses];
getAdSlotBidResponses(adSlot) {
return this.bidObjs.filter((bid) => bid.adUnitCode === adSlot);
}
}

class Bid {
constructor(bidId, bidder, src, start, adUnitCode, mediaType, allMediaTypeSizes) {
constructor(bidId, bidder, src, start, adUnitCode, mediaType, allMediaTypeSizes, resSizes) {
this.bidId = bidId;
this.bidder = bidder;
this.src = src;
Expand Down Expand Up @@ -348,6 +347,14 @@ class Bid {
this.used = false;
this.originalRequestId = bidId;
this.requestId = undefined;
this.advUrl = undefined;
this.latestAcid = undefined;
this.originalCurrency = undefined;
this.currMul = undefined;
this.inCurrMul = undefined;
this.res_mtype = undefined;
this.res_sizes = resSizes;
this.req_mtype = mediaType;
}

get size() {
Expand All @@ -374,7 +381,7 @@ class Bid {
cbdp: this.dfpbd,
dfpbd: this.dfpbd,
szs: this.allMediaTypeSizes.join('|'),
size: this.size,
size: (this.res_sizes || [this.size]).join('|'),
mtype: this.mediaType,
dId: this.dealId,
winner: this.winner,
Expand All @@ -389,6 +396,13 @@ class Bid {
flrrule: this.floorRule,
ext: JSON.stringify(this.ext),
rtime: this.serverLatencyMillis,
advurl: this.advUrl,
lacid: this.latestAcid,
icurr: this.originalCurrency,
imul: this.inCurrMul,
omul: this.currMul,
res_mtype: this.res_mtype,
req_mtype: this.req_mtype
}
}
}
Expand Down Expand Up @@ -454,11 +468,12 @@ class Auction {
return this.bidWrapper.findBidObj(key, value)
}

getAdSlotBids(adSlot) {
return this.bidWrapper.getAdSlotBids(adSlot);
getAdSlotBidRequests(adSlot) {
return this.bidWrapper.getAdSlotBidRequests(adSlot);
}
getAdSlotBidObjs(adSlot) {
return this.bidWrapper.getAdSlotBidObjs(adSlot);

getAdSlotBidResponses(adSlot) {
return this.bidWrapper.getAdSlotBidResponses(adSlot);
}

_mergeFieldsToLog(objParams) {
Expand Down Expand Up @@ -558,8 +573,8 @@ function _getSizes(mediaTypes, sizes) {
}

function bidResponseHandler(bid) {
const { width, height, mediaType, cpm, requestId, timeToRespond, auctionId, dealId, originalRequestId, bidder } = bid;
const {originalCpm, creativeId, adId, currency} = bid;
const { width, height, mediaType, cpm, requestId, timeToRespond, auctionId, dealId, originalRequestId, bidder, meta } = bid;
let {originalCpm, creativeId, adId, currency, originalCurrency} = bid;

if (!(auctions[auctionId] instanceof Auction)) {
return;
Expand All @@ -575,13 +590,26 @@ function bidResponseHandler(bid) {
bidObj = {};
isBidOverridden = false;
}
currency = currency ? currency.toUpperCase() : '';
originalCurrency = originalCurrency ? originalCurrency.toUpperCase() : currency;
Object.assign(bidObj, bidReq,
{ cpm, width, height, mediaType, timeToRespond, dealId, creativeId, originalRequestId, requestId },
{ adId, currency }
{ adId, currency, originalCurrency }
);
bidObj.floorPrice = deepAccess(bid, 'floorData.floorValue');
bidObj.floorRule = deepAccess(bid, 'floorData.floorRule');
bidObj.originalCpm = originalCpm || cpm;
bidObj.advUrl = meta && meta.advertiserDomains && meta.advertiserDomains.join(',');
bidObj.currMul = 1;
bidObj.inCurrMul = 1;
if (bidObj.originalCurrency !== 'USD') {
bidObj.originalCpm = exchangeCurrency(bidObj.originalCpm, bidObj.originalCurrency, 'USD');
bidObj.inCurrMul = exchangeCurrency(1, 'USD', bidObj.originalCurrency)
}
if (bidObj.currency !== 'USD') {
bidObj.cpm = exchangeCurrency(bidObj.cpm, bidObj.currency, 'USD');
bidObj.currMul = exchangeCurrency(1, 'USD', bidObj.currency)
}
let dfpbd = deepAccess(bid, 'adserverTargeting.hb_pb');
if (!dfpbd) {
let priceGranularity = getPriceGranularity(bid);
Expand All @@ -606,9 +634,19 @@ function bidResponseHandler(bid) {
if (typeof bid.serverResponseTimeMs !== 'undefined') {
bidObj.serverLatencyMillis = bid.serverResponseTimeMs;
}
bidObj.res_mtype = mediaType;
!isBidOverridden && auctions[auctionId].addBidObj(bidObj);
}

function exchangeCurrency(price, fromCurrency, toCurrency) {
try {
return convertCurrency(price, fromCurrency, toCurrency, false).toFixed(4)
} catch (e) {
logError(`Media.net Analytics Adapter: Could not convert ${fromCurrency} to ${toCurrency} for price ${price}`);
}
return price;
}

function noBidResponseHandler({ auctionId, bidId }) {
if (!(auctions[auctionId] instanceof Auction)) {
return;
Expand All @@ -630,7 +668,7 @@ function bidTimeoutHandler(timedOutBids) {
if (!(auctions[auctionId] instanceof Auction)) {
return;
}
const bidReq = auctions[auctionId].findReqBid('bidId', bidId);
const bidReq = auctions[auctionId].findReqBid(bidId);
if (!(bidReq instanceof Bid) || bidReq.used) {
return;
}
Expand Down Expand Up @@ -710,6 +748,7 @@ function bidWonHandler(bid) {
}).send();
return;
}
bidObj.latestAcid = bid.latestTargetedAuctionId;
auctions[auctionId].bidWonTime = Date.now();
bidObj.winner = 1;
sendEvent(auctionId, adUnitCode, LOG_TYPE.RA, bidObj.adId);
Expand Down Expand Up @@ -749,6 +788,43 @@ function getCommonLoggingData(acid, adtag) {
return Object.assign(commonParams, adunitParams, auctionParams);
}

function getResponseSizeMap(acid, adtag) {
const responses = auctions[acid].getAdSlotBidResponses(adtag);
const receivedResponse = {};
// Set true in map for success bids
responses.filter((bid) => bid.size !== '')
.forEach((bid) => deepSetValue(receivedResponse, `${bid.bidId}.${bid.size}`, true));

// For non-success bids:
// 1) set bid.res_sizes = (sizes for which no successful bid received)
// 2) set true in map
responses.filter((bid) => bid.size === '').forEach((bid) => {
bid.res_sizes = bid.allMediaTypeSizes.filter((size) => !deepAccess(receivedResponse, `${bid.bidId}.${size}`));
bid.allMediaTypeSizes.forEach((size) => deepSetValue(receivedResponse, `${bid.bidId}.${size}`, true));
});
return receivedResponse;
}

function getDummyBids(acid, adtag, receivedResponse) {
const emptyBids = [];
auctions[acid].getAdSlotBidRequests(adtag).forEach(({ bidId, bidder, src, start, adUnitCode, mediaType, allMediaTypeSizes, status }) => {
const emptySizes = allMediaTypeSizes.filter((size) => !deepAccess(receivedResponse, `${bidId}.${size}`));
if (bidder !== DUMMY_BIDDER && emptySizes.length > 0) {
const bid = new Bid(bidId, bidder, src, start, adUnitCode, mediaType, allMediaTypeSizes, emptySizes);
bid.status = status === BID_SUCCESS ? BID_NOBID : status;
emptyBids.push(bid);
}
});
return emptyBids;
}

function getLoggingBids(acid, adtag) {
const receivedResponse = getResponseSizeMap(acid, adtag);
const dummyBids = getDummyBids(acid, adtag, receivedResponse);

return [...auctions[acid].getAdSlotBidResponses(adtag), ...dummyBids];
}

function fireAuctionLog(acid, adtag, logType, adId) {
let commonParams = getCommonLoggingData(acid, adtag);
commonParams.lgtp = logType;
Expand All @@ -766,7 +842,7 @@ function fireAuctionLog(acid, adtag, logType, adId) {
bidParams = [winLogData];
commonParams.lper = 1;
} else {
bidParams = auctions[acid].getAdSlotBids(adtag).map(({winner, ...restParams}) => restParams);
bidParams = getLoggingBids(acid, adtag).map((bid) => bid.getLoggingData())
delete commonParams.wts;
}
let mnetPresent = bidParams.filter(b => b.pvnm === MEDIANET_BIDDER_CODE).length > 0;
Expand Down
18 changes: 9 additions & 9 deletions test/spec/modules/medianetAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ describe('Media.net Analytics Adapter', function() {
expect(noBidLog.pvnm).to.have.ordered.members(['-2', 'bidder1', 'bidder1', 'medianet']);
expect(noBidLog.mtype).to.have.ordered.members([banner, banner, banner, banner]);
expect(noBidLog.status).to.have.ordered.members(['1', '2', '2', '2']);
expect(noBidLog.size).to.have.ordered.members(['', '', '', '']);
expect(noBidLog.size).to.have.ordered.members(['300x100|300x250', '300x100', '300x250', '300x250|300x100']);
expect(noBidLog.szs).to.have.ordered.members(['300x100|300x250', '300x100', '300x250', '300x250|300x100']);
});

Expand All @@ -225,7 +225,7 @@ describe('Media.net Analytics Adapter', function() {
it('should have winner log in standard auction', function() {
medianetAnalytics.clearlogsQueue();
performStandardAuctionWithWinner();
let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner);
let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner === '1');
medianetAnalytics.clearlogsQueue();

expect(winnerLog.length).to.equal(1);
Expand All @@ -234,7 +234,7 @@ describe('Media.net Analytics Adapter', function() {
it('should have correct values in winner log', function() {
medianetAnalytics.clearlogsQueue();
performStandardAuctionWithWinner();
let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner);
let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner === '1');
medianetAnalytics.clearlogsQueue();

expect(winnerLog[0]).to.include({
Expand All @@ -258,7 +258,7 @@ describe('Media.net Analytics Adapter', function() {
it('should have correct bid floor data in winner log', function() {
medianetAnalytics.clearlogsQueue();
performAuctionWithFloorConfig();
let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner);
let winnerLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter((log) => log.winner === '1');
medianetAnalytics.clearlogsQueue();

expect(winnerLog[0]).to.include({
Expand Down Expand Up @@ -309,32 +309,32 @@ describe('Media.net Analytics Adapter', function() {

it('should pick winning bid if multibids with same request id', function() {
performStandardAuctionMultiBidWithSameRequestId(MOCK.BIDS_SAME_REQ_DIFF_CPM);
let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0];
let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner === '1')[0];
expect(winningBid.adid).equals('3e6e4bce5c8fb3');
medianetAnalytics.clearlogsQueue();

const reversedResponseArray = [].concat(MOCK.BIDS_SAME_REQ_DIFF_CPM).reverse();
performStandardAuctionMultiBidWithSameRequestId(reversedResponseArray);
winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0];
winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner === '1')[0];
expect(winningBid.adid).equals('3e6e4bce5c8fb3');
});

it('should pick winning bid if multibids with same request id and same time to respond', function() {
performStandardAuctionMultiBidWithSameRequestId(MOCK.BIDS_SAME_REQ_DIFF_CPM_SAME_TIME);
let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0];
let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner === '1')[0];
expect(winningBid.adid).equals('3e6e4bce5c8fb3');
medianetAnalytics.clearlogsQueue();
});

it('should pick winning bid if multibids with same request id and equal cpm', function() {
performStandardAuctionMultiBidWithSameRequestId(MOCK.BIDS_SAME_REQ_EQUAL_CPM);
let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0];
let winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner === '1')[0];
expect(winningBid.adid).equals('3e6e4bce5c8fb3');
medianetAnalytics.clearlogsQueue();

const reversedResponseArray = [].concat(MOCK.BIDS_SAME_REQ_EQUAL_CPM).reverse();
performStandardAuctionMultiBidWithSameRequestId(reversedResponseArray);
winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner)[0];
winningBid = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log)).filter(log => log.winner === '1')[0];
expect(winningBid.adid).equals('3e6e4bce5c8fb3');
});

Expand Down

0 comments on commit 0175c36

Please sign in to comment.