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

Azerion Edge RTD Module: Initial release #11134

Merged
merged 2 commits into from
Mar 5, 2024
Merged
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
91 changes: 91 additions & 0 deletions integrationExamples/gpt/azerionedgeRtdProvider_example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<html>
<head>
<meta name="keywords" content="football basketball rugby tenis" />
<script async src="../../build/dev/prebid.js" async></script>
<script
async
src="https://www.googletagservices.com/tag/js/gpt.js"
></script>

<script>
const FAILSAFE_TIMEOUT = 1000;
const PREBID_TIMEOUT = 1000;
const TEST_DIV = "test-div";
const TEST_SIZES = [[300, 250]];
const TEST_OUTPUT = "azerion-segments";
const STORAGE_KEY = "ht-pa-v1-a";
</script>

<script>
var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];
googletag.cmd.push(function () {
googletag
.defineSlot("/19968336/header-bid-tag-0", TEST_SIZES, TEST_DIV)
.addService(googletag.pubads());
googletag.pubads().disableInitialLoad();
googletag.pubads().enableSingleRequest();
googletag.enableServices();
});
</script>

<script>
var pbjs = pbjs || {};
pbjs.que = pbjs.que || [];
pbjs.que.push(function () {
pbjs.setConfig({
debug: true,
realTimeData: {
dataProviders: [
{
name: "azerionedge",
waitForIt: true,
params: { bidders: ["appnexus"] },
},
],
},
});
pbjs.setBidderConfig({ bidders: ["appnexus"], config: {} });
pbjs.addAdUnits([
{
code: TEST_DIV,
mediaTypes: { banner: { sizes: TEST_SIZES } },
bids: [{ bidder: "appnexus", params: { placementId: 13144370 } }],
},
]);
pbjs.requestBids({
bidsBackHandler: sendAdserverRequest,
timeout: PREBID_TIMEOUT,
});
});

function sendAdserverRequest() {
const output = document.getElementById(TEST_OUTPUT);
output.innerHTML = window.localStorage.getItem(STORAGE_KEY);

if (pbjs.adserverRequestSent) return;
pbjs.adserverRequestSent = true;
googletag.cmd.push(function () {
pbjs.que.push(function () {
pbjs.setTargetingForGPTAsync();
googletag.pubads().refresh();
});
});
}
setTimeout(sendAdserverRequest, FAILSAFE_TIMEOUT);
</script>
</head>

<body>
<h2>Azerion Edge RTD</h2>

<div id="test-div">
<script>
googletag.cmd.push(() => googletag.display(TEST_DIV));
</script>
</div>

Segments:
<div id="azerion-segments"></div>
</body>
</html>
1 change: 1 addition & 0 deletions modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"airgridRtdProvider",
"akamaiDapRtdProvider",
"arcspanRtdProvider",
"azerionedgeRtdProvider",
"blueconicRtdProvider",
"brandmetricsRtdProvider",
"browsiRtdProvider",
Expand Down
143 changes: 143 additions & 0 deletions modules/azerionedgeRtdProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/**
* This module adds the Azerion provider to the real time data module of prebid.
*
* The {@link module:modules/realTimeData} module is required
* @module modules/azerionedgeRtdProvider
* @requires module:modules/realTimeData
*/
import { submodule } from '../src/hook.js';
import { mergeDeep } from '../src/utils.js';
import { getStorageManager } from '../src/storageManager.js';
import { loadExternalScript } from '../src/adloader.js';
import { MODULE_TYPE_RTD } from '../src/activities/modules.js';

/**
* @typedef {import('./rtdModule/index.js').RtdSubmodule} RtdSubmodule
*/

const REAL_TIME_MODULE = 'realTimeData';
const SUBREAL_TIME_MODULE = 'azerionedge';
export const STORAGE_KEY = 'ht-pa-v1-a';

export const storage = getStorageManager({
moduleType: MODULE_TYPE_RTD,
moduleName: SUBREAL_TIME_MODULE,
});

/**
* Get script url to load
*
* @param {Object} config
*
* @return {String}
*/
function getScriptURL(config) {
const VERSION = 'v1';
const key = config.params?.key;
const publisherPath = key ? `${key}/` : '';
return `https://edge.hyth.io/js/${VERSION}/${publisherPath}azerion-edge.min.js`;
}

/**
* Attach script tag to DOM
*
* @param {Object} config
*
* @return {void}
*/
export function attachScript(config) {
const script = getScriptURL(config);
loadExternalScript(script, SUBREAL_TIME_MODULE, () => {
if (typeof window.azerionPublisherAudiences === 'function') {
window.azerionPublisherAudiences(config.params?.process || {});
}
});
}

/**
* Fetch audiences info from localStorage.
*
* @return {Array} Audience ids.
*/
export function getAudiences() {
try {
const data = storage.getDataFromLocalStorage(STORAGE_KEY);
return JSON.parse(data).map(({ id }) => id);
} catch (_) {
return [];
}
}

