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

Media.net Analytics improvements [Bug Fix] #5514

Merged
merged 1 commit into from
Jul 23, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
79 changes: 66 additions & 13 deletions modules/medianetAnalyticsAdapter.js
Expand Up @@ -4,7 +4,7 @@ import CONSTANTS from '../src/constants.json';
import * as utils from '../src/utils.js';
import { ajax } from '../src/ajax.js';
import { getRefererInfo } from '../src/refererDetection.js';
import { getPriceGranularity, AUCTION_IN_PROGRESS, AUCTION_COMPLETED } from '../src/auction.js'
import { AUCTION_COMPLETED, AUCTION_IN_PROGRESS, getPriceGranularity } from '../src/auction.js'

const analyticsType = 'endpoint';
const ENDPOINT = 'https://pb-logs.media.net/log?logid=kfk&evtid=prebid_analytics_events_client';
Expand All @@ -29,6 +29,7 @@ const ERROR_CONFIG_FETCH = 'analytics_config_ajax_fail';
const BID_SUCCESS = 1;
const BID_NOBID = 2;
const BID_TIMEOUT = 3;
const BID_FLOOR_REJECTED = 12;
const DUMMY_BIDDER = '-2';

const CONFIG_PENDING = 0;
Expand Down Expand Up @@ -151,7 +152,7 @@ class PageDetail {
this.canonical_url = canonicalUrl;
this.og_url = ogUrl;
this.twitter_url = twitterUrl;
this.screen = this._getWindowSize()
this.screen = this._getWindowSize();
}

