From c01ae57f15bad93285a0ae19556a7f2b366f8167 Mon Sep 17 00:00:00 2001 From: robertrmartinez Date: Wed, 9 Jun 2021 13:46:28 -0700 Subject: [PATCH] Enhancements for the analytics adapter --- modules/rubiconAnalyticsAdapter.js | 41 ++++++++++++++----- .../modules/rubiconAnalyticsAdapter_spec.js | 15 ++++++- test/spec/modules/rubiconAnalyticsSchema.json | 5 --- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/modules/rubiconAnalyticsAdapter.js b/modules/rubiconAnalyticsAdapter.js index 5a2e02b8f89..24e6558e75c 100644 --- a/modules/rubiconAnalyticsAdapter.js +++ b/modules/rubiconAnalyticsAdapter.js @@ -115,7 +115,7 @@ function formatSource(src) { return src.toLowerCase(); } -function sendMessage(auctionId, bidWonId) { +function sendMessage(auctionId, bidWonId, trigger) { function formatBid(bid) { return utils.pick(bid, [ 'bidder', @@ -163,7 +163,12 @@ function sendMessage(auctionId, bidWonId) { let auctionCache = cache.auctions[auctionId]; let referrer = config.getConfig('pageUrl') || (auctionCache && auctionCache.referrer); let message = { - eventTimeMillis: Date.now(), + timestamps: { + prebidLoaded: rubiconAdapter.MODULE_INITIALIZED_TIME, + auctionEnded: auctionCache.endTs, + eventTime: Date.now() + }, + trigger, integration: rubiConf.int_type || DEFAULT_INTEGRATION, version: '$prebid.version$', referrerUri: referrer, @@ -319,6 +324,10 @@ function sendMessage(auctionId, bidWonId) { ); } +function adUnitIsOnlyInstream(adUnit) { + return adUnit.mediaTypes && Object.keys(adUnit.mediaTypes).length === 1 && utils.deepAccess(adUnit, 'mediaTypes.video.context') === 'instream'; +} + function getBidPrice(bid) { // get the cpm from bidResponse let cpm; @@ -498,9 +507,9 @@ function subscribeToGamSlots() { clearTimeout(cache.timeouts[auctionId]); delete cache.timeouts[auctionId]; if (rubiConf.analyticsEventDelay > 0) { - setTimeout(() => sendMessage.call(rubiconAdapter, auctionId), rubiConf.analyticsEventDelay) + setTimeout(() => sendMessage.call(rubiconAdapter, auctionId, undefined, 'delayedGam'), rubiConf.analyticsEventDelay) } else { - sendMessage.call(rubiconAdapter, auctionId) + sendMessage.call(rubiconAdapter, auctionId, undefined, 'gam') } } }); @@ -509,6 +518,7 @@ function subscribeToGamSlots() { let baseAdapter = adapter({analyticsType: 'endpoint'}); let rubiconAdapter = Object.assign({}, baseAdapter, { + MODULE_INITIALIZED_TIME: Date.now(), referrerHostname: '', enableAnalytics(config = {}) { let error = false; @@ -594,7 +604,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { // mark adUnits we expect bidWon events for cache.auctions[args.auctionId].bidsWon[bid.adUnitCode] = false; - if (rubiConf.waitForGamSlots) { + if (rubiConf.waitForGamSlots && !adUnitIsOnlyInstream(bid)) { cache.auctions[args.auctionId].gamHasRendered[bid.adUnitCode] = false; } @@ -750,7 +760,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { // check if this BID_WON missed the boat, if so send by itself if (auctionCache.sent === true) { - sendMessage.call(this, args.auctionId, args.requestId); + sendMessage.call(this, args.auctionId, args.requestId, 'soloBidWon'); } else if (!rubiConf.waitForGamSlots && Object.keys(auctionCache.bidsWon).reduce((memo, adUnitCode) => { // only send if we've received bidWon events for all adUnits in auction memo = memo && auctionCache.bidsWon[adUnitCode]; @@ -759,14 +769,23 @@ let rubiconAdapter = Object.assign({}, baseAdapter, { clearTimeout(cache.timeouts[args.auctionId]); delete cache.timeouts[args.auctionId]; - sendMessage.call(this, args.auctionId); + sendMessage.call(this, args.auctionId, undefined, 'allBidWons'); } break; case AUCTION_END: - // start timer to send batched payload just in case we don't hear any BID_WON events - cache.timeouts[args.auctionId] = setTimeout(() => { - sendMessage.call(this, args.auctionId); - }, rubiConf.analyticsBatchTimeout || SEND_TIMEOUT); + // see how long it takes for the payload to come fire + cache.auctions[args.auctionId].endTs = Date.now(); + + const isOnlyInstreamAuction = args.adUnits && args.adUnits.every(adUnit => adUnitIsOnlyInstream(adUnit)); + // If only instream, do not wait around, just send payload + if (isOnlyInstreamAuction) { + sendMessage.call(this, args.auctionId, undefined, 'instreamAuction'); + } else { + // start timer to send batched payload just in case we don't hear any BID_WON events + cache.timeouts[args.auctionId] = setTimeout(() => { + sendMessage.call(this, args.auctionId, undefined, 'auctionEnd'); + }, rubiConf.analyticsBatchTimeout || SEND_TIMEOUT); + } break; case BID_TIMEOUT: args.forEach(badBid => { diff --git a/test/spec/modules/rubiconAnalyticsAdapter_spec.js b/test/spec/modules/rubiconAnalyticsAdapter_spec.js index df8bf03b56a..d8aa9ede9a5 100644 --- a/test/spec/modules/rubiconAnalyticsAdapter_spec.js +++ b/test/spec/modules/rubiconAnalyticsAdapter_spec.js @@ -274,7 +274,12 @@ const MOCK = { } } }, - 'mediaType': 'video', + 'mediaTypes': { + 'video': { + 'context': 'instream', + 'playerSize': [640, 480] + } + }, 'adUnitCode': '/19968336/header-bid-tag-0', 'transactionId': 'ca4af27a-6d02-4f90-949d-d5541fa12014', 'sizes': [[640, 480]], @@ -362,7 +367,6 @@ const STUBBED_UUID = '12345678-1234-1234-1234-123456789abc'; const ANALYTICS_MESSAGE = { 'channel': 'web', - 'eventTimeMillis': 1519767013781, 'integration': 'pbjs', 'version': '$prebid.version$', 'referrerUri': 'http://www.test.com/page.html', @@ -371,6 +375,12 @@ const ANALYTICS_MESSAGE = { 'id': STUBBED_UUID, 'start': 1519767013781 }, + 'timestamps': { + 'auctionEnded': 1519767013781, + 'eventTime': 1519767013781, + 'prebidLoaded': rubiconAnalyticsAdapter.MODULE_INITIALIZED_TIME + }, + 'trigger': 'allBidWons', 'referrerHostname': 'www.test.com', 'auctions': [ { @@ -1658,6 +1668,7 @@ describe('rubicon analytics adapter', function () { lineItemId: 6666, adSlot: '/19968336/header-bid-tag1' }; + expectedMessage.trigger = 'gam'; expect(message).to.deep.equal(expectedMessage); }); diff --git a/test/spec/modules/rubiconAnalyticsSchema.json b/test/spec/modules/rubiconAnalyticsSchema.json index 13d39e46cd0..a8fdeae5268 100644 --- a/test/spec/modules/rubiconAnalyticsSchema.json +++ b/test/spec/modules/rubiconAnalyticsSchema.json @@ -4,7 +4,6 @@ "description": "A batched data object describing the lifecycle of an auction or multiple auction across a single page view.", "type": "object", "required": [ - "eventTimeMillis", "integration", "version" ], @@ -21,10 +20,6 @@ } ], "properties": { - "eventTimeMillis": { - "type": "integer", - "description": "Unix timestamp of time of creation for this batched event in milliseconds." - }, "integration": { "type": "string", "description": "Integration type that generated this event.",