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

Analytics adapters: attach arbitrary labels to analytics events #12597

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
18 changes: 16 additions & 2 deletions libraries/analyticsAdapter/AnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ import { EVENTS } from '../../src/constants.js';
import {ajax} from '../../src/ajax.js';
import {logError, logMessage} from '../../src/utils.js';
import * as events from '../../src/events.js';
import {config} from '../../src/config.js';

export const _internal = {
ajax
};
const ENDPOINT = 'endpoint';
const BUNDLE = 'bundle';
const LABELS_KEY = 'analyticsLabels';

let labels = {};

config.getConfig(LABELS_KEY, (cfg) => {
labels = cfg[LABELS_KEY]
});

export const DEFAULT_INCLUDE_EVENTS = Object.values(EVENTS)
.filter(ev => ev !== EVENTS.AUCTION_DEBUG);
Expand Down Expand Up @@ -90,12 +98,18 @@ export default function AnalyticsAdapter({ url, analyticsType, global, handler }
}

function _callEndpoint({ eventType, args, callback }) {
_internal.ajax(url, callback, JSON.stringify({ eventType, args }));
_internal.ajax(url, callback, JSON.stringify({ eventType, args, labels }));
}

function _enqueue({eventType, args}) {
queue.push(() => {
this.track({eventType, args});
if (Object.keys(labels || []).length > 0) {
args = {
[LABELS_KEY]: labels,
...args,
}
}
this.track({eventType, labels, args});
});
emptyQueue();
}
Expand Down
89 changes: 80 additions & 9 deletions test/spec/AnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import {
DEFAULT_INCLUDE_EVENTS,
setDebounceDelay
} from '../../libraries/analyticsAdapter/AnalyticsAdapter.js';
import {config} from 'src/config.js';

const BID_WON = EVENTS.BID_WON;
const NO_BID = EVENTS.NO_BID;

const AnalyticsAdapter = require('libraries/analyticsAdapter/AnalyticsAdapter.js').default;
const config = {
const adapterConfig = {
url: 'https://localhost:9999/endpoint',
analyticsType: 'endpoint'
};
Expand All @@ -29,7 +30,7 @@ FEATURE: Analytics Adapters API
after(disableAjaxForAnalytics);

beforeEach(function () {
adapter = new AnalyticsAdapter(config);
adapter = new AnalyticsAdapter(adapterConfig);
});

afterEach(function () {
Expand All @@ -52,7 +53,7 @@ FEATURE: Analytics Adapters API
adapter.track({eventType, args});

let result = JSON.parse(server.requests[0].requestBody);
expect(result).to.deep.equal({args: {some: 'data'}, eventType});
sinon.assert.match(result, {args: {some: 'data'}, eventType})
});

it(`SHOULD queue the event first and then track it WHEN an event occurs before tracking library is available`, function () {
Expand All @@ -65,9 +66,79 @@ FEATURE: Analytics Adapters API
// As now AUCTION_DEBUG is triggered for WARNINGS too, the BID_RESPONSE goes last in the array
const index = server.requests.length - 1;
let result = JSON.parse(server.requests[index].requestBody);
expect(result).to.deep.equal({eventType, args: {wat: 'wot'}});
sinon.assert.match(result, {eventType, args: {wat: 'wot'}})
});

describe('analyticsLabels', () => {
let analyticsLabels;
beforeEach(() => {
analyticsLabels = {
experiment_1: 'group_a',
experiment_2: 'group_b'
}
config.setConfig({
analyticsLabels
})
})

it('should be attached to payloads (type: endpoint)', () => {
events.emit(BID_WON, {foo: 'bar'});
adapter.enableAnalytics();
server.requests
.map(req => JSON.parse(req.requestBody))
.forEach(payload => sinon.assert.match(payload, {labels: analyticsLabels, args: sinon.match({analyticsLabels})}))
});

it('should be attached payloads (type: bundle)', () => {
adapter = new AnalyticsAdapter({
analyticsType: 'bundle',
global: 'analytics'
})
window.analytics = sinon.stub();
try {
events.emit(BID_WON, {foo: 'bar'})
adapter.enableAnalytics();
sinon.assert.calledWith(window.analytics, sinon.match.any, BID_WON, sinon.match({analyticsLabels}))
} finally {
delete window.analytics;
}
});

it('should be passed to custom track', () => {
Object.assign(adapter, {
track: sinon.stub()
});
events.emit(BID_WON, {foo: 'bar'});
adapter.enableAnalytics();
sinon.assert.calledWith(adapter.track, sinon.match({
eventType: BID_WON,
args: sinon.match({analyticsLabels}),
labels: analyticsLabels
}))
})

it('should not override the "analyticsLabels" property an event payload may have', () => {
adapter.track = sinon.stub();
events.emit(BID_WON, {analyticsLabels: 'not these ones'});
adapter.enableAnalytics();
sinon.assert.calledWith(adapter.track, sinon.match({
args: {analyticsLabels: 'not these ones'}
}));
});

it('should not modify event payloads when there are no labels', () => {
config.resetConfig();
adapter.track = sinon.stub();
events.emit(BID_WON, {'foo': 'bar'});
adapter.enableAnalytics();
sinon.assert.calledWith(adapter.track, {
labels: {},
args: {foo: 'bar'},
eventType: BID_WON
})
})
})

describe('event filters', () => {
function fireEvents() {
events.emit(BID_WON, {});
Expand Down Expand Up @@ -112,7 +183,7 @@ FEATURE: Analytics Adapters API
events.emit(BID_WON, {})
}
})(adapter.track);
adapter.enableAnalytics(config);
adapter.enableAnalytics(adapterConfig);
events.emit(BID_WON, {});
expect(i >= 100).to.eql(false);
})
Expand Down Expand Up @@ -176,7 +247,7 @@ FEATURE: Analytics Adapters API

