Skip to content

Commit

Permalink
adomik Analytics Adapter: sampling revamp, add AB test info, enhance …
Browse files Browse the repository at this point in the history
…bidWon info (prebid#8122)

* Adomik Analytics Adapter: Save bid response of the won event

* wip

* adomikAnayticsAdapter/receive KV from sessionStorage

* AdomikAnalyticsAdapter: add specs

* kick off testing

Co-authored-by: thomasferal <thomas.feral@adomik.com>
Co-authored-by: Chris Huie <phoenixtechnerd@gmail.com>
  • Loading branch information
3 people authored and JoelPM committed Apr 25, 2022
1 parent a13d1b5 commit 4b274f4
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 76 deletions.
110 changes: 67 additions & 43 deletions modules/adomikAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import adapterManager from '../src/adapterManager.js';
import {logInfo} from '../src/utils.js';
import {find, findIndex} from '../src/polyfill.js';

// Events used in adomik analytics adapter
// Events used in adomik analytics adapter.
const auctionInit = CONSTANTS.EVENTS.AUCTION_INIT;
const auctionEnd = CONSTANTS.EVENTS.AUCTION_END;
const bidRequested = CONSTANTS.EVENTS.BID_REQUESTED;
Expand All @@ -30,17 +30,13 @@ let adomikAdapter = Object.assign(adapter({}),
break;

case bidResponse:
adomikAdapter.bucketEvents.push({
type: 'response',
event: adomikAdapter.buildBidResponse(args)
});
adomikAdapter.saveBidResponse(args);
break;

case bidWon:
adomikAdapter.sendWonEvent({
id: args.adId,
placementCode: args.adUnitCode
});
args.id = args.adId;
args.placementCode = args.adUnitCode;
adomikAdapter.sendWonEvent(args);
break;

case bidRequested:
Expand Down Expand Up @@ -69,16 +65,28 @@ adomikAdapter.initializeBucketEvents = function() {
adomikAdapter.bucketEvents = [];
}

adomikAdapter.saveBidResponse = function(args) {
let responseSaved = adomikAdapter.bucketEvents.find((bucketEvent) =>
bucketEvent.type == 'response' && bucketEvent.event.id == args.id
);
if (responseSaved) { return true; }
adomikAdapter.bucketEvents.push({
type: 'response',
event: adomikAdapter.buildBidResponse(args)
});
}

adomikAdapter.maxPartLength = function () {
return (ua.includes(' MSIE ')) ? 1600 : 60000;
};

adomikAdapter.sendTypedEvent = function() {
let [testId, testValue] = adomikAdapter.getKeyValues();
const groupedTypedEvents = adomikAdapter.buildTypedEvents();

const bulkEvents = {
testId: adomikAdapter.currentContext.testId,
testValue: adomikAdapter.currentContext.testValue,
testId: testId,
testValue: testValue,
uid: adomikAdapter.currentContext.uid,
ahbaid: adomikAdapter.currentContext.id,
hostname: window.location.hostname,
Expand Down Expand Up @@ -114,10 +122,8 @@ adomikAdapter.sendTypedEvent = function() {
const stringBulkEvents = JSON.stringify(bulkEvents)
logInfo('Events sent to adomik prebid analytic ' + stringBulkEvents);

// Encode object in base64
const encodedBuf = window.btoa(stringBulkEvents);

// Create final url and split it (+endpoint length)
const encodedUri = encodeURIComponent(encodedBuf);
const maxLength = adomikAdapter.maxPartLength();
const splittedUrl = encodedUri.match(new RegExp(`.{1,${maxLength}}`, 'g'));
Expand All @@ -130,16 +136,18 @@ adomikAdapter.sendTypedEvent = function() {
};

adomikAdapter.sendWonEvent = function (wonEvent) {
let keyValues = { testId: adomikAdapter.currentContext.testId, testValue: adomikAdapter.currentContext.testValue }
wonEvent = {...wonEvent, ...keyValues}
const stringWonEvent = JSON.stringify(wonEvent)
let [testId, testValue] = adomikAdapter.getKeyValues();
let keyValues = { testId: testId, testValue: testValue };
let samplingInfo = { sampling: adomikAdapter.currentContext.sampling };
wonEvent = { ...adomikAdapter.buildBidResponse(wonEvent), ...keyValues, ...samplingInfo };

const stringWonEvent = JSON.stringify(wonEvent);
logInfo('Won event sent to adomik prebid analytic ' + stringWonEvent);

// Encode object in base64
const encodedBuf = window.btoa(stringWonEvent);
const encodedUri = encodeURIComponent(encodedBuf);
const img = new Image(1, 1);
img.src = `https://${adomikAdapter.currentContext.url}/?q=${encodedUri}&id=${adomikAdapter.currentContext.id}&won=true`
img.src = `https://${adomikAdapter.currentContext.url}/?q=${encodedUri}&id=${adomikAdapter.currentContext.id}&won=true`;
}

adomikAdapter.buildBidResponse = function (bid) {
Expand Down Expand Up @@ -201,33 +209,49 @@ adomikAdapter.buildTypedEvents = function () {
return groupedTypedEvents;
}

adomikAdapter.adapterEnableAnalytics = adomikAdapter.enableAnalytics;
adomikAdapter.getKeyValues = function () {
let preventTest = sessionStorage.getItem(window.location.hostname + '_NoAdomikTest')
let inScope = sessionStorage.getItem(window.location.hostname + '_AdomikTestInScope')
let keyValues = JSON.parse(sessionStorage.getItem(window.location.hostname + '_AdomikTest'))
let testId;
let testValue;
if (typeof (keyValues) === 'object' && keyValues != undefined && !preventTest && inScope) {
testId = keyValues.testId
testValue = keyValues.testOptionLabel
}
return [testId, testValue]
}

adomikAdapter.enableAnalytics = function (config) {
adomikAdapter.currentContext = {};
const initOptions = config.options;

_sampled = typeof config === 'undefined' ||
typeof config.sampling === 'undefined' ||
Math.random() < parseFloat(config.sampling);

if (_sampled) {
if (initOptions) {
adomikAdapter.currentContext = {
uid: initOptions.id,
url: initOptions.url,
testId: initOptions.testId,
testValue: initOptions.testValue,
id: '',
timeouted: false,
sampling: config.sampling
}
logInfo('Adomik Analytics enabled with config', initOptions);
adomikAdapter.adapterEnableAnalytics(config);
}
} else {
logInfo('Adomik Analytics ignored for sampling', config.sampling);
adomikAdapter.enable = function(options) {
adomikAdapter.currentContext = {
uid: options.id,
url: options.url,
id: '',
timeouted: false,
sampling: options.sampling
}
logInfo('Adomik Analytics enabled with config', options);
adomikAdapter.adapterEnableAnalytics(options);
};

adomikAdapter.checkOptions = function(options) {
if (typeof options !== 'undefined') {
if (options.id && options.url) { adomikAdapter.enable(options); } else { logInfo('Adomik Analytics disabled because id and/or url is missing from config', options); }
} else { logInfo('Adomik Analytics disabled because config is missing'); }
};

adomikAdapter.checkSampling = function(options) {
_sampled = typeof options === 'undefined' ||
typeof options.sampling === 'undefined' ||
(options.sampling > 0 && Math.random() < parseFloat(options.sampling));
if (_sampled) { adomikAdapter.checkOptions(options) } else { logInfo('Adomik Analytics ignored for sampling', options.sampling); }
};

adomikAdapter.adapterEnableAnalytics = adomikAdapter.enableAnalytics;

adomikAdapter.enableAnalytics = function ({ provider, options }) {
logInfo('Adomik Analytics enableAnalytics', provider);
adomikAdapter.checkSampling(options);
};

adapterManager.registerAnalyticsAdapter({
Expand Down
166 changes: 133 additions & 33 deletions test/spec/modules/adomikAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,34 @@ describe('Adomik Prebid Analytic', function () {
let sendWonEventStub;
let clock;

before(function () {
beforeEach(function () {
clock = sinon.useFakeTimers();
sinon.spy(adomikAnalytics, 'track');
sendEventStub = sinon.stub(adomikAnalytics, 'sendTypedEvent');
sendWonEventStub = sinon.stub(adomikAnalytics, 'sendWonEvent');
sinon.stub(events, 'getEvents').returns([]);
adomikAnalytics.currentContext = undefined;

adapterManager.registerAnalyticsAdapter({
code: 'adomik',
adapter: adomikAnalytics
});
});
after(function () {

afterEach(function () {
adomikAnalytics.disableAnalytics();
clock.restore();
adomikAnalytics.track.restore();
sendEventStub.restore();
sendWonEventStub.restore();
events.getEvents.restore();
});

describe('enableAnalytics', function () {
beforeEach(function () {
sinon.spy(adomikAnalytics, 'track');
sendEventStub = sinon.stub(adomikAnalytics, 'sendTypedEvent');
sendWonEventStub = sinon.stub(adomikAnalytics, 'sendWonEvent');
sinon.stub(events, 'getEvents').returns([]);
});

afterEach(function () {
adomikAnalytics.track.restore();
sendEventStub.restore();
sendWonEventStub.restore();
events.getEvents.restore();
});

after(function () {
adomikAnalytics.disableAnalytics();
});

describe('adomikAnalytics.enableAnalytics', function () {
it('should catch all events', function (done) {
adapterManager.registerAnalyticsAdapter({
code: 'adomik',
adapter: adomikAnalytics
});

const initOptions = {
id: '123456',
url: 'testurl',
testId: '12345',
testValue: '1000'
url: 'testurl'
};

const bid = {
Expand Down Expand Up @@ -74,8 +65,6 @@ describe('Adomik Prebid Analytic', function () {
uid: '123456',
url: 'testurl',
sampling: undefined,
testId: '12345',
testValue: '1000',
id: '',
timeouted: false
});
Expand All @@ -87,8 +76,6 @@ describe('Adomik Prebid Analytic', function () {
uid: '123456',
url: 'testurl',
sampling: undefined,
testId: '12345',
testValue: '1000',
id: 'test-test-test',
timeouted: false
});
Expand Down Expand Up @@ -149,5 +136,118 @@ describe('Adomik Prebid Analytic', function () {

sinon.assert.callCount(adomikAnalytics.track, 6);
});

describe('when sampling is undefined', function () {
beforeEach(function() {
adapterManager.enableAnalytics({
provider: 'adomik',
options: { id: '123456', url: 'testurl' }
});
});

it('is enabled', function () {
expect(adomikAnalytics.currentContext).is.not.null;
});
});

describe('when sampling is 0', function () {
beforeEach(function() {
adapterManager.enableAnalytics({
provider: 'adomik',
options: { id: '123456', url: 'testurl', sampling: 0 }
});
});

it('is disabled', function () {
expect(adomikAnalytics.currentContext).to.equal(undefined);
});
});

describe('when sampling is 1', function () {
beforeEach(function() {
adapterManager.enableAnalytics({
provider: 'adomik',
options: { id: '123456', url: 'testurl', sampling: 1 }
});
});

it('is enabled', function () {
expect(adomikAnalytics.currentContext).is.not.null;
});
});

describe('when options is not defined', function () {
beforeEach(function() {
adapterManager.enableAnalytics({ provider: 'adomik' });
});

it('is disabled', function () {
expect(adomikAnalytics.currentContext).to.equal(undefined);
});
});

describe('when id is not defined in options', function () {
beforeEach(function() {
adapterManager.enableAnalytics({ provider: 'adomik', url: 'xxx' });
});

it('is disabled', function () {
expect(adomikAnalytics.currentContext).to.equal(undefined);
});
});

describe('when url is not defined in options', function () {
beforeEach(function() {
adapterManager.enableAnalytics({ provider: 'adomik', id: 'xxx' });
});

it('is disabled', function () {
expect(adomikAnalytics.currentContext).to.equal(undefined);
});
});
});

describe('adomikAnalytics.getKeyValues', function () {
it('returns [undefined, undefined]', function () {
let [testId, testValue] = adomikAnalytics.getKeyValues()
expect(testId).to.equal(undefined);
expect(testValue).to.equal(undefined);
});

describe('when test is in scope', function () {
beforeEach(function () {
sessionStorage.setItem(window.location.hostname + '_AdomikTestInScope', true);
});

it('returns [undefined, undefined]', function () {
let [testId, testValue] = adomikAnalytics.getKeyValues()
expect(testId).to.equal(undefined);
expect(testValue).to.equal(undefined);
});

describe('when key values are defined', function () {
beforeEach(function () {
sessionStorage.setItem(window.location.hostname + '_AdomikTest', '{"testId":"12345","testOptionLabel":"1000"}');
});

it('returns key values', function () {
let [testId, testValue] = adomikAnalytics.getKeyValues()
expect(testId).to.equal('12345');
expect(testValue).to.equal('1000');
});

describe('when preventTest is on', function () {
beforeEach(function () {
sessionStorage.setItem(window.location.hostname + '_NoAdomikTest', true);
});

it('returns [undefined, undefined]', function () {
let [testId, testValue] = adomikAnalytics.getKeyValues()
expect(testId).to.equal(undefined);
expect(testValue).to.equal(undefined);
});
});
});
});
});
});

0 comments on commit 4b274f4

Please sign in to comment.