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

Add Geoedge RTD provider submodule #5869

Merged
merged 14 commits into from
Nov 24, 2020
3 changes: 2 additions & 1 deletion modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"browsiRtdProvider",
"audigentRtdProvider",
"jwplayerRtdProvider",
"reconciliationRtdProvider"
"reconciliationRtdProvider",
"geoedgeRtdProvider"
]
}
213 changes: 213 additions & 0 deletions modules/geoedgeRtdProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
/**
* This module adds geoedge provider to the real time data module
* The {@link module:modules/realTimeData} module is required
* The module will fetch creative wrapper from geoedge server
* The module will place geoedge RUM client on bid responses markup
* @module modules/geoedgeProvider
* @requires module:modules/realTimeData
*/

/**
* @typedef {Object} ModuleParams
* @property {string} key
* @property {?Object} bidders
* @property {?boolean} wap
* @property {?string} keyName
*/

import { submodule } from '../src/hook.js';
import { ajax } from '../src/ajax.js';
import { generateUUID, insertElement, isEmpty, logError } from '../src/utils.js';

/** @type {string} */
const SUBMODULE_NAME = 'geoedge';
/** @type {string} */
export const WRAPPER_URL = 'https://wrappers.geoedge.be/wrapper.html';
/** @type {string} */
/* eslint-disable no-template-curly-in-string */
export const HTML_PLACEHOLDER = '${creative}';
/** @type {string} */
const PV_ID = generateUUID();
/** @type {string} */
const HOST_NAME = 'https://rumcdn.geoedge.be';
/** @type {string} */
const FILE_NAME = 'grumi.js';
/** @type {function} */
export let getClientUrl = (key) => `${HOST_NAME}/${key}/${FILE_NAME}`;
/** @type {string} */
export let wrapper
/** @type {boolean} */;
let wrapperReady;
/** @type {boolean} */;
let preloaded;

/**
* fetches the creative wrapper
* @param {function} sucess - success callback
*/
export function fetchWrapper(success) {
if (wrapperReady) {
return success(wrapper);
}
ajax(WRAPPER_URL, success);
}

/**
* sets the wrapper and calls preload client
* @param {string} responseText
*/
export function setWrapper(responseText) {
wrapperReady = true;
wrapper = responseText;
}

/**
* preloads the client
* @param {string} key
*/
export function preloadClient(key) {
let link = document.createElement('link');
link.rel = 'preload';
link.as = 'script';
link.href = getClientUrl(key);
link.onload = () => { preloaded = true };
insertElement(link);
}

/**
* creates identity function for string replace without special replacement patterns
* @param {string} str
* @return {function}
*/
function replacer(str) {
return function () {
return str;
}
}

export function wrapHtml(wrapper, html) {
return wrapper.replace(HTML_PLACEHOLDER, replacer(html));
}

/**
* generate macros dictionary from bid response
* @param {Object} bid
* @param {string} key
* @return {Object}
*/
function getMacros(bid, key) {
return {
'${key}': key,
'%%ADUNIT%%': bid.adUnitCode,
'%%WIDTH%%': bid.width,
'%%HEIGHT%%': bid.height,
'%%PATTERN:hb_adid%%': bid.adId,
'%%PATTERN:hb_bidder%%': bid.bidderCode,
'%_isHb!': true,
'%_hbcid!': bid.creativeId || '',
'%%PATTERN:hb_pb%%': bid.pbHg,
'%%SITE%%': location.hostname,
'%_pimp%': PV_ID
};
}

/**
* replace macro placeholders in a string with values from a dictionary
* @param {string} wrapper
* @param {Object} macros
* @return {string}
*/
function replaceMacros(wrapper, macros) {
var re = new RegExp('\\' + Object.keys(macros).join('|'), 'gi');

return wrapper.replace(re, function(matched) {
return macros[matched];
});
}

