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

bidWatch Analytics Adapter : add creative endpoint #8710

Merged
merged 11 commits into from
Aug 3, 2022
81 changes: 70 additions & 11 deletions modules/bidwatchAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ const {
EVENTS: {
AUCTION_END,
BID_WON,
BID_RESPONSE,
BID_REQUESTED,
}
} = CONSTANTS;

let saveEvents = {}
let allEvents = {}
let auctionEnd = {}
let initOptions = {}
let endpoint = 'https://default'
let objectToSearchForBidderCode = ['bidderRequests', 'bidsReceived', 'noBids']
Expand All @@ -22,53 +26,108 @@ function getAdapterNameForAlias(aliasName) {
return adapterManager.aliasRegistry[aliasName] || aliasName;
}

function setOriginalBidder(arg) {
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') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it'd be better to explicitly list what to keep rather than what to remove. As more features are added your payload will probably continue to grow, and I can't imagine new, unrecognized fields being too useful on your end. It would also make it less likely that we'd accidentally rename or remove some field that is important to you, because a simple code search will show us what you are depending on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion @dgirardi
I am no javascript specialist so I often go to the easiest way I know without searching for subtilities. As I have another PR to send very shortly I'll make the suggested changes with this next one.

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(); }
arg[key] = cleanArgObject(arg[key], removead);
});
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]] = 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']); }
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] = [] }
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] = [] }
auctionEnd[eventType].push(argsCleaned);
}
}
}

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];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these types of loops would look a bit cleaner as

for (let tmpAuction of saveEvents['auctionEnd']) { ... }`

or

saveEvents['auctionEnd'].forEach((tmpAuction) => {...})

Although I think auction, bid would be better names than tmpAuction, tmpBid etc.

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(allEvents), {method: 'POST', withCredentials: true});
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}), {
track({
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;
}
}});

Expand Down
18 changes: 17 additions & 1 deletion test/spec/modules/bidwatchAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ describe('BidWatch Analytics', function () {
'gdprConsent': {
'consentString': 'CONSENT',
'gdprApplies': true,
'apiVersion': 2
'apiVersion': 2,
'vendorData': 'a lot of borring stuff',
},
'start': 1647424261189
},
Expand Down Expand Up @@ -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);
});
});
Expand Down