From 6ca71ac08b1f3a931828c82248369208cdd808de Mon Sep 17 00:00:00 2001 From: Antonios Sarhanis Date: Mon, 25 Nov 2024 15:11:05 +1100 Subject: [PATCH] Adding Adnuntius as an analytics adapter. --- modules/adnuntiusAnalyticsAdapter.js | 424 ++++++++++++ modules/adnuntiusAnalyticsAdapter.md | 22 + .../modules/adnuntiusAnalyticsAdapter_spec.js | 636 ++++++++++++++++++ 3 files changed, 1082 insertions(+) create mode 100644 modules/adnuntiusAnalyticsAdapter.js create mode 100644 modules/adnuntiusAnalyticsAdapter.md create mode 100644 test/spec/modules/adnuntiusAnalyticsAdapter_spec.js diff --git a/modules/adnuntiusAnalyticsAdapter.js b/modules/adnuntiusAnalyticsAdapter.js new file mode 100644 index 000000000000..e765b48c671e --- /dev/null +++ b/modules/adnuntiusAnalyticsAdapter.js @@ -0,0 +1,424 @@ +import { timestamp, logInfo } from '../src/utils.js'; +import {ajax} from '../src/ajax.js'; +import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import { EVENTS, STATUS } from '../src/constants.js'; +import adapterManager from '../src/adapterManager.js'; + +const URL = 'https://analytics.adnuntius.com/prebid'; +const REQUEST_SENT = 1; +const RESPONSE_SENT = 2; +const WIN_SENT = 4; +const TIMEOUT_SENT = 8; +const AD_RENDER_FAILED_SENT = 16; + +let initOptions; +export const BID_WON_TIMEOUT = 500; + +const cache = { + auctions: {} +}; + +const adnAnalyticsAdapter = Object.assign(adapter({url: '', analyticsType: 'endpoint'}), { + track({eventType, args}) { + const time = timestamp(); + logInfo('ADN_EVENT:', [eventType, args]); + + switch (eventType) { + case EVENTS.AUCTION_INIT: + logInfo('ADN_AUCTION_INIT:', args); + cache.auctions[args.auctionId] = {bids: {}, bidAdUnits: {}}; + break; + case EVENTS.BID_REQUESTED: + logInfo('ADN_BID_REQUESTED:', args); + cache.auctions[args.auctionId].timeStamp = args.start; + + args.bids.forEach(function(bidReq) { + cache.auctions[args.auctionId].gdprApplies = args.gdprConsent ? args.gdprConsent.gdprApplies : undefined; + cache.auctions[args.auctionId].gdprConsent = args.gdprConsent ? args.gdprConsent.consentString : undefined; + let lwFloor; + + const container = document.getElementById(bidReq.adUnitCode); + const containerAttr = container ? container.getAttribute('data-adunitid') : undefined; + const adUnitId = containerAttr || undefined; + + if (bidReq.lwflr) { + lwFloor = bidReq.lwflr.flr; + + const buyerFloor = bidReq.lwflr.bflrs ? bidReq.lwflr.bflrs[bidReq.bidder] : undefined; + lwFloor = buyerFloor || lwFloor; + } + + cache.auctions[args.auctionId].bids[bidReq.bidId] = { + bidder: bidReq.bidder, + adUnit: bidReq.adUnitCode, + adUnitId: adUnitId, + isBid: false, + won: false, + timeout: false, + sendStatus: 0, + readyToSend: 0, + start: args.start, + lwFloor: lwFloor, + floorData: bidReq.floorData, + auc: bidReq.auc, + buc: bidReq.buc, + lw: bidReq.lw + }; + + logInfo(bidReq); + }); + logInfo(adnAnalyticsAdapter.requestEvents); + break; + case EVENTS.BID_RESPONSE: + logInfo('ADN_BID_RESPONSE:', args); + + const bidResp = cache.auctions[args.auctionId].bids[args.requestId]; + bidResp.isBid = args.getStatusCode() === STATUS.GOOD; + bidResp.width = args.width; + bidResp.height = args.height; + bidResp.cpm = args.cpm; + bidResp.currency = args.currency; + bidResp.originalCpm = args.originalCpm; + bidResp.originalCurrency = args.originalCurrency; + bidResp.ttr = args.timeToRespond; + bidResp.readyToSend = 1; + bidResp.mediaType = args.mediaType === 'native' ? 2 : (args.mediaType === 'video' ? 4 : 1); + bidResp.floorData = args.floorData; + bidResp.meta = args.meta; + + if (!bidResp.ttr) { + bidResp.ttr = time - bidResp.start; + } + if (!cache.auctions[args.auctionId].bidAdUnits[bidResp.adUnit]) { + cache.auctions[args.auctionId].bidAdUnits[bidResp.adUnit] = + { + sent: 0, + lw: bidResp.lw, + adUnitId: bidResp.adUnitId, + timeStamp: cache.auctions[args.auctionId].timeStamp + }; + } + break; + case EVENTS.BIDDER_DONE: + logInfo('ADN_BIDDER_DONE:', args); + args.bids.forEach(doneBid => { + let bid = cache.auctions[doneBid.auctionId].bids[doneBid.bidId || doneBid.requestId]; + if (!bid.ttr) { + bid.ttr = time - bid.start; + } + bid.readyToSend = 1; + }); + break; + case EVENTS.BID_WON: + logInfo('ADN_BID_WON:', args); + const wonBid = cache.auctions[args.auctionId].bids[args.requestId]; + wonBid.won = true; + wonBid.floorData = args.floorData; + wonBid.rUp = args.rUp; + wonBid.meta = args.meta; + wonBid.dealId = args.dealId; + if (wonBid.sendStatus !== 0) { + adnAnalyticsAdapter.sendEvents(); + } + break; + case EVENTS.AD_RENDER_SUCCEEDED: + logInfo('ADN_AD_RENDER_SUCCEEDED:', args); + const adRenderSucceeded = cache.auctions[args.bid.auctionId].bids[args.bid.requestId]; + adRenderSucceeded.renderedTimestamp = Date.now(); + break; + case EVENTS.AD_RENDER_FAILED: + logInfo('ADN_AD_RENDER_FAILED:', args); + const adRenderFailedBid = cache.auctions[args.bid.auctionId].bids[args.bid.requestId]; + adRenderFailedBid.adRenderFailed = true; + adRenderFailedBid.reason = args.reason; + adRenderFailedBid.message = args.message; + if (adRenderFailedBid.sendStatus !== 0) { + adnAnalyticsAdapter.sendEvents(); + } + break; + case EVENTS.BID_TIMEOUT: + logInfo('ADN_BID_TIMEOUT:', args); + args.forEach(timeout => { + cache.auctions[timeout.auctionId].bids[timeout.bidId].timeout = true; + }); + break; + case EVENTS.AUCTION_END: + logInfo('ADN_AUCTION_END:', args); + setTimeout(() => { + adnAnalyticsAdapter.sendEvents(); + }, BID_WON_TIMEOUT); + break; + } + } +}); + +// save the base class function +adnAnalyticsAdapter.originEnableAnalytics = adnAnalyticsAdapter.enableAnalytics; +adnAnalyticsAdapter.allRequestEvents = []; + +// override enableAnalytics so we can get access to the config passed in from the page +adnAnalyticsAdapter.enableAnalytics = function (config) { + initOptions = config.options; + adnAnalyticsAdapter.originEnableAnalytics(config); +}; + +adnAnalyticsAdapter.sendEvents = function() { + const sentRequests = getSentRequests(); + const events = { + publisherId: initOptions.publisherId, + gdpr: sentRequests.gdpr, + auctionIds: sentRequests.auctionIds, + requests: sentRequests.sentRequests, + responses: getResponses(sentRequests.gdpr, sentRequests.auctionIds), + wins: getWins(sentRequests.gdpr, sentRequests.auctionIds), + timeouts: getTimeouts(sentRequests.gdpr, sentRequests.auctionIds), + bidAdUnits: getBidAdUnits(), + rf: getAdRenderFailed(sentRequests.auctionIds), + ext: initOptions.ext + }; + + if (events.requests.length === 0 && events.responses.length === 0 && events.wins.length === 0 && events.timeouts.length === 0 && events.rf.length === 0) { + return; + } + + ajax(initOptions.endPoint || URL, undefined, JSON.stringify(events), {method: 'POST'}); +}; + +function getSentRequests() { + const sentRequests = []; + const gdpr = []; + const auctionIds = []; + + Object.keys(cache.auctions).forEach(auctionId => { + const auction = cache.auctions[auctionId]; + const gdprPos = getGdprPos(gdpr, auction); + const auctionIdPos = getAuctionIdPos(auctionIds, auctionId); + + Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { + let bid = auction.bids[bidId]; + if (!(bid.sendStatus & REQUEST_SENT)) { + bid.sendStatus |= REQUEST_SENT; + + sentRequests.push({ + adUnit: bid.adUnit, + adUnitId: bid.adUnitId, + bidder: bid.bidder, + timeStamp: auction.timeStamp, + gdpr: gdprPos, + floor: bid.lwFloor, + auctionId: auctionIdPos, + auc: bid.auc, + buc: bid.buc, + lw: bid.lw + }); + } + }); + }); + + return {gdpr: gdpr, auctionIds: auctionIds, sentRequests: sentRequests}; +} + +function getResponses(gdpr, auctionIds) { + const responses = []; + + Object.keys(cache.auctions).forEach(auctionId => { + Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { + let auction = cache.auctions[auctionId]; + let gdprPos = getGdprPos(gdpr, auction); + let auctionIdPos = getAuctionIdPos(auctionIds, auctionId) + let bid = auction.bids[bidId]; + if (bid.readyToSend && !(bid.sendStatus & RESPONSE_SENT) && !bid.timeout) { + bid.sendStatus |= RESPONSE_SENT; + + let response = getResponseObject(auction, bid, gdprPos, auctionIdPos); + + responses.push(response); + } + }); + }); + + return responses; +} + +function getWins(gdpr, auctionIds) { + const wins = []; + + Object.keys(cache.auctions).forEach(auctionId => { + Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { + const auction = cache.auctions[auctionId]; + const gdprPos = getGdprPos(gdpr, auction); + const auctionIdPos = getAuctionIdPos(auctionIds, auctionId); + const bid = auction.bids[bidId]; + + if (!(bid.sendStatus & WIN_SENT) && bid.won) { + bid.sendStatus |= WIN_SENT; + + wins.push({ + adUnit: bid.adUnit, + adUnitId: bid.adUnitId, + bidder: bid.bidder, + timeStamp: auction.timeStamp, + width: bid.width, + height: bid.height, + cpm: bid.cpm, + currency: bid.currency, + originalCpm: bid.originalCpm, + originalCurrency: bid.originalCurrency, + mediaType: bid.mediaType, + gdpr: gdprPos, + floor: bid.lwFloor ? bid.lwFloor : (bid.floorData ? bid.floorData.floorValue : undefined), + floorCur: bid.floorData ? bid.floorData.floorCurrency : undefined, + auctionId: auctionIdPos, + auc: bid.auc, + lw: bid.lw, + buc: bid.buc, + rUp: bid.rUp, + meta: bid.meta, + dealId: bid.dealId + }); + } + }); + }); + + return wins; +} + +function getGdprPos(gdpr, auction) { + let gdprPos; + for (gdprPos = 0; gdprPos < gdpr.length; gdprPos++) { + if (gdpr[gdprPos].gdprApplies === auction.gdprApplies && + gdpr[gdprPos].gdprConsent === auction.gdprConsent) { + break; + } + } + + if (gdprPos === gdpr.length) { + gdpr[gdprPos] = {gdprApplies: auction.gdprApplies, gdprConsent: auction.gdprConsent}; + } + + return gdprPos; +} + +function getAuctionIdPos(auIds, auId) { + let auctionIdPos; + for (auctionIdPos = 0; auctionIdPos < auIds.length; auctionIdPos++) { + if (auIds[auctionIdPos] === auId) { + break; + } + } + + if (auctionIdPos === auIds.length) { + auIds[auctionIdPos] = auId; + } + + return auctionIdPos; +} + +function getResponseObject(auction, bid, gdprPos, auctionIdPos) { + return { + adUnit: bid.adUnit, + adUnitId: bid.adUnitId, + bidder: bid.bidder, + timeStamp: auction.timeStamp, + renderedTimestamp: bid.renderedTimestamp, + width: bid.width, + height: bid.height, + cpm: bid.cpm, + currency: bid.currency, + originalCpm: bid.originalCpm, + originalCurrency: bid.originalCurrency, + ttr: bid.ttr, + isBid: bid.isBid, + mediaType: bid.mediaType, + gdpr: gdprPos, + floor: bid.lwFloor ? bid.lwFloor : (bid.floorData ? bid.floorData.floorValue : undefined), + floorCur: bid.floorData ? bid.floorData.floorCurrency : undefined, + auctionId: auctionIdPos, + auc: bid.auc, + buc: bid.buc, + lw: bid.lw, + meta: bid.meta + }; +} + +function getTimeouts(gdpr, auctionIds) { + const timeouts = []; + + Object.keys(cache.auctions).forEach(auctionId => { + const auctionIdPos = getAuctionIdPos(auctionIds, auctionId); + Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { + const auction = cache.auctions[auctionId]; + const gdprPos = getGdprPos(gdpr, auction); + const bid = auction.bids[bidId]; + if (!(bid.sendStatus & TIMEOUT_SENT) && bid.timeout) { + bid.sendStatus |= TIMEOUT_SENT; + + let timeout = getResponseObject(auction, bid, gdprPos, auctionIdPos); + + timeouts.push(timeout); + } + }); + }); + + return timeouts; +} + +function getAdRenderFailed(auctionIds) { + const adRenderFails = []; + + Object.keys(cache.auctions).forEach(auctionId => { + const auctionIdPos = getAuctionIdPos(auctionIds, auctionId); + Object.keys(cache.auctions[auctionId].bids).forEach(bidId => { + const auction = cache.auctions[auctionId]; + const bid = auction.bids[bidId]; + if (!(bid.sendStatus & AD_RENDER_FAILED_SENT) && bid.adRenderFailed) { + bid.sendStatus |= AD_RENDER_FAILED_SENT; + + adRenderFails.push({ + bidder: bid.bidder, + adUnit: bid.adUnit, + adUnitId: bid.adUnitId, + timeStamp: auction.timeStamp, + auctionId: auctionIdPos, + auc: bid.auc, + buc: bid.buc, + lw: bid.lw, + rsn: bid.reason, + msg: bid.message + }); + } + }); + }); + + return adRenderFails; +} + +function getBidAdUnits() { + const bidAdUnits = []; + + Object.keys(cache.auctions).forEach(auctionId => { + const auction = cache.auctions[auctionId]; + Object.keys(auction.bidAdUnits).forEach(adUnit => { + const bidAdUnit = auction.bidAdUnits[adUnit]; + if (!bidAdUnit.sent) { + bidAdUnit.sent = 1; + + bidAdUnits.push({ + adUnit: adUnit, + adUnitId: bidAdUnit.adUnitId, + timeStamp: bidAdUnit.timeStamp, + lw: bidAdUnit.lw + }); + } + }); + }); + + return bidAdUnits; +} + +adapterManager.registerAnalyticsAdapter({ + adapter: adnAnalyticsAdapter, + code: 'adnuntius' +}); + +export default adnAnalyticsAdapter; diff --git a/modules/adnuntiusAnalyticsAdapter.md b/modules/adnuntiusAnalyticsAdapter.md new file mode 100644 index 000000000000..a0d931529ad9 --- /dev/null +++ b/modules/adnuntiusAnalyticsAdapter.md @@ -0,0 +1,22 @@ +# Overview +Module Name: Adnuntius Analytics Adapter + +Module Type: Analytics Adapter + +Maintainer: ops@adnuntius.com + +# Description + +Analytics adapter for Adnuntius AS. Contact Adnuntius AS in order to use the adapter. + +# Test Parameters + +``` +{ + provider: 'adnuntius', + options : { + publisherId: "contact-adnuntius-for-this-id" + } +} + +``` diff --git a/test/spec/modules/adnuntiusAnalyticsAdapter_spec.js b/test/spec/modules/adnuntiusAnalyticsAdapter_spec.js new file mode 100644 index 000000000000..8f3f42c58852 --- /dev/null +++ b/test/spec/modules/adnuntiusAnalyticsAdapter_spec.js @@ -0,0 +1,636 @@ +import adnAnalyticsAdapter, { BID_WON_TIMEOUT } from 'modules/adnuntiusAnalyticsAdapter.js'; +import { AD_RENDER_FAILED_REASON, EVENTS, STATUS } from 'src/constants.js'; +import { config } from 'src/config.js'; +import { server } from 'test/mocks/xhr.js'; +import { setConfig } from 'modules/currency.js'; + +let events = require('src/events'); +let utils = require('src/utils'); +let adapterManager = require('src/adapterManager').default; + +const { + AUCTION_INIT, + AUCTION_END, + BID_REQUESTED, + BID_RESPONSE, + BIDDER_DONE, + BID_WON, + BID_TIMEOUT, + SET_TARGETING, + AD_RENDER_FAILED +} = EVENTS; + +const BID1 = { + width: 980, + height: 240, + cpm: 1.1, + originalCpm: 12.0, + currency: 'USD', + originalCurrency: 'AUX', + timeToRespond: 200, + bidId: '2ec0db240757', + requestId: '2ec240757', + adId: '2ec240757', + auctionId: '1234-4567-7890', + mediaType: 'banner', + meta: { + data: 'value1' + }, + dealId: 'dealid', + getStatusCode() { + return STATUS.GOOD; + } +}; + +const BID2 = Object.assign({}, BID1, { + width: 300, + height: 250, + cpm: 2.2, + originalCpm: 23.0, + currency: 'USD', + originalCurrency: 'AUX', + timeToRespond: 300, + bidId: '30db240757', + requestId: '30db240757', + adId: '30db240757', + meta: { + data: 'value2' + }, + dealId: undefined +}); + +const BID3 = { + bidId: '4ecff0db240757', + requestId: '4ecff0db240757', + adId: '4ecff0db240757', + auctionId: '1234-4567-7890', + mediaType: 'banner', + getStatusCode() { + return STATUS.GOOD; + } +}; + +const MOCK = { + AUCTION_INIT: { + 'auctionId': '1234-4567-7890', + }, + BID_REQUESTED: { + 'bidder': 'adnuntius', + 'auctionId': '1234-4567-7890', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'adnuntius', + 'adUnitCode': 'panorama_d_1', + 'bidId': '2ec240757', + }, + { + 'bidder': 'adnuntius', + 'adUnitCode': 'box_d_1', + 'bidId': '30db240757', + }, + { + 'bidder': 'adnuntius', + 'adUnitCode': 'box_d_2', + 'bidId': '4ecff0db240757', + } + ], + 'start': 1519149562216 + }, + BID_RESPONSE: [ + BID1, + BID2 + ], + AUCTION_END: { + }, + BID_WON: [ + Object.assign({}, BID1, { + 'status': 'rendered', + 'requestId': '2ec240757' + }), + Object.assign({}, BID2, { + 'status': 'rendered', + 'requestId': '30db240757' + }) + ], + BIDDER_DONE: { + 'bidderCode': 'adnuntius', + 'bids': [ + BID1, + BID2, + BID3 + ] + }, + BID_TIMEOUT: [ + { + 'bidId': '2ec240757', + 'auctionId': '1234-4567-7890' + } + ], + AD_RENDER_FAILED: [ + { + 'bidId': '2ec240757', + 'reason': AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, + 'message': 'message', + 'bid': BID1 + } + ] +}; + +const ANALYTICS_MESSAGE = { + publisherId: 'CC411485-42BC-4F92-8389-42C503EE38D7', + gdpr: [{}], + auctionIds: ['1234-4567-7890'], + bidAdUnits: [ + { + adUnit: 'panorama_d_1', + adUnitId: 'adunitid', + timeStamp: 1519149562216 + }, + { + adUnit: 'box_d_1', + adUnitId: 'adunitid', + timeStamp: 1519149562216 + } + ], + requests: [ + { + adUnit: 'panorama_d_1', + adUnitId: 'adunitid', + bidder: 'adnuntius', + timeStamp: 1519149562216, + gdpr: 0, + auctionId: 0 + }, + { + adUnit: 'box_d_1', + adUnitId: 'adunitid', + bidder: 'adnuntius', + timeStamp: 1519149562216, + gdpr: 0, + auctionId: 0 + }, + { + adUnit: 'box_d_2', + adUnitId: 'adunitid', + bidder: 'adnuntius', + timeStamp: 1519149562216, + gdpr: 0, + auctionId: 0 + } + ], + responses: [ + { + adUnit: 'panorama_d_1', + adUnitId: 'adunitid', + bidder: 'adnuntius', + timeStamp: 1519149562216, + width: 980, + height: 240, + cpm: 1.1, + currency: 'USD', + originalCpm: 12, + originalCurrency: 'AUX', + ttr: 200, + isBid: true, + mediaType: 1, + gdpr: 0, + auctionId: 0, + meta: { + data: 'value1' + } + }, + { + adUnit: 'box_d_1', + adUnitId: 'adunitid', + bidder: 'adnuntius', + timeStamp: 1519149562216, + width: 300, + height: 250, + cpm: 2.2, + currency: 'USD', + originalCpm: 23, + originalCurrency: 'AUX', + ttr: 300, + isBid: true, + mediaType: 1, + gdpr: 0, + auctionId: 0, + meta: { + data: 'value2' + } + } + ], + timeouts: [], + wins: [ + { + adUnit: 'panorama_d_1', + adUnitId: 'adunitid', + bidder: 'adnuntius', + timeStamp: 1519149562216, + width: 980, + height: 240, + cpm: 1.1, + currency: 'USD', + originalCpm: 12, + originalCurrency: 'AUX', + mediaType: 1, + gdpr: 0, + auctionId: 0, + meta: { + data: 'value1' + }, + dealId: 'dealid' + }, + { + adUnit: 'box_d_1', + adUnitId: 'adunitid', + bidder: 'adnuntius', + timeStamp: 1519149562216, + width: 300, + height: 250, + cpm: 2.2, + currency: 'USD', + originalCpm: 23, + originalCurrency: 'AUX', + mediaType: 1, + gdpr: 0, + auctionId: 0, + meta: { + data: 'value2' + } + } + ], + rf: [ + { + adUnit: 'panorama_d_1', + adUnitId: 'adunitid', + bidder: 'adnuntius', + timeStamp: 1519149562216, + auctionId: 0, + rsn: AD_RENDER_FAILED_REASON.CANNOT_FIND_AD, + msg: 'message' + }, + ] +}; + +function performStandardAuction() { + 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, MOCK.BID_RESPONSE[1]); + 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]); + events.emit(AD_RENDER_FAILED, MOCK.AD_RENDER_FAILED[0]); +} + +describe('Adnuntius analytics adapter', function () { + let sandbox; + let clock; + + beforeEach(function () { + sandbox = sinon.sandbox.create(); + + let element = { + getAttribute: function() { + return 'adunitid'; + } + } + sandbox.stub(events, 'getEvents').returns([]); + sandbox.stub(utils, 'timestamp').returns(1519149562416); + sandbox.stub(document, 'getElementById').returns(element); + + clock = sandbox.useFakeTimers(1519767013781); + }); + + afterEach(function () { + sandbox.restore(); + config.resetConfig(); + }); + + describe('when handling events', function () { + adapterManager.registerAnalyticsAdapter({ + code: 'adnuntius', + adapter: adnAnalyticsAdapter + }); + + beforeEach(function () { + adapterManager.enableAnalytics({ + provider: 'adnuntius', + options: { + publisherId: 'CC411485-42BC-4F92-8389-42C503EE38D7' + } + }); + }); + + afterEach(function () { + adnAnalyticsAdapter.disableAnalytics(); + }); + + it('should build a batched message from prebid events', function () { + performStandardAuction(); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + + expect(request.url).to.equal('https://analytics.adnuntius.com/prebid'); + + const message = JSON.parse(request.requestBody); + expect(message).to.deep.equal(ANALYTICS_MESSAGE); + }); + + it('should send batched message without BID_WON AND AD_RENDER_FAILED if necessary and further BID_WON and AD_RENDER_FAILED events individually', function () { + 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, MOCK.BID_RESPONSE[1]); + 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]); + + clock.tick(BID_WON_TIMEOUT + 1000); + + events.emit(BID_WON, MOCK.BID_WON[1]); + events.emit(AD_RENDER_FAILED, MOCK.AD_RENDER_FAILED[0]); + + expect(server.requests.length).to.equal(3); + + let message = JSON.parse(server.requests[0].requestBody); + expect(message.wins.length).to.equal(1); + expect(message.requests).to.deep.equal(ANALYTICS_MESSAGE.requests); + expect(message.wins[0]).to.deep.equal(ANALYTICS_MESSAGE.wins[0]); + + message = JSON.parse(server.requests[1].requestBody); + expect(message.wins.length).to.equal(1); + expect(message.wins[0]).to.deep.equal(ANALYTICS_MESSAGE.wins[1]); + + message = JSON.parse(server.requests[2].requestBody); + expect(message.rf.length).to.equal(1); + expect(message.rf[0]).to.deep.equal(ANALYTICS_MESSAGE.rf[0]); + }); + + it('should properly mark bids as timed out', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, MOCK.BID_REQUESTED); + events.emit(BID_TIMEOUT, MOCK.BID_TIMEOUT); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + + let message = JSON.parse(server.requests[0].requestBody); + expect(message.timeouts.length).to.equal(1); + expect(message.timeouts[0].bidder).to.equal('adnuntius'); + expect(message.timeouts[0].adUnit).to.equal('panorama_d_1'); + }); + + it('should forward GDPR data', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, { + 'bidder': 'adnuntius', + 'auctionId': '1234-4567-7890', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'adnuntius', + 'adUnitCode': 'panorama_d_1', + 'bidId': '2ec240757', + }, + { + 'bidder': 'adnuntius', + 'adUnitCode': 'box_d_1', + 'bidId': '30db240757', + } + ], + 'start': 1519149562216, + 'gdprConsent': { + 'gdprApplies': true, + 'consentString': 'consentstring' + } + }, + ); + + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + + expect(message.gdpr.length).to.equal(1); + expect(message.gdpr[0].gdprApplies).to.equal(true); + expect(message.gdpr[0].gdprConsent).to.equal('consentstring'); + expect(message.requests.length).to.equal(2); + expect(message.requests[0].gdpr).to.equal(0); + expect(message.requests[1].gdpr).to.equal(0); + + expect(message.responses.length).to.equal(1); + expect(message.responses[0].gdpr).to.equal(0); + + expect(message.wins.length).to.equal(1); + expect(message.wins[0].gdpr).to.equal(0); + }); + + it('should forward floor data', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, { + 'bidder': 'adnuntius', + 'auctionId': '1234-4567-7890', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'adnuntius', + 'adUnitCode': 'panorama_d_1', + 'bidId': '2ec240757' + } + ], + 'start': 1519149562216 + }); + + events.emit(BID_RESPONSE, Object.assign({}, + MOCK.BID_RESPONSE[0], + { + 'floorData': { + 'floorValue': 1.1, + 'floorCurrency': 'AUX' + } + })); + events.emit(BID_WON, Object.assign({}, + MOCK.BID_WON[0], + { + 'floorData': { + 'floorValue': 1.1, + 'floorCurrency': 'AUX' + } + })); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + + expect(message.gdpr.length).to.equal(1); + + expect(message.responses.length).to.equal(1); + expect(message.responses[0].floor).to.equal(1.1); + expect(message.responses[0].floorCur).to.equal('AUX'); + + expect(message.wins.length).to.equal(1); + expect(message.wins[0].floor).to.equal(1.1); + expect(message.wins[0].floorCur).to.equal('AUX'); + }); + + it('should forward Adnuntius floor data', function () { + events.emit(AUCTION_INIT, MOCK.AUCTION_INIT); + events.emit(BID_REQUESTED, { + 'bidder': 'adnuntius', + 'auctionId': '1234-4567-7890', + 'bidderRequestId': '1be65d7958826a', + 'bids': [ + { + 'bidder': 'adnuntius', + 'adUnitCode': 'panorama_d_1', + 'bidId': '2ec240757', + 'lwflr': { + 'flr': 1.1 + } + }, + { + 'bidder': 'adnuntius', + 'adUnitCode': 'box_d_1', + 'bidId': '30db240757', + 'lwflr': { + 'flr': 1.1, + 'bflrs': {'adnuntius': 2.2} + } + } + ], + 'start': 1519149562216 + }); + + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[0]); + events.emit(BID_RESPONSE, MOCK.BID_RESPONSE[1]); + events.emit(BID_WON, MOCK.BID_WON[0]); + events.emit(BID_WON, MOCK.BID_WON[1]); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + + expect(message.gdpr.length).to.equal(1); + + expect(message.responses.length).to.equal(2); + expect(message.responses[0].floor).to.equal(1.1); + expect(message.responses[1].floor).to.equal(2.2); + + expect(message.wins.length).to.equal(2); + expect(message.wins[0].floor).to.equal(1.1); + expect(message.wins[1].floor).to.equal(2.2); + }); + + it('should forward runner-up data as given by Adnuntius wrapper', function () { + 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_WON, Object.assign({}, + MOCK.BID_WON[0], + { + 'rUp': 'rUpObject' + })); + events.emit(AUCTION_END, MOCK.AUCTION_END); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + + expect(message.wins.length).to.equal(1); + expect(message.wins[0].rUp).to.equal('rUpObject'); + }); + }); + + describe('when given other endpoint', function () { + adapterManager.registerAnalyticsAdapter({ + code: 'adnuntius', + adapter: adnAnalyticsAdapter + }); + + beforeEach(function () { + adapterManager.enableAnalytics({ + provider: 'adnuntius', + options: { + publisherId: 'CC411485-42BC-4F92-8389-42C503EE38D7', + endPoint: 'https://whitelabeled.com/analytics/10' + } + }); + }); + + afterEach(function () { + adnAnalyticsAdapter.disableAnalytics(); + }); + + it('should call the endpoint', function () { + performStandardAuction(); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + + expect(request.url).to.equal('https://whitelabeled.com/analytics/10'); + }); + }); + + describe('when given extended options', function () { + adapterManager.registerAnalyticsAdapter({ + code: 'adnuntius', + adapter: adnAnalyticsAdapter + }); + + beforeEach(function () { + adapterManager.enableAnalytics({ + provider: 'adnuntius', + options: { + publisherId: 'CC411485-42BC-4F92-8389-42C503EE38D7', + ext: { + testparam: 123 + } + } + }); + }); + + afterEach(function () { + adnAnalyticsAdapter.disableAnalytics(); + }); + + it('should forward the extended options', function () { + performStandardAuction(); + + clock.tick(BID_WON_TIMEOUT + 1000); + + expect(server.requests.length).to.equal(1); + let request = server.requests[0]; + let message = JSON.parse(request.requestBody); + + expect(message.ext).to.not.equal(null); + expect(message.ext.testparam).to.equal(123); + }); + }); +});