Skip to content

Commit

Permalink
PubMatic analytics adapter to support bidCpmAdustment values (prebid#…
Browse files Browse the repository at this point in the history
…5354)

* added support for pubcommon, digitrust, id5id

* added support for IdentityLink

* changed the source for id5

* added unit test cases

* changed source param for identityLink

* set gross ecpm using originalCpm

* using bidGrossCpmUSD to set eg

* lint

* fixed old cases

* added some tests

* removed comments

* removed commented code , and unused import

* en related changes in test cases

* using pbjs.getHighestCpmBids, added test cases around it

* passing highestCpmBids

* fixed test cases

* added notes

* using getGlobal() instead of $$PREBID_GLOBAL$$
  • Loading branch information
pm-harshad-mane authored and iggyfisk committed Jun 22, 2020
1 parent 4cf34aa commit 352f634
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 30 deletions.
50 changes: 26 additions & 24 deletions modules/pubmaticAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import CONSTANTS from '../src/constants.json';
import { ajax } from '../src/ajax.js';
import { config } from '../src/config.js';
import * as utils from '../src/utils.js';
import { getGlobal } from '../src/prebidGlobal.js';

/// /////////// CONSTANTS //////////////
const ADAPTER_CODE = 'pubmatic';
Expand Down Expand Up @@ -124,7 +125,19 @@ function parseBidResponse(bid) {
if (typeof bid.getCpmInNewCurrency === 'function') {
return window.parseFloat(Number(bid.getCpmInNewCurrency(CURRENCY_USD)).toFixed(BID_PRECISION));
}
utils.logWarn(LOG_PRE_FIX + 'Could not determine the bidPriceUSD of the bid ', bid);
utils.logWarn(LOG_PRE_FIX + 'Could not determine the Net cpm in USD for the bid thus using bid.cpm', bid);
return bid.cpm
},
'bidGrossCpmUSD', () => {
if (typeof bid.originalCurrency === 'string' && bid.originalCurrency.toUpperCase() === CURRENCY_USD) {
return window.parseFloat(Number(bid.originalCpm).toFixed(BID_PRECISION));
}
// use currency conversion function if present
if (typeof getGlobal().convertCurrency === 'function') {
return window.parseFloat(Number(getGlobal().convertCurrency(bid.originalCpm, bid.originalCurrency, CURRENCY_USD)).toFixed(BID_PRECISION));
}
utils.logWarn(LOG_PRE_FIX + 'Could not determine the Gross cpm in USD for the bid, thus using bid.originalCpm', bid);
return bid.originalCpm
},
'dealId',
'currency',
Expand Down Expand Up @@ -152,20 +165,8 @@ function getDomainFromUrl(url) {
return a.hostname;
}

function getHighestBidForAdUnit(adUnit) {
return Object.keys(adUnit.bids).reduce(function(currentHighestBid, bidId) {
// todo: later we will need to consider grossECPM and netECPM
let bid = adUnit.bids[bidId];
if (bid.bidResponse && bid.bidResponse.bidPriceUSD > currentHighestBid.bidPriceUSD) {
currentHighestBid.bidPriceUSD = bid.bidResponse.bidPriceUSD;
currentHighestBid.bidId = bidId;
}
return currentHighestBid;
}, {bidId: '', bidPriceUSD: 0});
}