_getTopWindowReferrer() {
Expand Down Expand Up @@ -201,9 +202,9 @@ class PageDetail {
}

class AdSlot {
constructor(mediaTypes, bannerSizes, tmax, supplyAdCode, adext) {
constructor(mediaTypes, allMediaTypeSizes, tmax, supplyAdCode, adext, context, adSize) {
this.mediaTypes = mediaTypes;
this.bannerSizes = bannerSizes;
this.allMediaTypeSizes = allMediaTypeSizes;
this.tmax = tmax;
this.supplyAdCode = supplyAdCode;
this.adext = adext;
Expand All @@ -213,6 +214,8 @@ class AdSlot {
// shouldBeLogged is assigned when requested,
// since we are waiting for logging percent response
this.shouldBeLogged = undefined;
this.context = context;
this.adSize = adSize; // old ad unit sizes
}

getShouldBeLogged() {
Expand All @@ -226,10 +229,12 @@ class AdSlot {
return Object.assign({
supcrid: this.supplyAdCode,
mediaTypes: this.mediaTypes && this.mediaTypes.join('|'),
szs: this.bannerSizes.join('|'),
szs: this.allMediaTypeSizes.map(sz => sz.join('x')).join('|'),
tmax: this.tmax,
targ: JSON.stringify(this.targeting),
ismn: this.medianetPresent
ismn: this.medianetPresent,
vplcmtt: this.context,
sz2: this.adSize.map(sz => sz.join('x')).join('|'),
},
this.adext && {'adext': JSON.stringify(this.adext)},
);
Expand Down Expand Up @@ -261,6 +266,8 @@ class Bid {
this.crid = undefined;
this.pubcrid = undefined;
this.mpvid = undefined;
this.floorPrice = undefined;
this.floorRule = undefined;
}

get size() {
Expand Down Expand Up @@ -290,6 +297,8 @@ class Bid {
crid: this.crid,
pubcrid: this.pubcrid,
mpvid: this.mpvid,
bidflr: this.floorPrice,
flrrule: this.floorRule,
ext: JSON.stringify(this.ext)
}
}
Expand All @@ -306,6 +315,7 @@ class Auction {
this.setTargetingTime = undefined;
this.auctionEndTime = undefined;
this.bidWonTime = undefined;
this.floorData = {};
}

hasEnded() {
Expand All @@ -319,13 +329,22 @@ class Auction {
tts: this.setTargetingTime - this.auctionInitTime,
wts: this.bidWonTime - this.auctionInitTime,
aucstatus: this.status,
acid: this.acid
acid: this.acid,
flrdata: this._mergeFieldsToLog({
ln: this.floorData.location,
skp: this.floorData.skipped,
enfj: utils.deepAccess(this.floorData, 'enforcements.enforceJS'),
enfd: utils.deepAccess(this.floorData, 'enforcements.floorDeals'),
sr: this.floorData.skipRate,
fs: this.floorData.fetchStatus
}),
flrver: this.floorData.modelVersion
}
}

addSlot(supplyAdCode, { mediaTypes, bannerSizes, tmax, adext }) {
addSlot(supplyAdCode, { mediaTypes, allMediaTypeSizes, tmax, adext, context, adSize }) {
if (supplyAdCode && this.adSlots[supplyAdCode] === undefined) {
this.adSlots[supplyAdCode] = new AdSlot(mediaTypes, bannerSizes, tmax, supplyAdCode, adext);
this.adSlots[supplyAdCode] = new AdSlot(mediaTypes, allMediaTypeSizes, tmax, supplyAdCode, adext, context, adSize);
this.addBid(
new Bid('-1', DUMMY_BIDDER, 'client', '-1', supplyAdCode)
);
Expand All @@ -351,13 +370,27 @@ class Auction {
getWinnerAdslotBid(adslot) {
return this.getAdslotBids(adslot).filter((bid) => bid.winner);
}

_mergeFieldsToLog(objParams) {
let logParams = [];
let value;
for (const param of Object.keys(objParams)) {
value = objParams[param];
logParams.push(param + '=' + (value === undefined ? '' : value));
}
return logParams.join('||');
}
}

function auctionInitHandler({auctionId, timestamp}) {
function auctionInitHandler({auctionId, timestamp, bidderRequests}) {
if (auctionId && auctions[auctionId] === undefined) {
auctions[auctionId] = new Auction(auctionId);
auctions[auctionId].auctionInitTime = timestamp;
}
const floorData = utils.deepAccess(bidderRequests, '0.bids.0.floorData');
if (floorData) {
auctions[auctionId].floorData = {...floorData};
}
}

function bidRequestedHandler({ auctionId, auctionStart, bids, start, timeout, uspConsent, gdpr }) {
Expand All @@ -375,13 +408,16 @@ function bidRequestedHandler({ auctionId, auctionStart, bids, start, timeout, us
const { adUnitCode, bidder, mediaTypes, sizes, bidId, src } = bid;
if (!auctions[auctionId].adSlots[adUnitCode]) {
auctions[auctionId].auctionStartTime = auctionStart;
const sizeObject = _getSizes(mediaTypes, sizes);
auctions[auctionId].addSlot(
adUnitCode,
Object.assign({},
(mediaTypes instanceof Object) && { mediaTypes: Object.keys(mediaTypes) },
{ bannerSizes: utils.deepAccess(mediaTypes, 'banner.sizes') || sizes || [] },
{ allMediaTypeSizes: [].concat(sizeObject.banner, sizeObject.native, sizeObject.video) },
{ adext: utils.deepAccess(mediaTypes, 'banner.ext') || '' },
{ tmax: timeout }
{ tmax: timeout },
{ context: utils.deepAccess(mediaTypes, 'video.context') || '' },
{ adSize: sizeObject.banner }
)
);
}
Expand All @@ -395,6 +431,17 @@ function bidRequestedHandler({ auctionId, auctionStart, bids, start, timeout, us
});
}

function _getSizes(mediaTypes, sizes) {
const banner = utils.deepAccess(mediaTypes, 'banner.sizes') || sizes || [];
const native = utils.deepAccess(mediaTypes, 'native') ? [[1, 1]] : [];
const playerSize = utils.deepAccess(mediaTypes, 'video.playerSize') || [];
let video = [];
if (playerSize.length === 2) {
video = [playerSize]
}
return { banner, native, video }
}

function bidResponseHandler(bid) {
const { width, height, mediaType, cpm, requestId, timeToRespond, auctionId, dealId } = bid;
const {originalCpm, bidderCode, creativeId, adId, currency} = bid;
Expand All @@ -411,6 +458,8 @@ function bidResponseHandler(bid) {
{ cpm, width, height, mediaType, timeToRespond, dealId, creativeId },
{ adId, currency }
);
bidObj.floorPrice = utils.deepAccess(bid, 'floorData.floorValue');
bidObj.floorRule = utils.deepAccess(bid, 'floorData.floorRule');
bidObj.originalCpm = originalCpm || cpm;
let dfpbd = utils.deepAccess(bid, 'adserverTargeting.hb_pb');
if (!dfpbd) {
Expand All @@ -419,7 +468,11 @@ function bidResponseHandler(bid) {
dfpbd = bid[priceGranularityKey] || cpm;
}
bidObj.dfpbd = dfpbd;
bidObj.status = BID_SUCCESS;
if (bid.status === CONSTANTS.BID_STATUS.BID_REJECTED) {
bidObj.status = BID_FLOOR_REJECTED;
} else {
bidObj.status = BID_SUCCESS;
}

if (bidderCode === MEDIANET_BIDDER_CODE && bid.ext instanceof Object) {
Object.assign(
Expand Down
51 changes: 49 additions & 2 deletions test/spec/modules/medianetAnalyticsAdapter_spec.js
Expand Up @@ -10,15 +10,26 @@ const {

const MOCK = {
AUCTION_INIT: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'timestamp': 1584563605739},
AUCTION_INIT_WITH_FLOOR: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'timestamp': 1584563605739, 'bidderRequests': [{'bids': [{ 'floorData': {'enforcements': {'enforceJS': true}} }]}]},
BID_REQUESTED: {'bidderCode': 'medianet', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'medianet', 'params': {'cid': 'TEST_CID', 'crid': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]], 'ext': ['asdads']}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aece2', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743},
BID_RESPONSE: {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]},
MULTI_FORMAT_BID_REQUESTED: {'bidderCode': 'medianet', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'bids': [{'bidder': 'medianet', 'params': {'cid': 'TEST_CID', 'crid': '451466393'}, 'mediaTypes': {'banner': {'sizes': [[300, 250]]}, 'video': {'playerSize': [640, 480], 'context': 'instream'}, 'native': {'image': {'required': true, 'sizes': [150, 50]}, 'title': {'required': true, 'len': 80}}, 'ext': ['asdads']}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'sizes': [[300, 250]], 'bidId': '28248b0e6aece2', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'}], 'auctionStart': 1584563605739, 'timeout': 6000, 'uspConsent': '1YY', 'start': 1584563605743},
BID_RESPONSE: {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'ext': {'pvid': 123, 'crid': '321'}, 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'floorData': {'floorValue': 1.10, 'floorRule': 'banner'}, 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]},
AUCTION_END: {'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'auctionEnd': 1584563605739},
SET_TARGETING: {'div-gpt-ad-1460505748561-0': {'prebid_test': '1', 'hb_format': 'banner', 'hb_source': 'client', 'hb_size': '300x250', 'hb_pb': '2.00', 'hb_adid': '3e6e4bce5c8fb3', 'hb_bidder': 'medianet', 'hb_format_medianet': 'banner', 'hb_source_medianet': 'client', 'hb_size_medianet': '300x250', 'hb_pb_medianet': '2.00', 'hb_adid_medianet': '3e6e4bce5c8fb3', 'hb_bidder_medianet': 'medianet'}},
BID_WON: {'bidderCode': 'medianet', 'width': 300, 'height': 250, 'statusMessage': 'Bid available', 'adId': '3e6e4bce5c8fb3', 'requestId': '28248b0e6aece2', 'mediaType': 'banner', 'source': 'client', 'no_bid': false, 'cpm': 2.299, 'ad': 'AD_CODE', 'ttl': 180, 'creativeId': 'Test1', 'netRevenue': true, 'currency': 'USD', 'dfp_id': 'div-gpt-ad-1460505748561-0', 'originalCpm': 1.1495, 'originalCurrency': 'USD', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'responseTimestamp': 1584563606009, 'requestTimestamp': 1584563605743, 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'timeToRespond': 266, 'pbLg': '2.00', 'pbMg': '2.20', 'pbHg': '2.29', 'pbAg': '2.25', 'pbDg': '2.29', 'pbCg': '2.00', 'size': '300x250', 'adserverTargeting': {'hb_bidder': 'medianet', 'hb_adid': '3e6e4bce5c8fb3', 'hb_pb': '2.00', 'hb_size': '300x250', 'hb_source': 'client', 'hb_format': 'banner', 'prebid_test': 1}, 'status': 'rendered', 'params': [{'cid': 'test123', 'crid': '451466393'}]},
NO_BID: {'bidder': 'medianet', 'params': {'cid': 'test123', 'crid': '451466393', 'site': {}}, 'mediaTypes': {'banner': {'sizes': [[300, 250]], 'ext': ['asdads']}}, 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'transactionId': '303fa0c6-682f-4aea-8e4a-dc68f0d5c7d5', 'sizes': [[300, 250], [300, 600]], 'bidId': '28248b0e6aece2', 'bidderRequestId': '13fccf3809fe43', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'src': 'client'},
BID_TIMEOUT: [{'bidId': '28248b0e6aece2', 'bidder': 'medianet', 'adUnitCode': 'div-gpt-ad-1460505748561-0', 'auctionId': '8e0d5245-deb3-406c-96ca-9b609e077ff7', 'params': [{'cid': 'test123', 'crid': '451466393', 'site': {}}, {'cid': '8CUX0H51P', 'crid': '451466393', 'site': {}}], 'timeout': 6}]
}

function performAuctionWithFloorConfig() {
events.emit(AUCTION_INIT, MOCK.AUCTION_INIT_WITH_FLOOR);
events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
events.emit(BID_RESPONSE, MOCK.BID_RESPONSE);
events.emit(AUCTION_END, MOCK.AUCTION_END);
events.emit(SET_TARGETING, MOCK.SET_TARGETING);
events.emit(BID_WON, MOCK.BID_WON);
}

function performStandardAuctionWithWinner() {
events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
Expand All @@ -28,6 +39,14 @@ function performStandardAuctionWithWinner() {
events.emit(BID_WON, MOCK.BID_WON);
}

function performMultiFormatAuctionWithNoBid() {
events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
events.emit(BID_REQUESTED, MOCK.MULTI_FORMAT_BID_REQUESTED);
events.emit(BID_RESPONSE, MOCK.BID_RESPONSE);
events.emit(AUCTION_END, MOCK.AUCTION_END);
events.emit(SET_TARGETING, MOCK.SET_TARGETING);
}

function performStandardAuctionWithNoBid() {
events.emit(AUCTION_INIT, MOCK.AUCTION_INIT);
events.emit(BID_REQUESTED, MOCK.BID_REQUESTED);
Expand Down Expand Up @@ -115,6 +134,17 @@ describe('Media.net Analytics Adapter', function() {
expect(medianetAnalytics.getlogsQueue().length).to.equal(0);
});

it('should have all applicable sizes in request', function() {
medianetAnalytics.clearlogsQueue();
performMultiFormatAuctionWithNoBid();
const noBidLog = medianetAnalytics.getlogsQueue().map((log) => getQueryData(log))[0];
medianetAnalytics.clearlogsQueue();

expect(noBidLog.szs).to.equal(encodeURIComponent('300x250|1x1|640x480'));
expect(noBidLog.vplcmtt).to.equal('instream');
expect(noBidLog.sz2).to.equal(encodeURIComponent('300x250'));
});

it('should have winner log in standard auction', function() {
medianetAnalytics.clearlogsQueue();
performStandardAuctionWithWinner();
Expand Down Expand Up @@ -142,7 +172,24 @@ describe('Media.net Analytics Adapter', function() {
ogbdp: '1.1495',
flt: '1',
supcrid: 'div-gpt-ad-1460505748561-0',
mpvid: '123'
mpvid: '123',
bidflr: '1.1'
});
});

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);
medianetAnalytics.clearlogsQueue();

expect(winnerLog[0]).to.include({
winner: '1',
curr: 'USD',
ogbdp: '1.1495',
bidflr: '1.1',
flrrule: 'banner',
flrdata: encodeURIComponent('ln=||skp=||enfj=true||enfd=||sr=||fs=')
});
});

Expand Down