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

Medianet Analytics Adapter: ADD bid properties in logs and small fix in bidTimeoutHandler #12526

Merged
merged 1 commit into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
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
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