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

Pubxai Analytics Adapter: bug fixes and code revamp #6474

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 90 additions & 73 deletions modules/pubxaiAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ import * as utils from '../src/utils.js';

const emptyUrl = '';
const analyticsType = 'endpoint';
const pubxaiAnalyticsVersion = 'v1.0.0';
const pubxaiAnalyticsVersion = 'v1.1.0';
const defaultHost = 'api.pbxai.com';
const auctionPath = '/analytics/auction';
const winningBidPath = '/analytics/bidwon';

let initOptions;
let auctionTimestamp;
let auctionCache = [];
let events = {
bids: []
bids: [],
floorDetail: {},
pageDetail: {},
deviceDetail: {}
};

var pubxaiAnalyticsAdapter = Object.assign(adapter(
Expand All @@ -27,14 +31,14 @@ var pubxaiAnalyticsAdapter = Object.assign(adapter(
if (eventType === CONSTANTS.EVENTS.BID_TIMEOUT) {
args.forEach(item => { mapBidResponse(item, 'timeout'); });
} else if (eventType === CONSTANTS.EVENTS.AUCTION_INIT) {
events.auctionInit = args;
events.floorDetail = {};
if (typeof args.bidderRequests[0].bids[0] !== 'undefined' && typeof args.bidderRequests[0].bids[0].floorData !== 'undefined') {
Object.assign(events.floorDetail, args.bidderRequests[0].bids[0].floorData);
events.bids = [];
const floorData = utils.deepAccess(args, 'bidderRequests.0.bids.0.floorData');
if (typeof args.bidderRequests[0].bids[0] !== 'undefined' && typeof floorData !== 'undefined') {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this seems like overkill,

Can you simply just do:

if (typeof floorData !== 'undefined') {
   Object.assign(events.floorDetail, floorData);
}

Object.assign(events.floorDetail, floorData);
}
auctionTimestamp = args.timestamp;
} else if (eventType === CONSTANTS.EVENTS.BID_REQUESTED) {
events.bids = [];
mapBidRequests(args).forEach(item => { events.bids.push(item) });
} else if (eventType === CONSTANTS.EVENTS.BID_RESPONSE) {
mapBidResponse(args, 'response');
} else if (eventType === CONSTANTS.EVENTS.BID_WON) {
Expand All @@ -43,81 +47,48 @@ var pubxaiAnalyticsAdapter = Object.assign(adapter(
}, 'bidwon');
}
}

if (eventType === CONSTANTS.EVENTS.AUCTION_END) {
send(events, 'auctionEnd');
}
}
});

function mapBidRequests(params) {
let arr = [];
if (typeof params.bids !== 'undefined' && params.bids.length) {
params.bids.forEach(function (bid) {
arr.push({
bidderCode: bid.bidder,
bidId: bid.bidId,
adUnitCode: bid.adUnitCode,
requestId: bid.bidderRequestId,
auctionId: bid.auctionId,
placementId: bid.params.placementId,
floorData: bid.floorData,
transactionId: bid.transactionId,
sizes: utils.parseSizesInput(bid.mediaTypes.banner.sizes).toString(),
renderStatus: 1,
requestTimestamp: params.auctionStart
});
});
}
return arr;
}

function mapBidResponse(bidResponse, status) {
if (status !== 'bidwon') {
let bid = events.bids.filter(o => o.bidId === bidResponse.bidId || o.bidId === bidResponse.requestId)[0];
Object.assign(bid, {
bidderCode: bidResponse.bidder,
bidId: status === 'timeout' ? bidResponse.bidId : bidResponse.requestId,
if (typeof bidResponse !== 'undefined' && bidResponse) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure if there is a need to check if type is undefined AND also if the value is truthy.

I think just one of the other is enough.

let bid = {
adUnitCode: bidResponse.adUnitCode,
auctionId: bidResponse.auctionId,
creativeId: bidResponse.creativeId,
transactionId: bidResponse.transactionId,
currency: bidResponse.currency,
cpm: bidResponse.cpm,
netRevenue: bidResponse.netRevenue,
mediaType: bidResponse.mediaType,
statusMessage: bidResponse.statusMessage,
floorData: bidResponse.floorData,
status: bidResponse.status,
renderStatus: status === 'timeout' ? 3 : 2,
timeToRespond: bidResponse.timeToRespond,
requestTimestamp: bidResponse.requestTimestamp,
responseTimestamp: bidResponse.responseTimestamp,
platform: navigator.platform,
deviceType: getDeviceType()
});
} else {
return {
bidderCode: bidResponse.bidder,
bidId: bidResponse.requestId,
adUnitCode: bidResponse.adUnitCode,
auctionId: bidResponse.auctionId,
cpm: bidResponse.cpm,
creativeId: bidResponse.creativeId,
transactionId: bidResponse.transactionId,
currency: bidResponse.currency,
cpm: bidResponse.cpm,
netRevenue: bidResponse.netRevenue,
floorData: bidResponse.floorData,
renderedSize: bidResponse.size,
mediaType: bidResponse.mediaType,
statusMessage: bidResponse.statusMessage,
status: bidResponse.status,
renderStatus: 4,
timeToRespond: bidResponse.timeToRespond,
netRevenue: bidResponse.netRevenue,
requestTimestamp: bidResponse.requestTimestamp,
responseTimestamp: bidResponse.responseTimestamp,
platform: navigator.platform,
deviceType: getDeviceType()
status: bidResponse.status,
statusMessage: bidResponse.statusMessage,
timeToRespond: bidResponse.timeToRespond,
transactionId: bidResponse.transactionId
};
if (status !== 'bidwon') {
Object.assign(bid, {
bidId: status === 'timeout' ? bidResponse.bidId : bidResponse.requestId,
renderStatus: status === 'timeout' ? 3 : 2,
sizes: utils.parseSizesInput(bidResponse.size).toString(),
});
events.bids.push(bid);
} else {
Object.assign(bid, {
bidId: bidResponse.requestId,
floorProvider: events.floorDetail ? events.floorDetail.floorProvider : null,
isWinningBid: true,
placementId: bidResponse.params ? utils.deepAccess(bidResponse, 'params.0.placementId') : null,
renderedSize: bidResponse.size,
renderStatus: 4
});
return bid;
}
}
}
Expand All @@ -132,6 +103,36 @@ export function getDeviceType() {
return 'desktop';
}

export function getBrowser() {
var isChrome =
/Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
var isCriOS = navigator.userAgent.match('CriOS');
var isSafari =
/Safari/.test(navigator.userAgent) &&
/Apple Computer/.test(navigator.vendor);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isIE =
/Trident/.test(navigator.userAgent) || /MSIE/.test(navigator.userAgent);
var isEdge = /Edge/.test(navigator.userAgent);
if (isIE) return 'Internet Explorer';
if (isEdge) return 'Microsoft Edge';
if (isCriOS) return 'Chrome';
if (isSafari) return 'Safari';
if (isFirefox) return 'Firefox';
if (isChrome) return 'Chrome';
return 'Others';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of assigning variables for every single combo, I think it would be cleaner to just do a chain of if / else ifs (and maybe sort them in a order of % of users who use which browser even to make it even more optimized!

if (/Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)) return 'Chrome';
else if (navigator.userAgent.match('CriOS')) return 'Chrome';
else if (/Firefox/.test(navigator.userAgent)) return 'Firefox';
else if (/Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor)) return 'Safari';
else if (/Edge/.test(navigator.userAgent)) return 'Microsoft Edge';
else if (/Trident/.test(navigator.userAgent) || /MSIE/.test(navigator.userAgent)) return 'Internet Explorer';
else return 'Others';

Maybe something like that?

My best guess is Chrome > FF > Safari > Edge > IE

But feel free to order them however you would like.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This function was taken directly from another adapter and I kept it as it is. Thanks for your inputs, I have updated the code as you suggested.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems wrong, order matters, you have to test for edge before safari for example and your edge test is wrong

https://docs.microsoft.com/en-us/microsoft-edge/web-platform/user-agent-string

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@patmmccann I have updated the order so that edge test comes before safari and updated edge token to Edg instead of Edge. Thanks !

Copy link
Collaborator

Choose a reason for hiding this comment

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

@patmmccann

Sorry, was not aware that order mattered. Which is why I ordered by what I thought was most used browsers!

Makes sense if both have similar UA's

}

export function getOS() {
if (navigator.userAgent.indexOf('Android') != -1) return 'Android';
if (navigator.userAgent.indexOf('like Mac') != -1) return 'iOS';
if (navigator.userAgent.indexOf('Win') != -1) return 'Windows';
if (navigator.userAgent.indexOf('Mac') != -1) return 'Macintosh';
if (navigator.userAgent.indexOf('Linux') != -1) return 'Linux';
if (navigator.appVersion.indexOf('X11') != -1) return 'Unix';
return 'Others';
}

// add sampling rate
pubxaiAnalyticsAdapter.shouldFireEventRequest = function (samplingRate = 1) {
return (Math.floor((Math.random() * samplingRate + 1)) === parseInt(samplingRate));
Expand All @@ -140,12 +141,24 @@ pubxaiAnalyticsAdapter.shouldFireEventRequest = function (samplingRate = 1) {
function send(data, status) {
if (pubxaiAnalyticsAdapter.shouldFireEventRequest(initOptions.samplingRate)) {
let location = utils.getWindowLocation();
if (typeof data !== 'undefined') {
data.pageDetail = {};
Object.assign(data.pageDetail, { host: location.host, path: location.pathname, search: location.search });
}
data.initOptions = initOptions;

if (typeof data !== 'undefined' && typeof data.auctionInit !== 'undefined') {
Object.assign(data.pageDetail, {
host: location.host,
path: location.pathname,
search: location.search,
adUnitCount: data.auctionInit.adUnitCodes ? data.auctionInit.adUnitCodes.length : null
});
data.initOptions.auctionId = data.auctionInit.auctionId;
delete data.auctionInit;
}
data.deviceDetail = {};
Object.assign(data.deviceDetail, {
platform: navigator.platform,
deviceType: getDeviceType(),
deviceOS: getOS(),
browser: getBrowser()
});
let pubxaiAnalyticsRequestUrl = utils.buildUrl({
protocol: 'https',
hostname: (initOptions && initOptions.hostName) || defaultHost,
Expand All @@ -156,8 +169,12 @@ function send(data, status) {
prebidVersion: $$PREBID_GLOBAL$$.version
}
});

ajax(pubxaiAnalyticsRequestUrl, undefined, JSON.stringify(data), { method: 'POST', contentType: 'text/plain' });
if (status == 'bidwon') {
ajax(pubxaiAnalyticsRequestUrl, undefined, JSON.stringify(data), { method: 'POST', contentType: 'text/json' });
} else if (status == 'auctionEnd' && !auctionCache.includes(data.initOptions.auctionId)) {
pnhegde marked this conversation as resolved.
Show resolved Hide resolved
ajax(pubxaiAnalyticsRequestUrl, undefined, JSON.stringify(data), { method: 'POST', contentType: 'text/json' });
auctionCache.push(data.initOptions.auctionId);
}
}
}

Expand Down
Loading