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

Allox Analytics Adapter : initial release #12731

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
90 changes: 90 additions & 0 deletions modules/alloxAnalyticsAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js';
import adapterManager from '../src/adapterManager.js';
import { EVENTS } from '../src/constants.js';
import { MODULE_TYPE_ANALYTICS } from '../src/activities/modules.js';
import { getStorageManager } from '../src/storageManager.js';
import {
isEmpty,
triggerPixel
} from '../src/utils.js';

const analyticsType = 'endpoint';
const url = 'URL_TO_SERVER_ENDPOINT';
const PROVIDER_NAME = 'allox';

const TRACKER_TYPE = {
IMP: 1,
LOSE_NOTICE_WHEN_ALLOX_WIN: 101,
LOSE_NOTICE_WHEN_ALLOX_LOSE: 102
};

const LURL_REG = {
CPM: /\$\{ALLOX:AUCTION_PRICE\}/g,
CURRENCY: /\$\{ALLOX:AUCTION_CURRENCY\}/g
};

export const STORAGE_KEY = '__allox_trackers';
export const storage = getStorageManager({ moduleType: MODULE_TYPE_ANALYTICS, moduleName: PROVIDER_NAME });

const alloxAnalytics = Object.assign(
adapter({ url, analyticsType }), {
track({ eventType, args }) {
switch (eventType) {
case EVENTS.BID_WON:
this.onBidWon(args);
break;
}
},
onBidWon(bid) {
if (storage.localStorageIsEnabled()) {
const trackersValue = storage.getDataFromLocalStorage(STORAGE_KEY);
const trackers = trackersValue ? JSON.parse(trackersValue) : {};

if (bid.adUnitId in trackers) {
if (bid.bidder === PROVIDER_NAME) {
this.requestLurlFromTrackers(bid.trackers, TRACKER_TYPE.LOSE_NOTICE_WHEN_ALLOX_WIN);
} else {
const trackersBid = trackers[bid.adUnitId];
if (trackersBid.lurl) {
this.sendLurl(trackersBid.lurl, bid);
};
if (trackersBid.trackers) {
this.requestLurlFromTrackers(trackersBid.trackers, TRACKER_TYPE.LOSE_NOTICE_WHEN_ALLOX_LOSE, bid);
};
};
}
}
},
sendLurl(url, wonBid) {
if (wonBid) {
const lurl = url
.replace(LURL_REG.CPM, wonBid.originalCpm)
.replace(LURL_REG.CURRENCY, wonBid.originalCurrency);
triggerPixel(lurl);
} else {
triggerPixel(url);
};
},
requestLurlFromTrackers(trackers, trackerType, wonBid) {
const lurlTracker = trackers.filter(tracker => tracker.type === trackerType);
if (!isEmpty(lurlTracker)) {
lurlTracker.forEach(tracker => {
this.sendLurl(tracker.url, wonBid);
});
};
}
}
);

alloxAnalytics.originEnableAnalytics = alloxAnalytics.enableAnalytics;

alloxAnalytics.enableAnalytics = function (config) {
alloxAnalytics.originEnableAnalytics(config);
};

adapterManager.registerAnalyticsAdapter({
adapter: alloxAnalytics,
code: PROVIDER_NAME,
});

export default alloxAnalytics;
23 changes: 23 additions & 0 deletions modules/alloxAnalyticsAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Overview

```txt
Module Name: allox Analytics Adapter
Module Type: Analytics Adapter
Maintainer: [email protected]
```

# About

The Allox Analytics Adapter collects and transmits bidding data to Allox's internal analytics server.

This includes bids that lost within Allox's system as well as cases where Allox lost in the overall auction.

The collected data is used to analyze auction performance and optimize bidding strategies.

# Example Configuration

```js
pbjs.enableAnalytics({
provider: 'allox'
});
```
132 changes: 132 additions & 0 deletions test/spec/modules/alloxAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import alloxAnalyticsAdapter, { storage, STORAGE_KEY } from 'modules/alloxAnalyticsAdapter.js';
import { expect } from 'chai';
import adapterManager from 'src/adapterManager.js';
import { EVENTS } from 'src/constants.js';
import * as utils from 'src/utils.js';

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