expect(server.requests.length).to.equal(1);
let result = JSON.parse(server.requests[0].requestBody);
expect(result).to.deep.equal({args: {more: 'info'}, eventType: 'bidWon'});
sinon.assert.match(result, {args: {more: 'info'}, eventType: 'bidWon'})
});

it(`THEN should disable analytics when random number is outside sample range`, function () {
Expand Down Expand Up @@ -205,7 +276,7 @@ describe('Analytics asynchronous event tracking', () => {

beforeEach(() => {
clock = sinon.useFakeTimers();
adapter = new AnalyticsAdapter(config);
adapter = new AnalyticsAdapter(adapterConfig);
adapter.track = sinon.stub();
adapter.enableAnalytics({});
});
Expand All @@ -224,7 +295,7 @@ describe('Analytics asynchronous event tracking', () => {
sinon.assert.notCalled(adapter.track);
clock.tick(100);
sinon.assert.calledTwice(adapter.track);
sinon.assert.calledWith(adapter.track.firstCall, {eventType: BID_WON, args: {i: 0}});
sinon.assert.calledWith(adapter.track.secondCall, {eventType: BID_WON, args: {i: 1}});
sinon.assert.calledWith(adapter.track.firstCall, sinon.match({eventType: BID_WON, args: {i: 0}}));
sinon.assert.calledWith(adapter.track.secondCall, sinon.match({eventType: BID_WON, args: {i: 1}}));
});
})
2 changes: 1 addition & 1 deletion test/spec/modules/genericAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ describe('Generic analytics', () => {
recv = arg;
});
events.emit(BID_RESPONSE, {i: 1});
expect(recv).to.eql([{eventType: BID_RESPONSE, args: {i: 1}}]);
sinon.assert.match(recv, [sinon.match({eventType: BID_RESPONSE, args: {i: 1}})])
});

it('should not cause infinite recursion, if handler triggers more events', () => {
Expand Down
Loading