From 93dec77f889d784413d185d55b74fb7c4105a0a5 Mon Sep 17 00:00:00 2001 From: matthieularere-msq Date: Wed, 20 Apr 2022 15:39:29 +0200 Subject: [PATCH 01/10] New Analytics Adapter bidwatch --- modules/bidwatchAnalyticsAdapter.js | 90 +++++++++++++++++++++++++++++ modules/bidwatchAnalyticsAdapter.md | 22 +++++++ 2 files changed, 112 insertions(+) create mode 100644 modules/bidwatchAnalyticsAdapter.js create mode 100644 modules/bidwatchAnalyticsAdapter.md diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js new file mode 100644 index 00000000000..1f931dc0952 --- /dev/null +++ b/modules/bidwatchAnalyticsAdapter.js @@ -0,0 +1,90 @@ +import adapter from '../src/AnalyticsAdapter.js'; +import adapterManager from '../src/adapterManager.js'; +import CONSTANTS from '../src/constants.json'; +import { ajax } from '../src/ajax.js'; + +const analyticsType = 'endpoint'; +const url = 'URL_TO_SERVER_ENDPOINT'; + +const { + EVENTS: { + AUCTION_END, + BID_WON, + } +} = CONSTANTS; + +let allEvents = {} +let initOptions = {} +let endpoint = 'https://default' +let objectToSearchForBidderCode = ['bidderRequests', 'bidsReceived', 'noBids'] + +function getAdapterNameForAlias(aliasName) { + return adapterManager.aliasRegistry[aliasName] || aliasName; +} + +function setOriginalBidder(arg) { + Object.keys(arg).forEach(key => { + arg[key]['originalBidder'] = getAdapterNameForAlias(arg[key]['bidderCode']); + if (typeof arg[key]['creativeId'] == 'number') { arg[key]['creativeId'] = arg[key]['creativeId'].toString(); } + }); + return arg +} + +function checkBidderCode(args) { + if (typeof args == 'object') { + for (let i = 0; i < objectToSearchForBidderCode.length; i++) { + if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = setOriginalBidder(args[objectToSearchForBidderCode[i]]) } + } + } + if (typeof args['bidderCode'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidderCode']); } else if (typeof args['bidder'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidder']); } + if (typeof args['creativeId'] == 'number') { args['creativeId'] = args['creativeId'].toString(); } + return args +} + +function addEvent(eventType, args) { + if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } + if (eventType && args) { args = checkBidderCode(args); } + allEvents[eventType].push(args); +} + +function handleBidWon(args) { + if (typeof allEvents.bidRequested == 'object' && allEvents.bidRequested.length > 0 && allEvents.bidRequested[0].gdprConsent) { args.gdpr = allEvents.bidRequested[0].gdprConsent; } + ajax(endpoint + '.bidwatch.io/analytics/bid_won', null, JSON.stringify(args), {method: 'POST', withCredentials: true}); +} + +function handleAuctionEnd() { + ajax(endpoint + '/analytics/auctions', null, JSON.stringify(allEvents), {method: 'POST', withCredentials: true}); +} + +let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { + track({ + eventType, + args + }) { + addEvent(eventType, args); + switch (eventType) { + case AUCTION_END: + handleAuctionEnd(); + break; + case BID_WON: + handleBidWon(args); + break; + } + }}); + +// save the base class function +bidwatchAnalytics.originEnableAnalytics = bidwatchAnalytics.enableAnalytics; + +// override enableAnalytics so we can get access to the config passed in from the page +bidwatchAnalytics.enableAnalytics = function (config) { + bidwatchAnalytics.originEnableAnalytics(config); // call the base class function + initOptions = config.options; + if (initOptions.domain) { endpoint = "https://"+initOptions.domain; } +}; + +adapterManager.registerAnalyticsAdapter({ + adapter: bidwatchAnalytics, + code: 'bidwatch' +}); + +export default bidwatchAnalytics; diff --git a/modules/bidwatchAnalyticsAdapter.md b/modules/bidwatchAnalyticsAdapter.md new file mode 100644 index 00000000000..ba088394827 --- /dev/null +++ b/modules/bidwatchAnalyticsAdapter.md @@ -0,0 +1,22 @@ +# Overview +Module Name: bidwatch Analytics Adapter + +Module Type: Analytics Adapter + +Maintainer: contact@bidwatch.io + +# Description + +Analytics adapter for bidwatch.io. + +# Test Parameters + +``` +{ + provider: 'bidwatch', + options : { + domain: 'test.endpoint', + } +} + +``` From aa6107bdeead6b913fdd70de536b6d9344bafc30 Mon Sep 17 00:00:00 2001 From: matthieularere-msq Date: Wed, 20 Apr 2022 15:57:22 +0200 Subject: [PATCH 02/10] test for bidwatch Analytics Adapter --- .../modules/bidwatchAnalyticsAdapter_spec.js | 288 ++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 test/spec/modules/bidwatchAnalyticsAdapter_spec.js diff --git a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js new file mode 100644 index 00000000000..1a322d131a9 --- /dev/null +++ b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js @@ -0,0 +1,288 @@ +import bidwatchAnalytics from 'modules/bidwatchAnalyticsAdapter.js'; +import { expect } from 'chai'; +import { server } from 'test/mocks/xhr.js'; +let adapterManager = require('src/adapterManager').default; +let events = require('src/events'); +let constants = require('src/constants.json'); + +describe('BidWatch Analytics', function () { + let timestamp = new Date() - 256; + let auctionId = '5018eb39-f900-4370-b71e-3bb5b48d324f'; + let timeout = 1500; + + let bidTimeout = [ + { + 'bidId': '5fe418f2d70364', + 'bidder': 'appnexusAst', + 'adUnitCode': 'tag_200124_banner', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b' + } + ]; + + const auctionEnd = { + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'timestamp': 1647424261187, + 'auctionEnd': 1647424261714, + 'auctionStatus': 'completed', + 'adUnits': [ + { + 'code': 'tag_200124_banner', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 600 + ] + ] + } + }, + 'bids': [ + { + 'bidder': 'appnexus', + 'params': { + 'placementId': 123456 + } + }, + { + 'bidder': 'appnexusAst', + 'params': { + 'placementId': 234567 + } + } + ], + 'sizes': [ + [ + 300, + 600 + ] + ], + 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40' + } + ], + 'adUnitCodes': [ + 'tag_200124_banner' + ], + 'bidderRequests': [ + { + 'bidderCode': 'appnexus', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'bidderRequestId': '11dc6ff6378de7', + 'bids': [ + { + 'bidder': 'appnexus', + 'params': { + 'placementId': 123456 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': 'tag_200124_banner', + 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', + 'sizes': [ + [ + 300, + 600 + ] + ], + 'bidId': '34a63e5d5378a3', + 'bidderRequestId': '11dc6ff6378de7', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'auctionStart': 1647424261187, + 'timeout': 1000, + 'gdprConsent': { + 'consentString': 'CONSENT', + 'gdprApplies': true, + 'apiVersion': 2 + }, + 'start': 1647424261189 + }, + ], + 'noBids': [ + { + 'bidder': 'appnexusAst', + 'params': { + 'placementId': 10471298 + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 600 + ] + ] + } + }, + 'adUnitCode': 'tag_200124_banner', + 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', + 'sizes': [ + [ + 300, + 600 + ] + ], + 'bidId': '5fe418f2d70364', + 'bidderRequestId': '4229a45ab8ea87', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0 + } + ], + 'bidsReceived': [ + { + 'bidderCode': 'appnexus', + 'width': 300, + 'height': 600, + 'statusMessage': 'Bid available', + 'adId': '7a4ced80f33d33', + 'requestId': '34a63e5d5378a3', + 'transactionId': 'de664ccb-e18b-4436-aeb0-362382eb1b40', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 27.4276, + 'creativeId': '158534630', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 2000, + 'ad': 'some html', + 'meta': { + 'advertiserDomains': [ + 'example.com' + ] + }, + 'originalCpm': 25.02521, + 'originalCurrency': 'EUR', + 'responseTimestamp': 1647424261559, + 'requestTimestamp': 1647424261189, + 'bidder': 'appnexus', + 'adUnitCode': 'tag_200124_banner', + 'timeToRespond': 370, + 'pbLg': '5.00', + 'pbMg': '20.00', + 'pbHg': '20.00', + 'pbAg': '20.00', + 'pbDg': '20.00', + 'pbCg': '20.000000', + 'size': '300x600', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '7a4ced80f33d33', + 'hb_pb': '20.000000', + 'hb_size': '300x600', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': 'example.com' + } + } + ], + 'winningBids': [ + + ], + 'timeout': 1000 + }; + + let bidWon = { + 'bidderCode': 'appnexus', + 'width': 970, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '65d16ef039a97a', + 'requestId': '2bd3e8ff8a113f', + 'transactionId': '8b2a8629-d1ea-4bb1-aff0-e335b96dd002', + 'auctionId': '1e8b993d-8f0a-4232-83eb-3639ddf3a44b', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 27.4276, + 'creativeId': '158533702', + 'currency': 'USD', + 'netRevenue': true, + 'ttl': 2000, + 'ad': 'some html', + 'meta': { + 'advertiserDomains': [ + 'example.com' + ] + }, + 'originalCpm': 25.02521, + 'originalCurrency': 'EUR', + 'responseTimestamp': 1647424261558, + 'requestTimestamp': 1647424261189, + 'bidder': 'appnexus', + 'adUnitCode': 'tag_200123_banner', + 'timeToRespond': 369, + 'originalBidder': 'appnexus', + 'pbLg': '5.00', + 'pbMg': '20.00', + 'pbHg': '20.00', + 'pbAg': '20.00', + 'pbDg': '20.00', + 'pbCg': '20.000000', + 'size': '970x250', + 'adserverTargeting': { + 'hb_bidder': 'appnexus', + 'hb_adid': '65d16ef039a97a', + 'hb_pb': '20.000000', + 'hb_size': '970x250', + 'hb_source': 'client', + 'hb_format': 'banner', + 'hb_adomain': 'example.com' + }, + 'status': 'rendered', + 'params': [ + { + 'placementId': 123456 + } + ] + }; + + after(function () { + bidwatchAnalytics.disableAnalytics(); + }); + + describe('main test flow', function () { + beforeEach(function () { + sinon.stub(events, 'getEvents').returns([]); + }); + + afterEach(function () { + events.getEvents.restore(); + }); + + it('should catch events of interest', function () { + sinon.spy(bidwatchAnalytics, 'track'); + + adapterManager.registerAnalyticsAdapter({ + code: 'bidwatch', + adapter: bidwatchAnalytics + }); + + adapterManager.enableAnalytics({ + provider: 'bidwatch', + options: { + domain: 'test' + } + }); + events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); + events.emit(constants.EVENTS.AUCTION_END, auctionEnd); + events.emit(constants.EVENTS.BID_WON, bidWon); + sinon.assert.callCount(bidwatchAnalytics.track, 3); + }); + }); +}); From 5e6e3d13da45da02b695b652d18c960d99a7fa2e Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Wed, 20 Apr 2022 16:16:53 +0200 Subject: [PATCH 03/10] change maintainer address --- modules/bidwatchAnalyticsAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/bidwatchAnalyticsAdapter.md b/modules/bidwatchAnalyticsAdapter.md index ba088394827..032ff0f4d2e 100644 --- a/modules/bidwatchAnalyticsAdapter.md +++ b/modules/bidwatchAnalyticsAdapter.md @@ -3,7 +3,7 @@ Module Name: bidwatch Analytics Adapter Module Type: Analytics Adapter -Maintainer: contact@bidwatch.io +Maintainer: tech@bidwatch.io # Description From 706d98b3b2eb49381dcd38355ddccd0cfd0a8a8e Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Wed, 20 Apr 2022 16:34:33 +0200 Subject: [PATCH 04/10] Update bidwatchAnalyticsAdapter.js --- modules/bidwatchAnalyticsAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index 1f931dc0952..907c16be899 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -79,7 +79,7 @@ bidwatchAnalytics.originEnableAnalytics = bidwatchAnalytics.enableAnalytics; bidwatchAnalytics.enableAnalytics = function (config) { bidwatchAnalytics.originEnableAnalytics(config); // call the base class function initOptions = config.options; - if (initOptions.domain) { endpoint = "https://"+initOptions.domain; } + if (initOptions.domain) { endpoint = 'https://' + initOptions.domain; } }; adapterManager.registerAnalyticsAdapter({ From 54348a156192135dc01e193edfdb067f4ca28294 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Wed, 20 Apr 2022 17:42:44 +0200 Subject: [PATCH 05/10] Update bidwatchAnalyticsAdapter.js --- modules/bidwatchAnalyticsAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index 907c16be899..26a8c370af3 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -53,7 +53,7 @@ function handleBidWon(args) { } function handleAuctionEnd() { - ajax(endpoint + '/analytics/auctions', null, JSON.stringify(allEvents), {method: 'POST', withCredentials: true}); + ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(allEvents), {method: 'POST', withCredentials: true}); } let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { From 6cfbb139e7fb3155840748dd732b68125e9bbb13 Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Thu, 21 Apr 2022 14:00:48 +0200 Subject: [PATCH 06/10] Update bidwatchAnalyticsAdapter.md --- modules/bidwatchAnalyticsAdapter.md | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/bidwatchAnalyticsAdapter.md b/modules/bidwatchAnalyticsAdapter.md index 032ff0f4d2e..911d0b48af2 100644 --- a/modules/bidwatchAnalyticsAdapter.md +++ b/modules/bidwatchAnalyticsAdapter.md @@ -18,5 +18,4 @@ Analytics adapter for bidwatch.io. domain: 'test.endpoint', } } - ``` From 4e4eeb6d11692a55456ebc2cc83c7892342fee4f Mon Sep 17 00:00:00 2001 From: matthieularere-msq <63732822+matthieularere-msq@users.noreply.github.com> Date: Fri, 22 Apr 2022 11:22:38 +0200 Subject: [PATCH 07/10] Update bidwatchAnalyticsAdapter.md --- modules/bidwatchAnalyticsAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/bidwatchAnalyticsAdapter.md b/modules/bidwatchAnalyticsAdapter.md index 911d0b48af2..bfa453640b8 100644 --- a/modules/bidwatchAnalyticsAdapter.md +++ b/modules/bidwatchAnalyticsAdapter.md @@ -15,7 +15,7 @@ Analytics adapter for bidwatch.io. { provider: 'bidwatch', options : { - domain: 'test.endpoint', + domain: 'test.endpoint' } } ``` From 33892711ded69aee192e93ebcbfe6fdc572d9b56 Mon Sep 17 00:00:00 2001 From: matthieularere-msq Date: Tue, 19 Jul 2022 17:56:19 +0200 Subject: [PATCH 08/10] add features to bidwatchAnalyticsAdapter --- modules/bidwatchAnalyticsAdapter.js | 31 ++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index 26a8c370af3..c2c75ee090b 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -14,6 +14,7 @@ const { } = CONSTANTS; let allEvents = {} +let auctionEnd = {} let initOptions = {} let endpoint = 'https://default' let objectToSearchForBidderCode = ['bidderRequests', 'bidsReceived', 'noBids'] @@ -22,29 +23,42 @@ function getAdapterNameForAlias(aliasName) { return adapterManager.aliasRegistry[aliasName] || aliasName; } -function setOriginalBidder(arg) { +function setOriginalBidder(arg, removead) { Object.keys(arg).forEach(key => { arg[key]['originalBidder'] = getAdapterNameForAlias(arg[key]['bidderCode']); if (typeof arg[key]['creativeId'] == 'number') { arg[key]['creativeId'] = arg[key]['creativeId'].toString(); } + if (removead && typeof arg[key]['ad'] != 'undefined') { arg[key]['ad'] = 'emptied'; } + if (typeof arg[key]['gdprConsent'] != 'undefined' && typeof arg[key]['gdprConsent']['vendorData'] != 'undefined') { + arg[key]['gdprConsent']['vendorData'] = 'emptied'; + } }); return arg } -function checkBidderCode(args) { +function checkBidderCode(args, removead) { if (typeof args == 'object') { for (let i = 0; i < objectToSearchForBidderCode.length; i++) { - if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = setOriginalBidder(args[objectToSearchForBidderCode[i]]) } + if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = setOriginalBidder(args[objectToSearchForBidderCode[i]], removead) } } } if (typeof args['bidderCode'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidderCode']); } else if (typeof args['bidder'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidder']); } if (typeof args['creativeId'] == 'number') { args['creativeId'] = args['creativeId'].toString(); } + return args } function addEvent(eventType, args) { - if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } - if (eventType && args) { args = checkBidderCode(args); } - allEvents[eventType].push(args); + let argsCleaned; + if (eventType && args) { + if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } + argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), false); + allEvents[eventType].push(argsCleaned); + argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), true); + if (['auctionend', 'bidtimeout'].includes(eventType.toLowerCase())) { + if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } + auctionEnd[eventType].push(argsCleaned); + } + } } function handleBidWon(args) { @@ -53,7 +67,10 @@ function handleBidWon(args) { } function handleAuctionEnd() { - ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(allEvents), {method: 'POST', withCredentials: true}); + ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(auctionEnd), {method: 'POST', withCredentials: true}); + if (typeof allEvents['bidResponse'] != 'undefined') { + for (let i = 0; i < allEvents['bidResponse'].length; i++) { ajax(endpoint + '.bidwatch.io/analytics/creatives', null, JSON.stringify(allEvents['bidResponse'][i]), {method: 'POST', withCredentials: true}); } + } } let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { From 8df3521bb26399fa36dd1ada521db6afb0ebc5be Mon Sep 17 00:00:00 2001 From: matthieularere-msq Date: Wed, 20 Jul 2022 16:13:08 +0200 Subject: [PATCH 09/10] update tests --- test/spec/modules/bidwatchAnalyticsAdapter_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js index 1a322d131a9..a3688a2eddb 100644 --- a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js +++ b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js @@ -106,7 +106,8 @@ describe('BidWatch Analytics', function () { 'gdprConsent': { 'consentString': 'CONSENT', 'gdprApplies': true, - 'apiVersion': 2 + 'apiVersion': 2, + 'vendorData': 'lot of datas' }, 'start': 1647424261189 }, @@ -264,7 +265,6 @@ describe('BidWatch Analytics', function () { afterEach(function () { events.getEvents.restore(); }); - it('should catch events of interest', function () { sinon.spy(bidwatchAnalytics, 'track'); From 3eb1a6d49328aa48d3844e4061444794458410ed Mon Sep 17 00:00:00 2001 From: matthieularere-msq Date: Thu, 21 Jul 2022 16:33:14 +0200 Subject: [PATCH 10/10] add test and made improvements --- modules/bidwatchAnalyticsAdapter.js | 62 ++++++++++++++++--- .../modules/bidwatchAnalyticsAdapter_spec.js | 18 +++++- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/modules/bidwatchAnalyticsAdapter.js b/modules/bidwatchAnalyticsAdapter.js index c2c75ee090b..138f0533238 100644 --- a/modules/bidwatchAnalyticsAdapter.js +++ b/modules/bidwatchAnalyticsAdapter.js @@ -10,9 +10,12 @@ const { EVENTS: { AUCTION_END, BID_WON, + BID_RESPONSE, + BID_REQUESTED, } } = CONSTANTS; +let saveEvents = {} let allEvents = {} let auctionEnd = {} let initOptions = {} @@ -23,14 +26,23 @@ function getAdapterNameForAlias(aliasName) { return adapterManager.aliasRegistry[aliasName] || aliasName; } -function setOriginalBidder(arg, removead) { +function cleanArgObject(arg, removead) { + if (typeof arg['bidderCode'] == 'string') { arg['originalBidder'] = getAdapterNameForAlias(arg['bidderCode']); } + if (typeof arg['creativeId'] == 'number') { + arg['creativeId'] = arg['creativeId'].toString(); + } + if (removead && typeof arg['ad'] != 'undefined') { + arg['ad'] = 'emptied'; + } + if (typeof arg['gdprConsent'] != 'undefined' && typeof arg['gdprConsent']['vendorData'] != 'undefined') { + arg['gdprConsent']['vendorData'] = 'emptied'; + } + return arg; +} + +function cleanArgs(arg, removead) { Object.keys(arg).forEach(key => { - arg[key]['originalBidder'] = getAdapterNameForAlias(arg[key]['bidderCode']); - if (typeof arg[key]['creativeId'] == 'number') { arg[key]['creativeId'] = arg[key]['creativeId'].toString(); } - if (removead && typeof arg[key]['ad'] != 'undefined') { arg[key]['ad'] = 'emptied'; } - if (typeof arg[key]['gdprConsent'] != 'undefined' && typeof arg[key]['gdprConsent']['vendorData'] != 'undefined') { - arg[key]['gdprConsent']['vendorData'] = 'emptied'; - } + arg[key] = cleanArgObject(arg[key], removead); }); return arg } @@ -38,7 +50,7 @@ function setOriginalBidder(arg, removead) { function checkBidderCode(args, removead) { if (typeof args == 'object') { for (let i = 0; i < objectToSearchForBidderCode.length; i++) { - if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = setOriginalBidder(args[objectToSearchForBidderCode[i]], removead) } + if (typeof args[objectToSearchForBidderCode[i]] == 'object') { args[objectToSearchForBidderCode[i]] = cleanArgs(args[objectToSearchForBidderCode[i]], removead) } } } if (typeof args['bidderCode'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidderCode']); } else if (typeof args['bidder'] == 'string') { args['originalBidder'] = getAdapterNameForAlias(args['bidder']); } @@ -51,8 +63,10 @@ function addEvent(eventType, args) { let argsCleaned; if (eventType && args) { if (allEvents[eventType] == undefined) { allEvents[eventType] = [] } + if (saveEvents[eventType] == undefined) { saveEvents[eventType] = [] } argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), false); allEvents[eventType].push(argsCleaned); + saveEvents[eventType].push(argsCleaned); argsCleaned = checkBidderCode(JSON.parse(JSON.stringify(args)), true); if (['auctionend', 'bidtimeout'].includes(eventType.toLowerCase())) { if (auctionEnd[eventType] == undefined) { auctionEnd[eventType] = [] } @@ -62,15 +76,37 @@ function addEvent(eventType, args) { } function handleBidWon(args) { - if (typeof allEvents.bidRequested == 'object' && allEvents.bidRequested.length > 0 && allEvents.bidRequested[0].gdprConsent) { args.gdpr = allEvents.bidRequested[0].gdprConsent; } + args = cleanArgObject(JSON.parse(JSON.stringify(args)), true); + let increment = args['cpm']; + if (typeof saveEvents['auctionEnd'] == 'object') { + for (let i = 0; i < saveEvents['auctionEnd'].length; i++) { + let tmpAuction = saveEvents['auctionEnd'][i]; + if (tmpAuction['auctionId'] == args['auctionId'] && typeof tmpAuction['bidsReceived'] == 'object') { + for (let j = 0; j < tmpAuction['bidsReceived'].length; j++) { + let tmpBid = tmpAuction['bidsReceived'][j]; + if (tmpBid['transactionId'] == args['transactionId'] && tmpBid['adId'] != args['adId']) { + if (args['cpm'] < tmpBid['cpm']) { + increment = 0; + } else if (increment > args['cpm'] - tmpBid['cpm']) { + increment = args['cpm'] - tmpBid['cpm']; + } + } + } + } + } + } + args['cpmIncrement'] = increment; + if (typeof saveEvents.bidRequested == 'object' && saveEvents.bidRequested.length > 0 && saveEvents.bidRequested[0].gdprConsent) { args.gdpr = saveEvents.bidRequested[0].gdprConsent; } ajax(endpoint + '.bidwatch.io/analytics/bid_won', null, JSON.stringify(args), {method: 'POST', withCredentials: true}); } function handleAuctionEnd() { ajax(endpoint + '.bidwatch.io/analytics/auctions', null, JSON.stringify(auctionEnd), {method: 'POST', withCredentials: true}); + auctionEnd = {} if (typeof allEvents['bidResponse'] != 'undefined') { for (let i = 0; i < allEvents['bidResponse'].length; i++) { ajax(endpoint + '.bidwatch.io/analytics/creatives', null, JSON.stringify(allEvents['bidResponse'][i]), {method: 'POST', withCredentials: true}); } } + allEvents = {} } let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { @@ -78,14 +114,20 @@ let bidwatchAnalytics = Object.assign(adapter({url, analyticsType}), { eventType, args }) { - addEvent(eventType, args); switch (eventType) { case AUCTION_END: + addEvent(eventType, args); handleAuctionEnd(); break; case BID_WON: handleBidWon(args); break; + case BID_RESPONSE: + addEvent(eventType, args); + break; + case BID_REQUESTED: + addEvent(eventType, args); + break; } }}); diff --git a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js index a3688a2eddb..f827f068bb3 100644 --- a/test/spec/modules/bidwatchAnalyticsAdapter_spec.js +++ b/test/spec/modules/bidwatchAnalyticsAdapter_spec.js @@ -107,7 +107,7 @@ describe('BidWatch Analytics', function () { 'consentString': 'CONSENT', 'gdprApplies': true, 'apiVersion': 2, - 'vendorData': 'lot of datas' + 'vendorData': 'a lot of borring stuff', }, 'start': 1647424261189 }, @@ -265,6 +265,7 @@ describe('BidWatch Analytics', function () { afterEach(function () { events.getEvents.restore(); }); + it('should catch events of interest', function () { sinon.spy(bidwatchAnalytics, 'track'); @@ -281,7 +282,22 @@ describe('BidWatch Analytics', function () { }); events.emit(constants.EVENTS.BID_TIMEOUT, bidTimeout); events.emit(constants.EVENTS.AUCTION_END, auctionEnd); + expect(server.requests.length).to.equal(1); + let message = JSON.parse(server.requests[0].requestBody); + expect(message).to.have.property('auctionEnd').exist; + expect(message.auctionEnd).to.have.lengthOf(1); + expect(message.auctionEnd[0]).to.have.property('bidsReceived').and.to.have.lengthOf(1); + expect(message.auctionEnd[0].bidsReceived[0]).to.have.property('ad'); + expect(message.auctionEnd[0].bidsReceived[0].ad).to.equal('emptied'); + expect(message.auctionEnd[0]).to.have.property('bidderRequests').and.to.have.lengthOf(1); + expect(message.auctionEnd[0].bidderRequests[0]).to.have.property('gdprConsent'); + expect(message.auctionEnd[0].bidderRequests[0].gdprConsent).to.have.property('vendorData'); + expect(message.auctionEnd[0].bidderRequests[0].gdprConsent.vendorData).to.equal('emptied'); events.emit(constants.EVENTS.BID_WON, bidWon); + expect(server.requests.length).to.equal(2); + message = JSON.parse(server.requests[1].requestBody); + expect(message).to.have.property('ad').and.to.equal('emptied'); + expect(message).to.have.property('cpmIncrement').and.to.equal(27.4276); sinon.assert.callCount(bidwatchAnalytics.track, 3); }); });