/**
* Pass audience data to configured bidders, using ORTB2
*
* @param {Object} reqBidsConfigObj
* @param {Object} config
* @param {Array} audiences
*
* @return {void}
*/
export function setAudiencesToBidders(reqBidsConfigObj, config, audiences) {
const defaultBidders = ['improvedigital'];
const bidders = config.params?.bidders || defaultBidders;
bidders.forEach((bidderCode) =>
mergeDeep(reqBidsConfigObj.ortb2Fragments.bidder, {
[bidderCode]: {
user: {
data: [
{
name: 'azerionedge',
ext: { segtax: 4 },
segment: audiences.map((id) => ({ id })),
},
],
},
},
})
);
}

/**
* Module initialisation.
*
* @param {Object} config
* @param {Object} userConsent
*
* @return {boolean}
*/
function init(config, userConsent) {
attachScript(config);
return true;
}

/**
* Real-time user audiences retrieval
*
* @param {Object} reqBidsConfigObj
* @param {function} callback
* @param {Object} config
* @param {Object} userConsent
*
* @return {void}
*/
export function getBidRequestData(
reqBidsConfigObj,
callback,
config,
userConsent
) {
const audiences = getAudiences();
if (audiences.length > 0) {
setAudiencesToBidders(reqBidsConfigObj, config, audiences);
}
callback();
}

/** @type {RtdSubmodule} */
export const azerionedgeSubmodule = {
name: SUBREAL_TIME_MODULE,
init: init,
getBidRequestData: getBidRequestData,
};

submodule(REAL_TIME_MODULE, azerionedgeSubmodule);
112 changes: 112 additions & 0 deletions modules/azerionedgeRtdProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
layout: page_v2
title: azerion edge RTD Provider
display_name: Azerion Edge RTD Provider
description: Client-side contextual cookieless audiences.
page_type: module
module_type: rtd
module_code: azerionedgeRtdProvider
enable_download: true
vendor_specific: true
sidebarType: 1
---

# Azerion Edge RTD Provider

Client-side contextual cookieless audiences.

Azerion Edge RTD module helps publishers to capture users' interest
audiences on their site, and attach these into the bid request.

Maintainer: [azerion.com](https://www.azerion.com/)

{:.no_toc}

- TOC
{:toc}

## Integration

Compile the Azerion Edge RTD module (`azerionedgeRtdProvider`) into your Prebid build,
along with the parent RTD Module (`rtdModule`):

```bash
gulp build --modules=rtdModule,azerionedgeRtdProvider
```

Set configuration via `pbjs.setConfig`.

```js
pbjs.setConfig(
...
realTimeData: {
auctionDelay: 1000,
dataProviders: [
{
name: 'azerionedge',
waitForIt: true,
params: {
key: '',
bidders: ['improvedigital'],
process: {}
}
}
]
}
...
}
```

### Parameter Description

{: .table .table-bordered .table-striped }
| Name | Type | Description | Notes |
| :--- | :------- | :------------------ | :--------------- |
| name | `String` | RTD sub module name | Always "azerionedge" |
| waitForIt | `Boolean` | Required to ensure that the auction is delayed for the module to respond. | Optional. Defaults to false but recommended to true. |
| params.key | `String` | Publisher partner specific key | Optional |
| params.bidders | `Array` | Bidders with which to share segment information | Optional. Defaults to "improvedigital". |
| params.process | `Object` | Configuration for the Azerion Edge script. | Optional. Defaults to `{}`. |

## Context

As all data collection is on behalf of the publisher and based on the consent the publisher has
received from the user, this module does not require a TCF vendor configuration. Consent is
provided to the module when the user gives the relevant permissions on the publisher website.

As Prebid.js utilizes TCF vendor consent for the RTD module to load, the module needs to be labeled
within the Vendor Exceptions.

### Instructions

If the Prebid GDPR enforcement is enabled, the module should be labeled
as exception, as shown below:

```js
[
{
purpose: 'storage',
enforcePurpose: true,
enforceVendor: true,
vendorExceptions: ["azerionedge"]
},
...
]
```

## Testing

To view an example:

```bash
gulp serve-fast --modules=rtdModule,azerionedgeRtdProvider
```

Access [http://localhost:9999/integrationExamples/gpt/azerionedgeRtdProvider_example.html](http://localhost:9999/integrationExamples/gpt/azerionedgeRtdProvider_example.html)
in your browser.

Run the unit tests:

```bash
npm test -- --file "test/spec/modules/azerionedgeRtdProvider_spec.js"
```
3 changes: 2 additions & 1 deletion src/adloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const _approvedLoadExternalJSList = [
'hadron',
'medianet',
'improvedigital',
'azerionedge',
'aaxBlockmeter',
'confiant',
'arcspan',
Expand All @@ -33,7 +34,7 @@ const _approvedLoadExternalJSList = [
'contxtful',
'id5',
'lucead',
]
];

/**
* Loads external javascript. Can only be used if external JS is approved by Prebid. See https://github.com/prebid/prebid-js-external-js-template#policy
Expand Down
Loading