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

AirGrid RTD Submodule: Initial Release #7108

Merged
merged 9 commits into from
Aug 2, 2021
152 changes: 152 additions & 0 deletions integrationExamples/gpt/airgridRtdProvider_example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<html>
<head>
<script>
var matchedAudiences = ["sport", "travel"];
window.localStorage.setItem(
"edkt_matched_audience_ids",
JSON.stringify(matchedAudiences)
);
</script>
<script>
var FAILSAFE_TIMEOUT = 2000;

var adUnits = [
{
code: "test-div",
mediaTypes: {
banner: {
sizes: [
[300, 250],
[300, 600],
[728, 90],
],
},
},
bids: [
{
bidder: "appnexus",
params: {
placementId: 13144370,
},
},
],
},
];

var pbjs = pbjs || {};
pbjs.que = pbjs.que || [];
</script>
<script src="../../build/dev/prebid.js" async></script>

<script>
var googletag = googletag || {};
var AUCTION_DELAY = 2000;
googletag.cmd = googletag.cmd || [];
googletag.cmd.push(function () {
googletag.pubads().disableInitialLoad();
});

pbjs.que.push(function () {
pbjs.setConfig({
debug: true,
realTimeData: {
auctionDelay: AUCTION_DELAY,
dataProviders: [
{
name: "airgrid",
waitForIt: true,
params: {
apiKey: "key123",
accountId: "sdk",
publisherId: "pub123",
},
},
],
},
});
pbjs.setBidderConfig({
bidders: ["appnexus", "pubmatic"],
config: {
ortb2: {
user: {
ext: {
data: {
registered: true,
interests: ["cars"],
},
},
},
},
},
});

pbjs.addAdUnits(adUnits);
pbjs.requestBids({ bidsBackHandler: sendAdserverRequest });
});

function sendAdserverRequest() {
document.getElementById("airgrid_audiences").innerHTML =
window.localStorage.getItem("edkt_matched_audience_ids");

if (pbjs.adserverRequestSent) return;
pbjs.adserverRequestSent = true;
googletag.cmd.push(function () {
pbjs.que.push(function () {
pbjs.setTargetingForGPTAsync();
googletag.pubads().refresh();
});
});
}

setTimeout(function () {
sendAdserverRequest();
}, FAILSAFE_TIMEOUT);
</script>

<script>
(function () {
var gads = document.createElement("script");
gads.async = true;
gads.type = "text/javascript";
var useSSL = "https:" == document.location.protocol;
gads.src =
(useSSL ? "https:" : "http:") +
"//www.googletagservices.com/tag/js/gpt.js";
var node = document.getElementsByTagName("script")[0];
node.parentNode.insertBefore(gads, node);
})();
</script>

<script>
googletag.cmd.push(function () {
googletag
.defineSlot(
"/112115922/FL_PB_MedRect",
[
[300, 250],
[300, 600],
],
"test-div"
)
.addService(googletag.pubads());
googletag.pubads().enableSingleRequest();
googletag.enableServices();
});
</script>
</head>

<body>
<h2>AirGrid RTD Prebid</h2>

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

AirGrid Audiences:
<div id="airgrid_audiences"></div>
</body>
</html>
138 changes: 138 additions & 0 deletions modules/airgridRtdProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* This module adds the AirGrid provider to the real time data module
* The {@link module:modules/realTimeData} module is required
* The module will fetch real-time audience data from AirGrid
* @module modules/airgridRtdProvider
* @requires module:modules/realTimeData
*/
import {config} from '../src/config.js';
import {submodule} from '../src/hook.js';
import {mergeDeep, isPlainObject, deepSetValue, deepAccess} from '../src/utils.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {getStorageManager} from '../src/storageManager.js';

const MODULE_NAME = 'realTimeData';
const SUBMODULE_NAME = 'airgrid';
const AG_TCF_ID = 782;
export const AG_AUDIENCE_IDS_KEY = 'edkt_matched_audience_ids'

export const storage = getStorageManager(AG_TCF_ID, SUBMODULE_NAME);

/**
* Attach script tag to DOM
* @param {Object} rtdConfig
* @return {void}
*/
export function attachScriptTagToDOM(rtdConfig) {
var edktInitializor = window.edktInitializor = window.edktInitializor || {};
if (!edktInitializor.invoked) {
edktInitializor.invoked = true;
edktInitializor.accountId = rtdConfig.params.accountId;
edktInitializor.publisherId = rtdConfig.params.publisherId;
edktInitializor.apiKey = rtdConfig.params.apiKey;
edktInitializor.load = function(e) {
var p = e || 'sdk';
var n = document.createElement('script');
n.type = 'text/javascript';
n.async = true;
n.src = 'https://cdn.edkt.io/' + p + '/edgekit.min.js';
document.getElementsByTagName('head')[0].appendChild(n);
};
edktInitializor.load(edktInitializor.accountId);
}
}

/**
* Fetch audiences from localStorage
* @return {Array}
*/
export function getMatchedAudiencesFromStorage() {
const audiences = storage.getDataFromLocalStorage(AG_AUDIENCE_IDS_KEY);
if (!audiences) return []
try {
return JSON.parse(audiences);
} catch (e) {
return [];
}
}

