Skip to content

Commit

Permalink
AdvRed Analytics Adapter : initial release (prebid#11703)
Browse files Browse the repository at this point in the history
* Initial version of AdvRed analytics adapter

* Initial version of AdvRed analytics adapter
  • Loading branch information
danoykin authored and DecayConstant committed Jul 18, 2024
1 parent 10c0336 commit 664712c
Show file tree
Hide file tree
Showing 3 changed files with 342 additions and 0 deletions.
198 changes: 198 additions & 0 deletions modules/advRedAnalyticsAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import {generateUUID, logInfo} from '../src/utils.js'
import {ajaxBuilder} from '../src/ajax.js'
import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'
import adapterManager from '../src/adapterManager.js'
import {EVENTS} from '../src/constants.js'
import {getRefererInfo} from '../src/refererDetection.js';

/**
* advRedAnalyticsAdapter.js - analytics adapter for AdvRed
*/
const DEFAULT_EVENT_URL = 'https://api.adv.red/api/event'

let ajax = ajaxBuilder(10000)
let pwId
let initOptions
let flushInterval
let queue = []

let advRedAnalytics = Object.assign(adapter({url: DEFAULT_EVENT_URL, analyticsType: 'endpoint'}), {
track({eventType, args}) {
handleEvent(eventType, args)
}
})

function sendEvents() {
if (queue.length > 0) {
const message = {
pwId: pwId,
publisherId: initOptions.publisherId,
events: queue,
pageUrl: getRefererInfo().page
}
queue = []

const url = initOptions.url ? initOptions.url : DEFAULT_EVENT_URL
ajax(
url,
() => logInfo('AdvRed Analytics sent ' + queue.length + ' events'),
JSON.stringify(message),
{
method: 'POST',
contentType: 'application/json',
withCredentials: true
}
)
}
}

function convertAdUnit(adUnit) {
if (!adUnit) return adUnit

const shortAdUnit = {}
shortAdUnit.code = adUnit.code
shortAdUnit.sizes = adUnit.sizes
return shortAdUnit
}

function convertBid(bid) {
if (!bid) return bid

const shortBid = {}
shortBid.adUnitCode = bid.adUnitCode
shortBid.bidder = bid.bidder
shortBid.cpm = bid.cpm
shortBid.currency = bid.currency
shortBid.mediaTypes = bid.mediaTypes
shortBid.sizes = bid.sizes
shortBid.serverResponseTimeMs = bid.serverResponseTimeMs
return shortBid
}

function convertAuctionInit(origEvent) {
let shortEvent = {}
shortEvent.auctionId = origEvent.auctionId
shortEvent.timeout = origEvent.timeout
shortEvent.adUnits = origEvent.adUnits && origEvent.adUnits.map(convertAdUnit)
return shortEvent
}

function convertBidRequested(origEvent) {
let shortEvent = {}
shortEvent.bidderCode = origEvent.bidderCode
shortEvent.bids = origEvent.bids && origEvent.bids.map(convertBid)
shortEvent.timeout = origEvent.timeout
return shortEvent
}

function convertBidTimeout(origEvent) {
let shortEvent = {}
shortEvent.bids = origEvent && origEvent.map ? origEvent.map(convertBid) : origEvent
return shortEvent
}

function convertBidderError(origEvent) {
let shortEvent = {}
shortEvent.bids = origEvent.bidderRequest && origEvent.bidderRequest.bids && origEvent.bidderRequest.bids.map(convertBid)
return shortEvent
}

function convertAuctionEnd(origEvent) {
let shortEvent = {}
shortEvent.adUnitCodes = origEvent.adUnitCodes
shortEvent.bidsReceived = origEvent.bidsReceived && origEvent.bidsReceived.map(convertBid)
shortEvent.noBids = origEvent.noBids && origEvent.noBids.map(convertBid)
return shortEvent
}

function convertBidWon(origEvent) {
let shortEvent = {}
shortEvent.adUnitCode = origEvent.adUnitCode
shortEvent.bidderCode = origEvent.bidderCode
shortEvent.mediaType = origEvent.mediaType
shortEvent.netRevenue = origEvent.netRevenue
shortEvent.cpm = origEvent.cpm
shortEvent.size = origEvent.size
shortEvent.currency = origEvent.currency
return shortEvent
}

function handleEvent(eventType, origEvent) {
try {
origEvent = origEvent ? JSON.parse(JSON.stringify(origEvent)) : {}
} catch (e) {
}

let shortEvent
switch (eventType) {
case EVENTS.AUCTION_INIT: {
shortEvent = convertAuctionInit(origEvent)
break
}
case EVENTS.BID_REQUESTED: {
shortEvent = convertBidRequested(origEvent)
break
}
case EVENTS.BID_TIMEOUT: {
shortEvent = convertBidTimeout(origEvent)
break
}
case EVENTS.BIDDER_ERROR: {
shortEvent = convertBidderError(origEvent)
break
}
case EVENTS.AUCTION_END: {
shortEvent = convertAuctionEnd(origEvent)
break
}
case EVENTS.BID_WON: {
shortEvent = convertBidWon(origEvent)
break
}
default:
return
}

shortEvent.eventType = eventType
shortEvent.auctionId = origEvent.auctionId
shortEvent.timestamp = origEvent.timestamp || Date.now()

sendEvent(shortEvent)
}

function sendEvent(event) {
queue.push(event)

if (event.eventType === EVENTS.AUCTION_END) {
sendEvents()
}
}

advRedAnalytics.originEnableAnalytics = advRedAnalytics.enableAnalytics
advRedAnalytics.enableAnalytics = function (config) {
initOptions = config.options || {}
pwId = generateUUID()
flushInterval = setInterval(sendEvents, 1000)

advRedAnalytics.originEnableAnalytics(config)
}

advRedAnalytics.originDisableAnalytics = advRedAnalytics.disableAnalytics
advRedAnalytics.disableAnalytics = function () {
clearInterval(flushInterval)
sendEvents()
advRedAnalytics.originDisableAnalytics()
}

adapterManager.registerAnalyticsAdapter({
adapter: advRedAnalytics,
code: 'advRed'
})

advRedAnalytics.getOptions = function () {
return initOptions
}

advRedAnalytics.sendEvents = sendEvents

export default advRedAnalytics
30 changes: 30 additions & 0 deletions modules/advRedAnalyticsAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Overview
```
Module Name: AdvRed Analytics Adapter
Module Type: Analytics Adapter
Maintainer: support@adv.red
```

### Usage

The AdvRed analytics adapter can be used by all clients after approval. For more information,
please visit <https://ams.adv.red/>

### Analytics Options
| Param enableAnalytics | Scope | Type | Description | Example |
|-----------------------|----------|--------|------------------------------------------------------|----------------------------------------|
| provider | Required | String | The name of this Adapter. | `'advRed'` |
| params | Required | Object | Details of module params. | |
| params.publisherId | Required | String | This is the Publisher ID value obtained from AdvRed. | `'123456'` |
| params.url | Optional | String | Custom URL of the endpoint to collect the events | `'https://pub1.api.adv.red/api/event'` |

### Example Configuration

```javascript
pbjs.enableAnalytics({
provider: 'advRed',
options: {
publisherId: '123456' // change to the Publisher ID you received from AdvRed
}
});
```
114 changes: 114 additions & 0 deletions test/spec/modules/advRedAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import advRedAnalytics from 'modules/advRedAnalyticsAdapter.js';
import {expect} from 'chai';
import {server} from 'test/mocks/xhr.js';
import {expectEvents} from '../../helpers/analytics.js';
import { EVENTS } from 'src/constants.js';
import sinon from 'sinon';

let events = require('src/events');

describe('AdvRed Analytics Adapter', function () {
let bidWonEvent = {
'bidderCode': 'appnexus',
'width': 300,
'height': 250,
'adId': '1ebb82ec35375e',
'mediaType': 'banner',
'cpm': 0.5,
'requestId': '1582271863760569973',
'creative_id': '96846035',
'creativeId': '96846035',
'ttl': 60,
'currency': 'USD',
'netRevenue': true,
'auctionId': '9c7b70b9-b6ab-4439-9e71-b7b382797c18',
'responseTimestamp': 1537521629657,
'requestTimestamp': 1537521629331,
'bidder': 'appnexus',
'adUnitCode': 'div-gpt-ad-1460505748561-0',
'timeToRespond': 326,
'size': '300x250',
'status': 'rendered',
'eventType': 'bidWon',
'ad': 'some ad',
'adUrl': 'ad url'
};

describe('AdvRed Analytic tests', function () {
beforeEach(function () {
sinon.stub(events, 'getEvents').returns([]);
});

afterEach(function () {
advRedAnalytics.disableAnalytics();
events.getEvents.restore();
});

it('support custom endpoint', function () {
let custom_endpoint = 'custom url';
advRedAnalytics.enableAnalytics({
provider: 'advRed',
options: {
url: custom_endpoint,
publisherId: '1234567890'
}
});

expect(advRedAnalytics.getOptions().url).to.equal(custom_endpoint);
});

it('bid won event', function() {
let publisherId = '1234567890';
advRedAnalytics.enableAnalytics({
provider: 'advRed',
options: {
publisherId: publisherId
}
});

events.emit(EVENTS.BID_WON, bidWonEvent);
advRedAnalytics.sendEvents();

expect(server.requests.length).to.equal(1);
expect(server.requests[0].url).to.equal('https://api.adv.red/api/event');

const message = JSON.parse(server.requests[0].requestBody);
expect(message.pwId).to.exist;
expect(message.publisherId).to.equal(publisherId);
expect(message.events.length).to.equal(1);
expect(message.events[0].eventType).to.equal('bidWon');
expect(message.events[0].ad).to.be.undefined;
expect(message.events[0].adUrl).to.be.undefined;
});

it('track event', function () {
sinon.spy(advRedAnalytics, 'track');

advRedAnalytics.enableAnalytics({
provider: 'advRed',
options: {
publisherId: '1234567890'
}
});

expectEvents().to.beTrackedBy(advRedAnalytics.track);
});
});

describe('pageUrl detection', function () {
afterEach(function () {
advRedAnalytics.disableAnalytics()
});
it('check pageUrl property', function () {
advRedAnalytics.enableAnalytics({
provider: 'advRed',
options: {
publisherId: '1234567890'
}
});

const message = JSON.parse(server.requests[0].requestBody);
expect(message.pageUrl).to.equal(window.top.location.href);
});
});
});

0 comments on commit 664712c

Please sign in to comment.