function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId) {
const highestsBid = getHighestBidForAdUnit(adUnit);
function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestBid) {
highestBid = (highestBid && highestBid.length > 0) ? highestBid[0] : null;
return Object.keys(adUnit.bids).reduce(function(partnerBids, bidId) {
let bid = adUnit.bids[bidId];
partnerBids.push({
Expand All @@ -175,16 +176,15 @@ function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId) {
'kgpv': bid.params.kgpv ? bid.params.kgpv : adUnitId,
'kgpsv': bid.params.kgpv ? bid.params.kgpv : adUnitId,
'psz': bid.bidResponse ? (bid.bidResponse.dimensions.width + 'x' + bid.bidResponse.dimensions.height) : '0x0',
'eg': bid.bidResponse ? bid.bidResponse.bidPriceUSD : 0, // todo: later we will need to consider grossECPM and netECPM, precision
'en': bid.bidResponse ? bid.bidResponse.bidPriceUSD : 0, // todo: later we will need to consider grossECPM and netECPM, precision
'eg': bid.bidResponse ? bid.bidResponse.bidGrossCpmUSD : 0,
'en': bid.bidResponse ? bid.bidResponse.bidPriceUSD : 0,
'di': bid.bidResponse ? (bid.bidResponse.dealId || EMPTY_STRING) : EMPTY_STRING,
'dc': bid.bidResponse ? (bid.bidResponse.dealChannel || EMPTY_STRING) : EMPTY_STRING,
'l1': bid.bidResponse ? bid.clientLatencyTimeMs : 0,
'l2': 0,
// 'ss': (bid.source === 'server' ? 1 : 0), // todo: is there any special handling required as per OW?
'ss': (s2sBidders.indexOf(bid.bidder) > -1) ? 1 : 0,
't': (bid.status == ERROR && bid.error.code == TIMEOUT_ERROR) ? 1 : 0,
'wb': highestsBid.bidId === bid.bidId ? 1 : 0,
'wb': (highestBid && highestBid.requestId === bid.bidId ? 1 : 0),
'mi': bid.bidResponse ? (bid.bidResponse.mi || undefined) : undefined,
'af': bid.bidResponse ? (bid.bidResponse.mediaType || undefined) : undefined,
'ocpm': bid.bidResponse ? (bid.bidResponse.originalCpm || 0) : 0,
Expand All @@ -194,7 +194,8 @@ function gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId) {
}, [])
}

function executeBidsLoggerCall(auctionId) {
function executeBidsLoggerCall(e, highestCpmBids) {
let auctionId = e.auctionId;
let referrer = config.getConfig('pageUrl') || cache.auctions[auctionId].referer || '';
let auctionCache = cache.auctions[auctionId];
let outputObj = { s: [] };
Expand Down Expand Up @@ -230,7 +231,7 @@ function executeBidsLoggerCall(auctionId) {
let slotObject = {
'sn': adUnitId,
'sz': adUnit.dimensions.map(e => e[0] + 'x' + e[1]),
'ps': gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId)
'ps': gatherPartnerBidsForAdUnitForLogger(adUnit, adUnitId, highestCpmBids.filter(bid => bid.adUnitCode === adUnitId))
};
slotsArray.push(slotObject);
return slotsArray;
Expand Down Expand Up @@ -263,8 +264,8 @@ function executeBidWonLoggerCall(auctionId, adUnitId) {
pixelURL += '&pdvid=' + enc(profileVersionId);
pixelURL += '&slot=' + enc(adUnitId);
pixelURL += '&pn=' + enc(winningBid.bidder);
pixelURL += '&en=' + enc(winningBid.bidResponse.bidPriceUSD); // todo: later we will need to consider grossECPM and netECPM
pixelURL += '&eg=' + enc(winningBid.bidResponse.bidPriceUSD); // todo: later we will need to consider grossECPM and netECPM
pixelURL += '&en=' + enc(winningBid.bidResponse.bidPriceUSD);
pixelURL += '&eg=' + enc(winningBid.bidResponse.bidGrossCpmUSD);
pixelURL += '&kgpv=' + enc(winningBid.params.kgpv || adUnitId);
ajax(
pixelURL,
Expand Down Expand Up @@ -345,8 +346,9 @@ function bidWonHandler(args) {

function auctionEndHandler(args) {
// if for the given auction bidderDonePendingCount == 0 then execute logger call sooners
let highestCpmBids = getGlobal().getHighestCpmBids() || [];
setTimeout(() => {
executeBidsLoggerCall.call(this, args.auctionId);
executeBidsLoggerCall.call(this, args, highestCpmBids);
}, (cache.auctions[args.auctionId].bidderDonePendingCount === 0 ? 500 : SEND_TIMEOUT));
}

Expand Down
3 changes: 1 addition & 2 deletions modules/pubmaticAnalyticsAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ pbjs.enableAnalytics({
- Supports only Banner and Video media-type
- Does not supports Native media type
- Does not supports instream-video creative-render tracker
- BidCpmAdjustment: Bid CPM only after BidCpmAdjustment is logged
- If a currency module is NOT included and a bidder responds in a non-USD currency then PubMatic analytics bidder will not be able to log the bid CPM
- If a currency module is NOT included and a bidder responds in a non-USD currency then PubMatic analytics bidder will log values in original bid currency otherwise always logged in USD
133 changes: 129 additions & 4 deletions test/spec/modules/pubmaticAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@ describe('pubmatic analytics adapter', function () {
});

it('Logger: best case + win tracker', function() {
sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => {
return [MOCK.BID_RESPONSE[0], MOCK.BID_RESPONSE[1]]
});

events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]);
Expand Down Expand Up @@ -387,6 +391,122 @@ describe('pubmatic analytics adapter', function () {
expect(data.en).to.equal('1.23');
});

it('bidCpmAdjustment: USD: Logger: best case + win tracker', function() {
const bidCopy = utils.deepClone(BID);
bidCopy.cpm = bidCopy.originalCpm * 2; // bidCpmAdjustment => bidCpm * 2

sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => {
return [bidCopy, MOCK.BID_RESPONSE[1]]
});

events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
events.emit(BID_RESPONSE, bidCopy);
events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]);
events.emit(BID_RESPONSE, bidCopy);
events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
events.emit(AUCTION_END, MOCK.AUCTION_END);
events.emit(SET_TARGETING, MOCK.SET_TARGETING);
events.emit(BID_WON, MOCK.BID_WON[0]);
events.emit(BID_WON, MOCK.BID_WON[1]);

clock.tick(2000 + 1000);
expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker
let request = requests[2]; // logger is executed late, trackers execute first
expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999&gdEn=1');
let data = getLoggerJsonFromRequest(request.requestBody);
expect(data.pubid).to.equal('9999');
expect(data.pid).to.equal('1111');
expect(data.s).to.be.an('array');
expect(data.s.length).to.equal(2);
// slot 1
expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0');
expect(data.s[0].sz).to.deep.equal(['640x480']);
expect(data.s[0].ps).to.be.an('array');
expect(data.s[0].ps.length).to.equal(1);
expect(data.s[0].ps[0].pn).to.equal('pubmatic');
expect(data.s[0].ps[0].bidid).to.equal('2ecff0db240757');
expect(data.s[0].ps[0].kgpv).to.equal('/19968336/header-bid-tag-0');
expect(data.s[0].ps[0].eg).to.equal(1.23);
expect(data.s[0].ps[0].en).to.equal(2.46);
expect(data.s[0].ps[0].wb).to.equal(1);
expect(data.s[0].ps[0].af).to.equal('video');
expect(data.s[0].ps[0].ocpm).to.equal(1.23);
expect(data.s[0].ps[0].ocry).to.equal('USD');
// tracker slot1
let firstTracker = requests[0].url;
expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt');
data = {};
firstTracker.split('?')[1].split('&').map(e => e.split('=')).forEach(e => data[e[0]] = e[1]);
expect(data.pubid).to.equal('9999');
expect(data.tst).to.equal('1519767014');
expect(data.iid).to.equal('25c6d7f5-699a-4bfc-87c9-996f915341fa');
expect(data.eg).to.equal('1.23');
expect(data.en).to.equal('2.46');
});

it('bidCpmAdjustment: JPY: Logger: best case + win tracker', function() {
setConfig({
adServerCurrency: 'JPY',
rates: {
USD: {
JPY: 100
}
}
});
const bidCopy = utils.deepClone(BID);
bidCopy.originalCpm = 100;
bidCopy.originalCurrency = 'JPY';
bidCopy.currency = 'JPY';
bidCopy.cpm = bidCopy.originalCpm * 2; // bidCpmAdjustment => bidCpm * 2

events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
// events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]);
events.emit(BID_RESPONSE, bidCopy);
events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]);
events.emit(BID_RESPONSE, bidCopy);
events.emit(BIDDER_DONE, MOCK.BIDDER_DONE);
events.emit(AUCTION_END, MOCK.AUCTION_END);
events.emit(SET_TARGETING, MOCK.SET_TARGETING);
events.emit(BID_WON, MOCK.BID_WON[0]);
events.emit(BID_WON, MOCK.BID_WON[1]);

clock.tick(2000 + 1000);
expect(requests.length).to.equal(3); // 1 logger and 2 win-tracker
let request = requests[2]; // logger is executed late, trackers execute first
expect(request.url).to.equal('https://t.pubmatic.com/wl?pubid=9999&gdEn=1');
let data = getLoggerJsonFromRequest(request.requestBody);
expect(data.pubid).to.equal('9999');
expect(data.pid).to.equal('1111');
expect(data.s).to.be.an('array');
expect(data.s.length).to.equal(2);
// slot 1
expect(data.s[0].sn).to.equal('/19968336/header-bid-tag-0');
expect(data.s[0].sz).to.deep.equal(['640x480']);
expect(data.s[0].ps).to.be.an('array');
expect(data.s[0].ps.length).to.equal(1);
expect(data.s[0].ps[0].pn).to.equal('pubmatic');
expect(data.s[0].ps[0].bidid).to.equal('2ecff0db240757');
expect(data.s[0].ps[0].kgpv).to.equal('/19968336/header-bid-tag-0');
expect(data.s[0].ps[0].eg).to.equal(1);
expect(data.s[0].ps[0].en).to.equal(200);
expect(data.s[0].ps[0].wb).to.equal(0); // bidPriceUSD is not getting set as currency module is not added, so unable to set wb to 1
expect(data.s[0].ps[0].af).to.equal('video');
expect(data.s[0].ps[0].ocpm).to.equal(100);
expect(data.s[0].ps[0].ocry).to.equal('JPY');
// tracker slot1
let firstTracker = requests[0].url;
expect(firstTracker.split('?')[0]).to.equal('https://t.pubmatic.com/wt');
data = {};
firstTracker.split('?')[1].split('&').map(e => e.split('=')).forEach(e => data[e[0]] = e[1]);
expect(data.pubid).to.equal('9999');
expect(data.tst).to.equal('1519767014');
expect(data.iid).to.equal('25c6d7f5-699a-4bfc-87c9-996f915341fa');
expect(data.eg).to.equal('1');
expect(data.en).to.equal('200'); // bidPriceUSD is not getting set as currency module is not added
});

it('Logger: when bid is not submitted, default bid status 1 check: pubmatic set as s2s', function() {
events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
Expand Down Expand Up @@ -463,6 +583,11 @@ describe('pubmatic analytics adapter', function () {

it('Logger: post-timeout check with bid response', function() {
// db = 1 and t = 1 means bidder did NOT respond with a bid but we got a timeout notification

sandbox.stub($$PREBID_GLOBAL$$, 'getHighestCpmBids').callsFake((key) => {
return [MOCK.BID_RESPONSE[1]]
});

events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]);
Expand Down Expand Up @@ -492,7 +617,7 @@ describe('pubmatic analytics adapter', function () {
expect(data.s[1].ps[0].l2).to.equal(0);
expect(data.s[1].ps[0].ss).to.equal(1);
expect(data.s[1].ps[0].t).to.equal(1);
expect(data.s[1].ps[0].wb).to.equal(1);
expect(data.s[1].ps[0].wb).to.equal(1); // todo
expect(data.s[1].ps[0].af).to.equal('banner');
expect(data.s[1].ps[0].ocpm).to.equal(1.52);
expect(data.s[1].ps[0].ocry).to.equal('USD');
Expand Down Expand Up @@ -538,8 +663,8 @@ describe('pubmatic analytics adapter', function () {
expect(data.s[1].ps[0].kgpv).to.equal('this-is-a-kgpv');
expect(data.s[1].ps[0].kgpsv).to.equal('this-is-a-kgpv');
expect(data.s[1].ps[0].psz).to.equal('728x90');
expect(data.s[1].ps[0].eg).to.equal(undefined); // bidPriceUSD is not getting set as currency module is not added
expect(data.s[1].ps[0].en).to.equal(undefined); // bidPriceUSD is not getting set as currency module is not added
expect(data.s[1].ps[0].eg).to.equal(1);
expect(data.s[1].ps[0].en).to.equal(100);
expect(data.s[1].ps[0].di).to.equal('the-deal-id');
expect(data.s[1].ps[0].dc).to.equal('PMP');
expect(data.s[1].ps[0].mi).to.equal('matched-impression');
Expand All @@ -552,5 +677,5 @@ describe('pubmatic analytics adapter', function () {
expect(data.s[1].ps[0].ocpm).to.equal(100);
expect(data.s[1].ps[0].ocry).to.equal('JPY');
});
})
});
});

0 comments on commit 352f634

Please sign in to comment.