Skip to content

Commit

Permalink
Support billable events (prebid#10148)
Browse files Browse the repository at this point in the history
  • Loading branch information
johanbrandmetrics authored and Santiago Carabone committed Aug 22, 2023
1 parent 38533f2 commit 2d52d55
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 11 deletions.
38 changes: 36 additions & 2 deletions modules/brandmetricsRtdProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@
* @requires module:modules/realTimeData
*/
import {submodule} from '../src/hook.js';
import {deepAccess, deepSetValue, logError, mergeDeep} from '../src/utils.js';
import {deepAccess, deepSetValue, logError, mergeDeep, generateUUID} from '../src/utils.js';
import {loadExternalScript} from '../src/adloader.js';
import * as events from '../src/events.js';
import CONSTANTS from '../src/constants.json';

const MODULE_NAME = 'brandmetrics'
const MODULE_CODE = MODULE_NAME
const RECEIVED_EVENTS = []
const GVL_ID = 422
const TCF_PURPOSES = [1, 7]

let billableEventsInitialized = false

function init (config, userConsent) {
const hasConsent = checkConsent(userConsent)

if (hasConsent) {
const moduleConfig = getMergedConfig(config)
initializeBrandmetrics(moduleConfig.params.scriptId)
initializeBillableEvents()
}
return hasConsent
}
Expand Down Expand Up @@ -82,7 +87,6 @@ function processBrandmetricsEvents (reqBidsConfigObj, moduleConfig, callback) {
if (RECEIVED_EVENTS.length > 0) {
callBidTargeting(RECEIVED_EVENTS[RECEIVED_EVENTS.length - 1])
} else {
window._brandmetrics = window._brandmetrics || []
window._brandmetrics.push({
cmd: '_addeventlistener',
val: {
Expand Down Expand Up @@ -120,6 +124,8 @@ function setBidderTargeting (reqBidsConfigObj, moduleConfig, key, val) {
* @param {string} scriptId - The script- id provided by brandmetrics or brandmetrics partner
*/
function initializeBrandmetrics(scriptId) {
window._brandmetrics = window._brandmetrics || []

if (scriptId) {
const path = 'https://cdn.brandmetrics.com/survey/script/'
const file = scriptId + '.js'
Expand All @@ -129,6 +135,34 @@ function initializeBrandmetrics(scriptId) {
}
}

/**
* Hook in to brandmetrics creative_in_view- event and emit billable- event for creatives measured by brandmetrics.
*/
function initializeBillableEvents() {
if (!billableEventsInitialized) {
window._brandmetrics.push({
cmd: '_addeventlistener',
val: {
event: 'creative_in_view',
handler: (ev) => {
if (ev.source && ev.source.type === 'pbj') {
const bid = ev.source.data;
events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, {
vendor: 'brandmetrics',
type: 'creative_in_view',
measurementId: ev.mid,
billingId: generateUUID(),
auctionId: bid.auctionId,
transactionId: bid.transactionId,
});
}
},
}
})
billableEventsInitialized = true
}
}

/**
* Merges a provided config with default values
* @param {Object} customConfig
Expand Down
19 changes: 17 additions & 2 deletions modules/brandmetricsRtdProvider.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ Enable the Brandmetrics RTD in your Prebid configuration, using the below format
pbjs.setConfig({
...,
realTimeData: {
auctionDelay: 500, // auction delay
auctionDelay: 500,
dataProviders: [{
name: 'brandmetrics',
waitForIt: true // should be true if there's an `auctionDelay`,
waitForIt: true,
params: {
scriptId: '00000000-0000-0000-0000-000000000000',
bidders: ['ozone']
Expand All @@ -29,6 +29,7 @@ pbjs.setConfig({
...
})
```
The scriptId- parameter is provided by brandmetrics or a brandmetrics partner.

## Parameters
| Name | Type | Description | Default |
Expand All @@ -38,3 +39,17 @@ pbjs.setConfig({
| params | Object | | - |
| params.bidders | String[] | An array of bidders which should receive targeting keys. | `[]` |
| params.scriptId | String | A script- id GUID if the brandmetrics- script should be initialized. | `undefined` |

## Billable events
The module emits a billable event for creatives that are measured by brandmetrics and are considered in- view.

```javascript
{
vendor: 'brandmetrics',
type: 'creative_in_view',
measurementId: string, // UUID, brandmetrics measurement id
billingId: string, // UUID, unique billing id
auctionId: string, // Prebid auction id
transactionId: string, //Prebid transaction id
}
```
76 changes: 69 additions & 7 deletions test/spec/modules/brandmetricsRtdProvider_spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as brandmetricsRTD from '../../../modules/brandmetricsRtdProvider.js';
import {config} from 'src/config.js';
import * as events from '../../../src/events';
import * as sinon from 'sinon';

const VALID_CONFIG = {
name: 'brandmetrics',
Expand Down Expand Up @@ -77,14 +79,16 @@ function mockSurveyLoaded(surveyConf) {
});
}

function scriptTagExists(url) {
const tags = document.getElementsByTagName('script');
for (let i = 0; i < tags.length; i++) {
if (tags[i].src === url) {
return true;
function mockCreativeInView(creativeInViewConf) {
const commands = window._brandmetrics || [];
commands.forEach(command => {
if (command.cmd === '_addeventlistener') {
const conf = command.val;
if (conf.event === 'creative_in_view') {
conf.handler(creativeInViewConf);
}
}
}
return false;
})
}

describe('BrandmetricsRTD module', () => {
Expand Down Expand Up @@ -188,4 +192,62 @@ describe('getBidRequestData', () => {
expect(bidderOrtb2[exp].user.ext.data.brandmetrics_survey).to.equal('mockMeasurementId')
})
});

describe('billable events', () => {
let sandbox;
let eventsEmitSpy;

before(() => {
sandbox = sinon.sandbox.create();
eventsEmitSpy = sandbox.spy(events, ['emit']);
});

beforeEach(() => {
eventsEmitSpy.resetHistory();
})

afterEach(() => {
sandbox.restore();
});

it('should emit billable event from prebid events', () => {
const expectedEvent = {
vendor: 'brandmetrics',
type: 'creative_in_view',
measurementId: 'mockMeasurementId',
auctionId: 'mockAuctionId',
transactionId: 'mockTransactionId'
};

mockCreativeInView({
mid: expectedEvent.measurementId,
source: {
type: 'pbj',
data: {
auctionId: expectedEvent.auctionId,
transactionId: expectedEvent.transactionId
},
}
});

expect(eventsEmitSpy.callCount).to.equal(1);

const event = eventsEmitSpy.getCalls()[0].args[1];
delete event['billingId'];

expect(event).to.deep.equal(expectedEvent);
});

it('should not emit billable event from non prebid- sources', () => {
mockCreativeInView({
mid: 'mockMeasurementId',
source: {
type: 'gpt',
data: {},
}
});

expect(eventsEmitSpy.callCount).to.equal(0);
});
});
});

0 comments on commit 2d52d55

Please sign in to comment.