/**
* Mutates the adUnits object
* @param {Object} adUnits
* @param {Array} audiences
* @return {void}
*/
function setAudiencesToAppNexusAdUnits(adUnits, audiences) {
adUnits.forEach((adUnit) => {
adUnit.bids.forEach((bid) => {
if (bid.bidder && bid.bidder === 'appnexus') {
deepSetValue(bid, 'params.keywords.perid', audiences || []);
}
})
})
}

/**
* Pass audience data to configured bidders, using ORTB2
* @param {Object} rtdConfig
* @param {Array} audiences
* @return {void}
*/
export function setAudiencesUsingBidderOrtb2(rtdConfig, audiences) {
const bidders = deepAccess(rtdConfig, 'params.bidders');
if (!bidders || bidders.length === 0) return;
const allBiddersConfig = config.getBidderConfig();
const agOrtb2 = {}
deepSetValue(agOrtb2, 'ortb2.user.ext.data.airgrid', audiences || []);

bidders.forEach((bidder) => {
let bidderConfig = {};
if (isPlainObject(allBiddersConfig[bidder])) {
bidderConfig = allBiddersConfig[bidder];
}
config.setBidderConfig({
bidders: [bidder],
config: mergeDeep(bidderConfig, agOrtb2)
});
});
}

/**
* Module init
* @param {Object} rtdConfig
* @param {Object} userConsent
* @return {boolean}
*/
function init(rtdConfig, userConsent) {
attachScriptTagToDOM(rtdConfig);
return true;
}

/**
* Real-time data retrieval from AirGrid
* @param {Object} reqBidsConfigObj
* @param {function} onDone
* @param {Object} rtdConfig
* @param {Object} userConsent
* @return {void}
*/
export function passAudiencesToBidders(bidConfig, onDone, rtdConfig, userConsent) {
msm0504 marked this conversation as resolved.
Show resolved Hide resolved
const adUnits = bidConfig.adUnits || getGlobal().adUnits;
const audiences = getMatchedAudiencesFromStorage();
if (audiences.length > 0) {
setAudiencesUsingBidderOrtb2(rtdConfig, audiences);
if (adUnits) {
setAudiencesToAppNexusAdUnits(adUnits, audiences);
}
}
onDone();
};

/** @type {RtdSubmodule} */
export const airgridSubmodule = {
name: SUBMODULE_NAME,
init: init,
getBidRequestData: passAudiencesToBidders
};

submodule(MODULE_NAME, airgridSubmodule);
95 changes: 95 additions & 0 deletions modules/airgridRtdProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
layout: page_v2
title: AirGrid RTD SubModule
description: Client-side, cookieless and privacy-first audiences.
page_type: module
module_type: rtd
module_code : example
enable_download : true
sidebarType : 1
---

# AirGrid

AirGrid is a privacy-first, cookie-less audience platform. Designed to help publishers increase inventory yield,
whilst providing audience signal to buyers in the bid request, without exposing raw user level data to any party.

This real-time data module provides quality first-party data, contextual data, site-level data and more that is
injected into bid request objects destined for different bidders in order to optimize targeting.

## Usage

Compile the Halo RTD module into your Prebid build:

`gulp build --modules=rtdModule,airgridRtdProvider,appnexusBidAdapter`

Add the AirGrid RTD provider to your Prebid config. In this example we will configure publisher 1234 to retrieve segments from Audigent. See the "Parameter Descriptions" below for more detailed information of the configuration parameters.

```js
pbjs.setConfig(
...
realTimeData: {
auctionDelay: 1000,
dataProviders: [
{
name: 'airgrid',
waitForIt: true,
params: {
// These are unique values for each account.
apiKey: 'apiKey',
accountId: 'accountId',
publisherId: 'publisherId',
bidders: ['appnexus', 'pubmatic']
}
}
]
}
...
}
```

### Parameter Descriptions

| Name |Type | Description | Notes |
| :------------ | :------------ | :------------ |:------------ |
| name | `String` | RTD sub module name | Always 'airgrid' |
| waitForIt | `Boolean` | Wether to delay auction for module response | Optional. Defaults to false |
| params.apiKey | `Boolean` | Publisher partner specific API key | Required |
| params.accountId | `String` | Publisher partner specific account ID | Required |
| params.publisherId | `String` | Publisher partner specific publisher ID | Required |
| params.bidders | `Array` | Bidders with which to share segment information | Optional |

_Note: Although the module supports passing segment data to any bidder using the ORTB2 spec, there is no way for this to be currently monetised. Please reach out to support, to discuss using bidders other than Xandr/AppNexus._

If you do not have your own `apiKey`, `accountId` & `publisherId` please reach out to [support@airgrid.io](mailto:support@airgrid.io)

## Testing

To view an example of the on page setup required:

```bash
gulp serve-fast --modules=rtdModule,airgridRtdProvider,appnexusBidAdapter
```

Then in your browser access:

```
http://localhost:9999/integrationExamples/gpt/airgridRtdProvider_example.html
```

Run the unit tests, just on the AirGrid RTD module test file:

```bash
gulp test --file "test/spec/modules/airgridRtdProvider_spec.js"
```

## Support

If you require further assistance or are interested in discussing the module functionality please reach out to:
- [hello@airgrid.io](mailto:hello@airgrid.io) for general questions.
- [support@airgrid.io](mailto:support@airgrid.io) for technical questions.

You are also able to find more examples and other integration routes on the [AirGrid docs site](docs.airgrid.io).

Happy Coding! 😊
The AirGrid Team.
Loading