Skip to content

Commit

Permalink
New version of Konduit Accelerate module (prebid#5164)
Browse files Browse the repository at this point in the history
* Adding Konduit module

* Removed superfluous arguments passed to obtainVastUrl function

* Removed superfluous arguments passed to obtainVastUrl function.

* Build trigger (empty commit)

* Module documentation updated according to the comments

* Logic in obtainVastUrl function updated according to the review comment.

* Removed hook, enabled eslint

* Merged recent prebid changes

* New method is introduced to process a bid and return dynamic CPM data

* New Konduit Analytics adapter responsible for client auction stats collection

* Updated konduit analytics adapter .md file

* Fixed linter issue with more than 1 blank line used

* Use '$prebid.version$' instead of the $$PREBID_GLOBAL$$.version

* Updated unit tests

Co-authored-by: Max Shevchenko <max.shevchenko@sigma.software>
Co-authored-by: Alexander Kislitsyn <alexander.kislitsyn@sigmaukraine.com>
  • Loading branch information
3 people authored and Jimmy Tu committed Jun 12, 2020
1 parent c57ec9c commit 398622d
Show file tree
Hide file tree
Showing 6 changed files with 740 additions and 126 deletions.
225 changes: 225 additions & 0 deletions modules/konduitAnalyticsAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import { ajax } from '../src/ajax.js';
import adapter from '../src/AnalyticsAdapter.js';
import adapterManager from '../src/adapterManager.js';
import * as utils from '../src/utils.js';
import { targeting } from '../src/targeting.js';
import { config } from '../src/config.js';
import CONSTANTS from '../src/constants.json';

const TRACKER_HOST = 'tracker.konduit.me';

const analyticsType = 'endpoint';

const eventDataComposerMap = {
[CONSTANTS.EVENTS.AUCTION_INIT]: obtainAuctionInfo,
[CONSTANTS.EVENTS.AUCTION_END]: obtainAuctionInfo,
[CONSTANTS.EVENTS.BID_REQUESTED]: obtainBidRequestsInfo,
[CONSTANTS.EVENTS.BID_TIMEOUT]: obtainBidTimeoutInfo,
[CONSTANTS.EVENTS.BID_RESPONSE]: obtainBidResponseInfo,
[CONSTANTS.EVENTS.BID_WON]: obtainWinnerBidInfo,
[CONSTANTS.EVENTS.NO_BID]: obtainNoBidInfo,
};

// This function is copy from prebid core
function formatQS(query) {
return Object
.keys(query)
.map(k => Array.isArray(query[k])
? query[k].map(v => `${k}[]=${v}`).join('&')
: `${k}=${query[k]}`)
.join('&');
}

// This function is copy from prebid core
function buildUrl(obj) {
return (obj.protocol || 'http') + '://' +
(obj.host ||
obj.hostname + (obj.port ? `:${obj.port}` : '')) +
(obj.pathname || '') +
(obj.search ? `?${formatQS(obj.search || '')}` : '') +
(obj.hash ? `#${obj.hash}` : '');
}

const getWinnerBidFromAggregatedEvents = () => {
return konduitAnalyticsAdapter.context.aggregatedEvents
.filter(evt => evt.eventType === CONSTANTS.EVENTS.BID_WON)[0];
};

const isWinnerBidDetected = () => {
return !!getWinnerBidFromAggregatedEvents();
};
const isWinnerBidExist = () => {
return !!targeting.getWinningBids()[0];
};

const konduitAnalyticsAdapter = Object.assign(
adapter({ analyticsType }),
{
track ({ eventType, args }) {
if (CONSTANTS.EVENTS.AUCTION_INIT === eventType) {
konduitAnalyticsAdapter.context.aggregatedEvents.splice(0);
}

if (eventDataComposerMap[eventType]) {
konduitAnalyticsAdapter.context.aggregatedEvents.push({
eventType,
data: eventDataComposerMap[eventType](args),
});
}

if (eventType === CONSTANTS.EVENTS.AUCTION_END) {
if (!isWinnerBidDetected() && isWinnerBidExist()) {
const bidWonData = eventDataComposerMap[CONSTANTS.EVENTS.BID_WON](targeting.getWinningBids()[0]);

konduitAnalyticsAdapter.context.aggregatedEvents.push({
eventType: CONSTANTS.EVENTS.BID_WON,
data: bidWonData,
});
}
sendRequest({ method: 'POST', path: '/analytics-initial-event', payload: composeRequestPayload() });
}
}
}
);

function obtainBidTimeoutInfo (args) {
return args.map(item => item.bidder).filter(utils.uniques);
}

function obtainAuctionInfo (auction) {
return {
auctionId: auction.auctionId,
timestamp: auction.timestamp,
auctionEnd: auction.auctionEnd,
auctionStatus: auction.auctionStatus,
adUnitCodes: auction.adUnitCodes,
labels: auction.labels,
timeout: auction.timeout
};
}

function obtainBidRequestsInfo (bidRequests) {
return {
bidderCode: bidRequests.bidderCode,
time: bidRequests.start,
bids: bidRequests.bids.map(function (bid) {
return {
transactionId: bid.transactionId,
adUnitCode: bid.adUnitCode,
bidId: bid.bidId,
startTime: bid.startTime,
sizes: utils.parseSizesInput(bid.sizes).toString(),
params: bid.params
};
}),
};
}

function obtainBidResponseInfo (bidResponse) {
return {
bidderCode: bidResponse.bidder,
transactionId: bidResponse.transactionId,
adUnitCode: bidResponse.adUnitCode,
statusMessage: bidResponse.statusMessage,
mediaType: bidResponse.mediaType,
renderedSize: bidResponse.size,
cpm: bidResponse.cpm,
currency: bidResponse.currency,
netRevenue: bidResponse.netRevenue,
timeToRespond: bidResponse.timeToRespond,
bidId: bidResponse.bidId,
requestId: bidResponse.requestId,
creativeId: bidResponse.creativeId
};
}

function obtainNoBidInfo (bidResponse) {
return {
bidderCode: bidResponse.bidder,
transactionId: bidResponse.transactionId,
adUnitCode: bidResponse.adUnitCode,
bidId: bidResponse.bidId,
};
}

function obtainWinnerBidInfo (bidResponse) {
return {
adId: bidResponse.adId,
bidderCode: bidResponse.bidder,
adUnitCode: bidResponse.adUnitCode,
statusMessage: bidResponse.statusMessage,
mediaType: bidResponse.mediaType,
renderedSize: bidResponse.size,
cpm: bidResponse.cpm,
currency: bidResponse.currency,
netRevenue: bidResponse.netRevenue,
timeToRespond: bidResponse.timeToRespond,
bidId: bidResponse.requestId,
dealId: bidResponse.dealId,
status: bidResponse.status,
creativeId: bidResponse.creativeId
};
}

function composeRequestPayload () {
const konduitId = config.getConfig('konduit.konduitId');
const { width, height } = window.screen;

return {
konduitId,
prebidVersion: '$prebid.version$',
environment: {
screen: { width, height },
language: navigator.language,
},
events: konduitAnalyticsAdapter.context.aggregatedEvents,
};
}

function sendRequest ({ host = TRACKER_HOST, method, path, payload }) {
const formattedUrlOptions = {
protocol: 'https',
hostname: host,
pathname: path,
};
if (method === 'GET') {
formattedUrlOptions.search = payload;
}

let konduitAnalyticsRequestUrl = buildUrl(formattedUrlOptions);

ajax(
konduitAnalyticsRequestUrl,
undefined,
method === 'POST' ? JSON.stringify(payload) : null,
{
contentType: 'application/json',
method,
withCredentials: true
}
);
}

konduitAnalyticsAdapter.originEnableAnalytics = konduitAnalyticsAdapter.enableAnalytics;

konduitAnalyticsAdapter.enableAnalytics = function (analyticsConfig) {
const konduitId = config.getConfig('konduit.konduitId');

if (!konduitId) {
utils.logError('A konduitId in config is required to use konduitAnalyticsAdapter');
return;
}

konduitAnalyticsAdapter.context = {
aggregatedEvents: [],
};

konduitAnalyticsAdapter.originEnableAnalytics(analyticsConfig);
};

adapterManager.registerAnalyticsAdapter({
adapter: konduitAnalyticsAdapter,
code: 'konduit'
});

export default konduitAnalyticsAdapter;
32 changes: 32 additions & 0 deletions modules/konduitAnalyticsAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Overview
```
Module Name: Konduit Analytics Adapter
Module Type: Analytics Adapter
Maintainer: support@konduit.me
```
# Description
Konduit Analytics adapter pushes Prebid events into Konduit platform, which is then organizes the data and presents it to a client in different insightful views.
For more information, visit the [official Konduit website](https://konduitvideo.com/).
# Usage
Konduit Analytics can be enabled with a standard `enableAnalytics` call.
Note it is also important to provide a valid Konduit identifier as a config parameter.
```javascript
pbjs.setConfig({
konduit: {
konduitId: your_konduit_id,
}
});
pbjs.enableAnalytics({
provider: 'konduit'
})
```
Loading

0 comments on commit 398622d

Please sign in to comment.