From 0175c366bee715bf26d13ce17f640b7e82c3f72f Mon Sep 17 00:00:00 2001 From: vivekyadav15 Date: Tue, 3 Dec 2024 21:34:46 +0530 Subject: [PATCH] Medianet Analytics Adapter: ADD bid properties in logs and small fix in bidTimeoutHandler (#12526) --- modules/medianetAnalyticsAdapter.js | 116 +++++++++++++++--- .../modules/medianetAnalyticsAdapter_spec.js | 18 +-- 2 files changed, 105 insertions(+), 29 deletions(-) diff --git a/modules/medianetAnalyticsAdapter.js b/modules/medianetAnalyticsAdapter.js index e8d73e77d5f..1b4ccc45c88 100644 --- a/modules/medianetAnalyticsAdapter.js +++ b/modules/medianetAnalyticsAdapter.js @@ -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'; @@ -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'; @@ -112,6 +114,7 @@ class Configure { pbv: PREBID_VERSION, pbav: ANALYTICS_VERSION, flt: 1, + enableDbf: 1 } } @@ -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; @@ -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() { @@ -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, @@ -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 } } } @@ -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) { @@ -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; @@ -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); @@ -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; @@ -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; } @@ -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); @@ -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; @@ -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; diff --git a/test/spec/modules/medianetAnalyticsAdapter_spec.js b/test/spec/modules/medianetAnalyticsAdapter_spec.js index 7c3cf88dace..8a298acae80 100644 --- a/test/spec/modules/medianetAnalyticsAdapter_spec.js +++ b/test/spec/modules/medianetAnalyticsAdapter_spec.js @@ -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']); }); @@ -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); @@ -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({ @@ -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({ @@ -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'); });