Skip to content

Commit

Permalink
Adloox real time data module
Browse files Browse the repository at this point in the history
  • Loading branch information
jimdigriz committed Feb 13, 2021
1 parent 4ddcffa commit eddc700
Show file tree
Hide file tree
Showing 5 changed files with 462 additions and 4 deletions.
8 changes: 8 additions & 0 deletions integrationExamples/gpt/adloox.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@
googletag.cmd.push(function() {
pbjs.que.push(function() {
pbjs.setConfig({
realTimeData: {
auctionDelay: AUCTION_DELAY,
dataProviders: [
{
name: 'adloox'
}
]
},
instreamTracking: {
enabled: true
},
Expand Down
11 changes: 7 additions & 4 deletions modules/adlooxAnalyticsAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ When tracking video you have two options:

To view an [example of an Adloox integration](../integrationExamples/gpt/adloox.html):

gulp serve --nolint --notest --modules=gptPreAuction,categoryTranslation,dfpAdServerVideo,instreamTracking,rubiconBidAdapter,spotxBidAdapter,adlooxAnalyticsAdapter,adlooxAdServerVideo
gulp serve --nolint --notest --modules=gptPreAuction,categoryTranslation,dfpAdServerVideo,rtdModule,instreamTracking,rubiconBidAdapter,spotxBidAdapter,adlooxAnalyticsAdapter,adlooxAdServerVideo,adlooxRtdProvider

**N.B.** `categoryTranslation` is required by `dfpAdServerVideo` that otherwise causes a JavaScript console warning

Expand All @@ -44,6 +44,8 @@ Now point your browser at: http://localhost:9999/integrationExamples/gpt/adloox.

The example is published publically at: https://storage.googleapis.com/adloox-ads-js-test/prebid.html?pbjs_debug=true

**N.B.** this will show a [CORS error](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors) for the request `https://p.adlooxtracking.com/q?...` that is safe to ignore on the public example page; it is related to the [RTD integration](./adlooxRtdProvider.md) which requires pre-registration of your sites

It is recommended you use [Google Chrome's 'Local Overrides' located in the Developer Tools panel](https://www.trysmudford.com/blog/chrome-local-overrides/) to explore the example without the inconvience of having to run your own web server.

#### Pre-built `prebid.js`
Expand All @@ -61,7 +63,7 @@ You should be able to use this during the QA process of your own internal testin
The main Prebid.js documentation is a bit opaque on this but you can use the following to test only Adloox's modules:

gulp lint
gulp test-coverage --file 'test/spec/modules/adloox{AnalyticsAdapter,AdServerVideo}_spec.js'
gulp test-coverage --file 'test/spec/modules/adloox{AnalyticsAdapter,AdServerVideo,RtdProvider}_spec.js'
gulp view-coverage

# Integration
Expand Down Expand Up @@ -103,8 +105,8 @@ For example, you have a number of reporting breakdown slots available in the for
platformid: 0,
tagid: 0,
params: {
id1: function(b) { return b.adUnitCode },
id2: '%%pbAdSlot%%',
id1: function(b) { return b.adUnitCode }, // do not change when using the Adloox RTD Provider
id2: '%%pbAdSlot%%', // do not change when using the Adloox RTD Provider
id3: function(b) { return b.bidder },
id4: function(b) { return b.adId },
id5: function(b) { return b.dealId },
Expand All @@ -124,6 +126,7 @@ For example, you have a number of reporting breakdown slots available in the for
The following macros are available

* `%%pbAdSlot%%`: [Prebid Ad Slot](https://docs.prebid.org/features/pbAdSlot.html) if set, otherwise returns [`AdUnit.code`](https://docs.prebid.org/dev-docs/adunit-reference.html)
* it is recommended you read the [Prebid Ad Slot section in the Adloox RTD Provider documentation](./adlooxRtdProvider.md#prebid-ad-slot)

### Functions

Expand Down
171 changes: 171 additions & 0 deletions modules/adlooxRtdProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/**
* This module adds the Adloox provider to the real time data module
* This module adds the [Adloox]{@link https://www.adloox.com/} provider to the real time data module
* The {@link module:modules/realTimeData} module is required
* The module will inject Adloox's prebid API JS
* The module will fetch segments from Adloox's server
* @module modules/adlooxRtdProvider
* @requires module:modules/realTimeData
* @requires module:modules/adlooxAnalyticsAdapter
*/

/* eslint prebid/validate-imports: "warn" */

import { command as analyticsCommand, COMMAND } from './adlooxAnalyticsAdapter.js';
import { config as _config } from '../src/config.js';
import { submodule } from '../src/hook.js';
import includes from 'core-js-pure/features/array/includes.js';
import { getGlobal } from '../src/prebidGlobal.js';
import * as utils from '../src/utils.js';

const MODULE = 'adlooxRtdProvider';

let CONFIGURED = false;

const URL_JS = 'https://p.adlooxtracking.com/gpt/a.js';

function init(config, userConsent) {
utils.logInfo(MODULE, 'init', config, userConsent);

if (!utils.isPlainObject(config)) {
utils.logError(MODULE, 'missing config');
return false;
}
if (config.params === undefined) config.params = {};
if (!(utils.isPlainObject(config.params))) {
utils.logError(MODULE, 'invalid params');
return false;
}
if (!(config.params.js === undefined || utils.isStr(config.params.js))) {
utils.logError(MODULE, 'invalid js params value');
return false;
}
// legacy/deprecated configuration code path
if (config.params.params === undefined) {
config.params.params = {};
} else if (!utils.isPlainObject(config.params.params) || !(utils.isInteger(config.params.params.clientid) && utils.isInteger(config.params.params.tagid) && utils.isInteger(config.params.params.platformid))) {
utils.logError(MODULE, 'invalid subsection params block');
return false;
}

window.adloox_pubint = window.adloox_pubint || { cmd: [] };

const script = document.createElement('script');
script.src = config.params.js || URL_JS;
utils.insertElement(script);

function analyticsConfigCallback(data) {
CONFIGURED = true;

const params = utils.mergeDeep({}, config.params.params, data);

window.adloox_pubint.cmd.push(function() {
window.adloox_pubint.init(params);
});
}
if (Object.keys(config.params.params).length) {
utils.logWarn(MODULE, 'legacy/deprecated configuration (please migrate to adlooxAnalyticsAdapter)');
analyticsConfigCallback({});
} else {
analyticsCommand(COMMAND.CONFIG, null, analyticsConfigCallback);
}

return true;
}

function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) {
if (!CONFIGURED) {
utils.logError(MODULE, 'getBidRequestData', 'called before configured, is analytics enabled?');
return;
}

utils.logInfo(MODULE, 'getBidRequestData', reqBidsConfigObj, callback, config, userConsent);

const context = {
set: function(n, x) {
utils.logInfo(MODULE, 'segment', 'context', n, x);
const data = _config.getConfig('fpd.context.data') || {};
delete data[n];
if (x !== undefined) data[n] = x;
_config.setConfig({ fpd: { context: { data: data } } });
}
};
const user = {
set: function(n, x) {
utils.logInfo(MODULE, 'segment', 'user', n, x);
const data = _config.getConfig('fpd.user.data') || {};
delete data[n];
if (x !== undefined) data[n] = x;
_config.setConfig({ fpd: { user: { data: data } } });
}
};
const slots = (reqBidsConfigObj.adUnits || getGlobal().adUnits).map(adUnit => {
return {
id: adUnit.code,
// modules/gptPreAuction.js does not update the AdUnits themselves... (╯°□°)╯ ┻━┻
name: utils.deepAccess(adUnit, 'fpd.context.pbAdSlot') || utils.getGptSlotInfoForAdUnitCode(adUnit.code).gptSlot || adUnit.code,
set: function(n, x) {
utils.logInfo(MODULE, 'segment', 'slot', adUnit.code, n, x);
const data = utils.deepAccess(adUnit, 'fpd.context.data', {});
delete data[n];
if (x !== undefined) data[n] = x;
utils.deepSetValue(adUnit, 'fpd.context.data', data);
}
};
});

window.adloox_pubint.cmd.push(function() {
window.adloox_pubint.seg(context, user, slots, callback);
});
}

function getTargetingData(adUnitArray, config, userConsent) {
utils.logInfo(MODULE, 'getTargetingData', adUnitArray, config, userConsent);

function add(pairs, dest) {
// targeting:getTargetingValues expects strings or arrays
Object.keys(pairs).filter(key => /^adl_/.test(key)).forEach(k => {
let v = pairs[k];
switch (true) {
case utils.isBoolean(v):
if (!v) break;
v = 1;
// falls through
case utils.isNumber(v):
if (!v) break;
v = v.toString();
// falls through
case utils.isStr(v):
if (!v.length) break;
v = [ v ];
// falls through
case utils.isArray(v):
let i = v.length;
if (!i) break;
while (i-- > 0) v[i] = v[i].toString();
dest[k] = v;
// falls through
}
});
}

const data0 = {};
add(_config.getConfig('fpd.context.data') || {}, data0);
add(_config.getConfig('fpd.user.data') || {}, data0);

return getGlobal().adUnits.filter(adUnit => includes(adUnitArray, adUnit.code)).reduce((data, adUnit) => {
data[adUnit.code] = utils.deepClone(data0);
const fpdContextData = utils.deepAccess(adUnit, 'fpd.context.data', {});
add(fpdContextData, data[adUnit.code]);
return data;
}, {});
}

export const subModuleObj = {
name: 'adloox',
init: init,
getBidRequestData: getBidRequestData,
getTargetingData: getTargetingData
};

submodule('realTimeData', subModuleObj);
85 changes: 85 additions & 0 deletions modules/adlooxRtdProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Overview

Module Name: Adloox RTD Provider
Module Type: RTD Provider
Maintainer: [email protected]

# Description

RTD provider for adloox.com. Contact [email protected] for information.

In addition to populating the ad server key-value targeting, fetched segments (prefixed with `adl_...`) will also populate [First Party Data](https://docs.prebid.org/features/firstPartyData.html), some examples of the segments as described by the Adloox 'Google Publisher Tag Targeting Guidelines' and where they are placed are:

* Page/Device segments are placed into `fpd.context.data`, for example:
* **`adl_ivt`:** `fpd.context.data.adl_ivt` is a boolean
* **`adl_ua_old`:** `fpd.context.data.ua_old` is a boolean
* **`adl_ip`:** `fpd.context.data.adl_ip` is an array of strings
* AdUnit segments are placed into `AdUnit.fpd.context.data`, for example:
* **`adl_{dis,vid,aud}`:** `AdUnit.fpd.context.data.adl_{dis,vid,aud}` is an array of integers
* **`adl_atf`:** `AdUnit.fpd.context.data.adl_atf` is a boolean (or `-1` on no measure)

**N.B.** this provider does not offer or utilise any user orientated data

This module adds an HTML `<script>` tag to the page to fetch our JavaScript from `https://p.adlooxtracking.com/gpt/a.js` (~3kiB gzipped) to support this integration.

## Example

To view an example of an Adloox integration look at the example provided in the [Adloox Analytics Adapter documentation](./adlooxAnalyticsAdapter.md#example).

# Integration

To use this, you *must* also integrate the [Adloox Analytics Adapter](./adlooxAnalyticsAdapter.md) as shown below:

pbjs.setConfig({
...

realTimeData: {
auctionDelay: 100, // see below for guidance
dataProviders: [
{
name: 'adloox',
params: {
params: { // optional
thresholds: [ 50, 80 ]
}
}
}
]
},

...
});
pbjs.enableAnalytics({
provider: 'adloox',
options: {
client: 'adlooxtest',
clientid: 127,
platformid: 0,
tagid: 0
}
});

You may optionally pass a subsection `params` in the `params` block to the Adloox RTD Provider, these will be passed through to the segment handler as is and as described by the integration guidelines.

**N.B.** If you pass `params` to the Adloox Analytics Adapter, `id1` (`AdUnit.code`) and `id2` (`%%pbAdSlot%%`) *must* describe a stable identifier otherwise no usable segments will be served and so they *must not* be changed; if `id1` for your inventory could contain a non-stable random number please consult with us before continuing

Though our segment technology is fast (less than 10ms) the time it takes for the users device to connect to our service and fetch the segments may not be. For this reason we recommend setting `auctionDelay` no lower than 100ms and if possible you should explore using user-agent sourced information such as [NetworkInformation.{rtt,downlink,...}](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation) to dynamically tune this for each user.

## Prebid Ad Slot

To create reliable segments, a stable description for slots on your inventory needs to be supplied which is typically solved by using the [Prebid Ad Slot](https://docs.prebid.org/features/pbAdSlot.html).

You may use one of two ways to do achieve this:

* for display inventory [using GPT](https://developers.google.com/publisher-tag/guides/get-started) you may configure Prebid.js to automatically use the [full ad unit path](https://developers.google.com/publisher-tag/reference#googletag.Slot_getAdUnitPath)
1. include the [`gptPreAuction` module](https://docs.prebid.org/dev-docs/modules/gpt-pre-auction.html)
1. wrap both `pbjs.setConfig({...})` and `pbjs.enableAnalytics({...})` with `googletag.cmd.push(function() { ... })`
* set `pbAdSlot` in the [first party data](https://docs.prebid.org/dev-docs/adunit-reference.html#first-party-data) variable `AdUnit.fpd.context.pbAdSlot` for all your ad units

## Timeouts

It is strongly recommended you increase any [failsafe timeout](https://docs.prebid.org/dev-docs/faq.html#when-starting-out-what-should-my-timeouts-be) you use by at least the value you supply to `auctionDelay` above.

Adloox recommends you use the following (based on [examples provided on the Prebid.js website](https://docs.prebid.org/dev-docs/examples/basic-example.html))

FAILSAFE_TIMEOUT = AUCTION_DELAY + (3 * PREBID_TIMEOUT)
Loading

0 comments on commit eddc700

Please sign in to comment.