describe('allox analytics adapter', function () {
let adUnitId = '9361d2b7-38da-4900-9713-0bc765f7b36b';
let mockTrackers = {
[adUnitId]: {
trackers: [
{
type: 1,
url: 'https://alxl-s.allox-s.allox.d2c.ne.jp/529833ce55314b19e8796116/imp',
method: 'GET'
},
{
type: 1,
url: 'https://alxl-s.allox-s.allox.d2c.ne.jp/prebid/imp',
method: 'GET'
},
{
type: 101,
url: 'https://alxl-s.allox-s.allox.d2c.ne.jp/529833ce55314b19e8796116/lose_101',
method: 'GET'
},
{
type: 102,
url: 'https://alxl-s.allox-s.allox.d2c.ne.jp/xdp/v1/notify/lose_102?wprice=${ALLOX:AUCTION_PRICE}&wcur=${ALLOX:AUCTION_CURRENCY}',
method: 'GET'
}
],
lurl: 'https://alxl-s.allox-s.allox.d2c.ne.jp/xdp/v1/notify/lose?wprice=${ALLOX:AUCTION_PRICE}&wcur=${ALLOX:AUCTION_CURRENCY}',
date: 1736236376718
}
};
let wonBid;
let triggerPixelStub;
let replaceMacro = (url) => {
return url.replace('${ALLOX:AUCTION_PRICE}', wonBid.originalCpm).replace('${ALLOX:AUCTION_CURRENCY}', wonBid.originalCurrency);
};

before(function () {
storage.setDataInLocalStorage(STORAGE_KEY, JSON.stringify(mockTrackers));
});

beforeEach(function () {
wonBid = {
'adId': '11a0ac97879de06',
'adUnitId': adUnitId,
'mediaType': 'banner',
'requestId': '10feb9ccd60c8e9',
'cpm': 20,
'creativeId': '529833ce55314b19e8796116_1385706446',
'currency': 'JPY',
'netRevenue': true,
'ttl': 300,
'auctionId': '1e221e41-c811-462b-8def-fea6ecc4d5eb',
'statusMessage': 'Bid available',
'responseTimestamp': 1736988970347,
'requestTimestamp': 1736988970123,
'bidder': 'allox',
'adUnitCode': 'banner-test',
'timeToRespond': 218,
'size': '300x250',
'status': 'rendered',
'trackers': mockTrackers[adUnitId].trackers,
'originalCpm': 20,
'originalCurrency': 'JPY',
'lurl': 'https://alxl-s.allox-s.allox.d2c.ne.jp/xdp/v1/notify/lose?wprice=${ALLOX:AUCTION_PRICE}&wcur=${ALLOX:AUCTION_CURRENCY}',
};
adapterManager.enableAnalytics({
provider: 'allox'
});
sinon.stub(events, 'getEvents').returns([]);
sinon.spy(alloxAnalyticsAdapter, 'onBidWon');
triggerPixelStub = sinon.stub(utils, 'triggerPixel');
});

afterEach(function () {
utils.triggerPixel.restore();
events.getEvents.restore();
alloxAnalyticsAdapter.onBidWon.restore();
alloxAnalyticsAdapter.disableAnalytics();
});

describe('allox bid adapter with bid response', function () {
describe('win allox', function() {
it('should fire tracker type 101 url on bid won', function() {
events.emit(EVENTS.BID_WON, wonBid);
const type101Tracker = mockTrackers[adUnitId].trackers.find(tracker => tracker.type === 101);
expect(triggerPixelStub.args[0]).to.include(type101Tracker.url);
expect(triggerPixelStub.callCount).to.equal(1);
});
});

describe('lose allox', function() {
it('should fire allox bid response lurl on bid won', function() {
wonBid.bidder = 'otherBidder';
events.emit(EVENTS.BID_WON, wonBid);
const lurl = replaceMacro(wonBid.lurl);
expect(triggerPixelStub.args[0]).to.include(lurl);
expect(triggerPixelStub.callCount).to.equal(2);
});

it('should fire tracker type 102 url on bid won', function() {
wonBid.bidder = 'otherBidder';
events.emit(EVENTS.BID_WON, wonBid);
const type102Tracker = mockTrackers[adUnitId].trackers.find(tracker => tracker.type === 102);
const lurl = replaceMacro(type102Tracker.url);
expect(triggerPixelStub.args[1]).to.include(lurl);
expect(triggerPixelStub.callCount).to.equal(2);
});
});
});

describe('allox bid adapter without bid response', function () {
describe('lose allox', function() {
it('should fire tracker type 102 url on bid won', function() {
wonBid.bidder = 'otherBidder';
events.emit(EVENTS.BID_WON, wonBid);
const type102Tracker = mockTrackers[adUnitId].trackers.find(tracker => tracker.type === 102);
const lurl = replaceMacro(type102Tracker.url);
expect(triggerPixelStub.args[1]).to.include(lurl);
expect(triggerPixelStub.callCount).to.equal(2);
});
});
});
});