/**
* build final creative html with creative wrapper
* @param {Object} bid
* @param {string} wrapper
* @param {string} html
* @return {string}
*/
function buildHtml(bid, wrapper, html, key) {
let macros = getMacros(bid, key);
wrapper = replaceMacros(wrapper, macros);
return wrapHtml(wrapper, html);
}

/**
* muatates the bid ad property
* @param {Object} bid
* @param {string} ad
*/
function mutateBid(bid, ad) {
bid.ad = ad;
}

/**
* wraps a bid object with the creative wrapper
* @param {Object} bid
* @param {string} key
*/
export function wrapBidResponse(bid, key) {
let wrapped = buildHtml(bid, wrapper, bid.ad, key);
mutateBid(bid, wrapped);
}

/**
* checks if bidder's bids should be monitored
* @param {string} bidder
* @return {boolean}
*/
function isSupportedBidder(bidder, paramsBidders) {
return isEmpty(paramsBidders) || paramsBidders[bidder] === true;
}

/**
* checks if bid should be monitored
* @param {Object} bid
* @return {boolean}
*/
function shouldWrap(bid, params) {
let supportedBidder = isSupportedBidder(bid.bidderCode, params.bidders);
let donePreload = params.wap ? preloaded : true;
return wrapperReady && supportedBidder && donePreload;
}

function conditionallyWrap(bidResponse, config, userConsent) {
let params = config.params;
if (shouldWrap(bidResponse, params)) {
wrapBidResponse(bidResponse, params.key);
}
}

function init(config, userConsent) {
let params = config.params;
if (!params || !params.key) {
logError('missing key for geoedge RTD module provider');
return false;
}
preloadClient(params.key);
return true;
}

/** @type {RtdSubmodule} */
export const geoedgeSubmodule = {
/**
* used to link submodule with realTimeData
* @type {string}
*/
name: SUBMODULE_NAME,
init,
onBidResponseEvent: conditionallyWrap
};

export function beforeInit() {
fetchWrapper(setWrapper);
submodule('realTimeData', geoedgeSubmodule);
}

beforeInit();
67 changes: 67 additions & 0 deletions modules/geoedgeRtdProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
## Overview

Module Name: Geoedge Rtd provider
Module Type: Rtd Provider
Maintainer: guy.books@geoedge.com

The Geoedge Realtime module let pusblishers to block bad ads such as automatic redirects, malware, offensive creatives and landing pages.
To use this module, you'll need to work with [Geoedge](https://www.geoedge.com/publishers-real-time-protection/) to get an account and cutomer key.

## Integration

1) Build the geoedge RTD module into the Prebid.js package with:

```
gulp build --modules=geoedgeRtdProvider,...
```

2) Use `setConfig` to instruct Prebid.js to initilize the geoedge module, as specified below.

## Configuration

This module is configured as part of the `realTimeData.dataProviders` object:

```javascript
pbjs.setConfig({
realTimeData: {
dataProviders: [{
name: 'geoedge',
params: {
key: '123123',
bidders: {
'bidderA': true, // monitor bids form this bidder
'bidderB': false // do not monitor bids form this bidder.
},
wap: true
}
}]
}
});
```

Parameters details:

{: .table .table-bordered .table-striped }
|Name |Type |Description |Notes |
| :------------ | :------------ | :------------ |:------------ |
|name | String | Real time data module name |Required, always 'geoedge' |
|params | Object | | |
|params.key | String | Customer key |Required, contact Geoedge to get your key |
|params.bidders | Object | Bidders to monitor |Optional, list of bidder to include / exclude from monitoring. Omitting this will monitor bids from all bidders. |
|params.wap |Boolean |Wrap after preload |Optional, defaults to `false`. Set to `true` if you want to monitor only after the module has preloaded the monitoring client. |

## Example

To view an integration example:

1) in your cli run:

```
gulp serve --modules=appnexusBidAdapter,geoedgeRtdProvider
```

2) in your browser, navigate to:

```
http://localhost:9999/integrationExamples/gpt/geoedgeRtdProvider_example.html
```
Loading