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

Halo RTD Module: FPD 2.0 Updates & add ID system tests #6505

Merged
merged 16 commits into from
Apr 19, 2021
Merged
30 changes: 3 additions & 27 deletions integrationExamples/gpt/haloRtdProvider_example.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,27 +40,6 @@
googletag.pubads().disableInitialLoad();
});

var appnexusSegmentHandler = function(bid, segments) {
if (!bid.params) {
bid.params = {};
}
if (!bid.params.user) {
bid.params.user = {};
}

if (!Array.isArray(bid.params.user.segments)) {
bid.params.user.segments = [];
}

var appnexusSegments = [];
for (var i = 0; i < segments.length; i++) {
var segment = segments[i];
let appnexusSegment = {'id': segment.id, 'value': segment.value};
appnexusSegments.push(appnexusSegment);
}
bid.params.user.segments = bid.params.user.segments.concat(appnexusSegments);
};

pbjs.que.push(function() {
pbjs.setConfig({
debug: true,
Expand All @@ -71,9 +50,6 @@
name: "halo",
waitForIt: true,
params: {
mapSegments: {
appnexus: appnexusSegmentHandler // pass true to use the builtin handler. here, we will demo overriding the handler with a function
},
segmentCache: false,
requestParams: {
publisherId: 0
Expand All @@ -89,7 +65,7 @@
});

function sendAdserverRequest() {
document.getElementById('audigent_segments').innerHTML = JSON.stringify(adUnits[0].bids[0].params.user.segments);
document.getElementById('audigent_segments').innerHTML = window.localStorage.getItem('auHaloRtd');
document.getElementById('halo_id').innerHTML = testHaloId;

if (pbjs.adserverRequestSent) return;
Expand Down Expand Up @@ -130,7 +106,7 @@
</head>

<body>
<h2>Audigent Segments Prebid</h2>
<h2>Halo RTD Prebid</h2>

<div id='test-div'>
<script>
Expand All @@ -142,7 +118,7 @@ <h2>Audigent Segments Prebid</h2>
<div id='halo_id'>
</div>

Audigent Segments (Appnexus):
Halo Real-Time Data:
<div id='audigent_segments'>
</div>
</body>
Expand Down
44 changes: 27 additions & 17 deletions modules/haloIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
* @requires module:modules/userId
*/

import * as utils from '../src/utils.js';
import {ajax} from '../src/ajax.js';
import {getStorageManager} from '../src/storageManager.js';
import {submodule} from '../src/hook.js';
import * as utils from '../src/utils.js';

const MODULE_NAME = 'haloId';
const AU_GVLID = 561;

export const storage = getStorageManager(AU_GVLID, 'halo');

/** @type {Submodule} */
export const haloIdSubmodule = {
Expand Down Expand Up @@ -37,24 +41,30 @@ export const haloIdSubmodule = {
const url = `https://id.halo.ad.gt/api/v1/pbhid`;

const resp = function (callback) {
const callbacks = {
success: response => {
let responseObj;
if (response) {
try {
responseObj = JSON.parse(response);
} catch (error) {
utils.logError(error);
let haloId = storage.getDataFromLocalStorage('auHaloId');
if (utils.isStr(haloId)) {
const responseObj = {haloId: haloId};
callback(responseObj);
} else {
const callbacks = {
success: response => {
let responseObj;
if (response) {
try {
responseObj = JSON.parse(response);
} catch (error) {
utils.logError(error);
}
}
callback(responseObj);
},
error: error => {
utils.logError(`${MODULE_NAME}: ID fetch encountered an error`, error);
callback();
}
callback(responseObj);
},
error: error => {
utils.logError(`${MODULE_NAME}: ID fetch encountered an error`, error);
callback();
}
};
ajax(url, callbacks, undefined, {method: 'GET'});
};
ajax(url, callbacks, undefined, {method: 'GET'});
}
};
return {callback: resp};
}
Expand Down
171 changes: 81 additions & 90 deletions modules/haloRtdProvider.js
Original file line number Diff line number Diff line change
@@ -1,109 +1,99 @@
/**
* This module adds audigent provider to the real time data module
* This module adds the Audigent Halo provider to the real time data module
* The {@link module:modules/realTimeData} module is required
* The module will fetch segments from audigent server
* @module modules/audigentRtdProvider
* The module will fetch real-time data from Audigent
* @module modules/haloRtdProvider
* @requires module:modules/realTimeData
*/
import {ajax} from '../src/ajax.js';
import {config} from '../src/config.js';
import {getGlobal} from '../src/prebidGlobal.js';
import * as utils from '../src/utils.js';
import {getStorageManager} from '../src/storageManager.js';
import {submodule} from '../src/hook.js';
import {ajax} from '../src/ajax.js';
import { getStorageManager } from '../src/storageManager.js';
import {isFn, isStr, isPlainObject, mergeDeep, logError} from '../src/utils.js';

export const storage = getStorageManager();

/** @type {string} */
const MODULE_NAME = 'realTimeData';
const SUBMODULE_NAME = 'halo';
const AU_GVLID = 561;

export const HALOID_LOCAL_NAME = 'auHaloId';
export const SEG_LOCAL_NAME = '__adgntseg';
export const RTD_LOCAL_NAME = 'auHaloRtd';
export const storage = getStorageManager(AU_GVLID, SUBMODULE_NAME);

/**
* Deep set an object unless value present.
* @param {Object} obj
* @param {String} path
* @param {Object} val
*/
const set = (obj, path, val) => {
const keys = path.split('.');
const lastKey = keys.pop();
const lastObj = keys.reduce((obj, key) => obj[key] = obj[key] || {}, obj);
lastObj[lastKey] = lastObj[lastKey] || val;
};

/** bid adapter format segment augmentation functions */
const segmentMappers = {
appnexus: function(bid, segments) {
set(bid, 'params.user.segments', []);
let appnexusSegments = [];
segments.forEach(segment => {
if (typeof segment.id != 'undefined' && segment.id != null) {
appnexusSegments.push(parseInt(segment.id));
}
})
bid.params.user.segments = bid.params.user.segments.concat(appnexusSegments);
},
generic: function(bid, segments) {
bid.segments = bid.segments || [];
if (Array.isArray(bid.segments)) {
bid.segments = bid.segments.concat(segments);
}
/**
* Lazy merge objects.
* @param {String} target
* @param {String} source
*/
function mergeLazy(target, source) {
if (!isPlainObject(target)) {
target = {};
}
if (!isPlainObject(source)) {
source = {};
}
return mergeDeep(target, source);
}

/**
* decorate adUnits with segment data
* @param {adUnit[]} adUnits
* @param {Object} data
* Param or default.
* @param {String} param
* @param {String} defaultVal
*/
export function addSegmentData(adUnits, segmentData, config) {
adUnits.forEach(adUnit => {
if (adUnit.hasOwnProperty('bids')) {
adUnit.bids.forEach(bid => {
try {
set(bid, 'fpd.user.data', []);
if (Array.isArray(bid.fpd.user.data)) {
bid.fpd.user.data.forEach(fpdData => {
let segments = segmentData[fpdData.id] || segmentData[fpdData.name] || [];
fpdData.segment = (fpdData.segment || []).concat(segments);
});
}
} catch (err) {
utils.logError(err.message);
}
function paramOrDefault(param, defaultVal) {
if (isFn(param)) {
return param();
} else if (isStr(param)) {
return param;
}
return defaultVal;
}

try {
if (config.params.mapSegments && config.params.mapSegments[bid.bidder] && segmentData[bid.bidder]) {
if (typeof config.params.mapSegments[bid.bidder] == 'function') {
config.params.mapSegments[bid.bidder](bid, segmentData[bid.bidder]);
} else if (segmentMappers[bid.bidder]) {
segmentMappers[bid.bidder](bid, segmentData[bid.bidder]);
}
}
} catch (err) {
utils.logError(err.message);
}
});
}
});
/**
* Add real-time data & merge segments.
* @param {Object} bidConfig
* @param {Object} rtd
* @param {Object} rtdConfig
*/
export function addRealTimeData(bidConfig, rtd, rtdConfig) {
let ortb2 = config.getConfig('ortb2') || {};

return adUnits;
if (rtdConfig.params && rtdConfig.params.handleRtd) {
rtdConfig.params.handleRtd(bidConfig, rtd, rtdConfig, config);
} else if (rtd.ortb2) {
config.setConfig({ortb2: mergeLazy(ortb2, rtd.ortb2)});
}
}

/**
* segment retrieval from audigent's backends
* Real-time data retrieval from Audigent
* @param {Object} reqBidsConfigObj
* @param {function} onDone
* @param {Object} config
* @param {Object} rtdConfig
* @param {Object} userConsent
*/
export function getSegments(reqBidsConfigObj, onDone, config, userConsent) {
const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits;

if (config.params.segmentCache) {
let jsonData = storage.getDataFromLocalStorage(SEG_LOCAL_NAME);
export function getRealTimeData(bidConfig, onDone, rtdConfig, userConsent) {
if (rtdConfig && isPlainObject(rtdConfig.params) && rtdConfig.params.segmentCache) {
let jsonData = storage.getDataFromLocalStorage(RTD_LOCAL_NAME);

if (jsonData) {
let data = JSON.parse(jsonData);
antlauzon marked this conversation as resolved.
Show resolved Hide resolved

if (data.audigent_segments) {
addSegmentData(adUnits, data.audigent_segments, config);
if (data.rtd) {
addRealTimeData(bidConfig, data.rtd, rtdConfig);
onDone();
return;
}
Expand All @@ -113,53 +103,54 @@ export function getSegments(reqBidsConfigObj, onDone, config, userConsent) {
const userIds = (getGlobal()).getUserIds();

let haloId = storage.getDataFromLocalStorage(HALOID_LOCAL_NAME);
if (haloId) {
if (isStr(haloId)) {
userIds.haloId = haloId;
getSegmentsAsync(adUnits, onDone, config, userConsent, userIds);
getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent, userIds);
} else {
var script = document.createElement('script')
script.type = 'text/javascript';

script.onload = function() {
userIds.haloId = storage.getDataFromLocalStorage(HALOID_LOCAL_NAME);
getSegmentsAsync(adUnits, onDone, config, userConsent, userIds);
window.pubHaloCb = (haloId) => {
userIds.haloId = haloId;
getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent, userIds);
}

script.src = 'https://id.halo.ad.gt/api/v1/haloid';
const haloIdUrl = rtdConfig.params && rtdConfig.params.haloIdUrl;
script.src = paramOrDefault(haloIdUrl, 'https://id.halo.ad.gt/api/v1/haloid');
document.getElementsByTagName('head')[0].appendChild(script);
}
}

/**
* async segment retrieval from audigent's backends
* @param {adUnit[]} adUnits
* Async rtd retrieval from Audigent
* @param {function} onDone
* @param {Object} config
* @param {Object} rtdConfig
* @param {Object} userConsent
* @param {Object} userIds
*/
export function getSegmentsAsync(adUnits, onDone, config, userConsent, userIds) {
export function getRealTimeDataAsync(bidConfig, onDone, rtdConfig, userConsent, userIds) {
let reqParams = {};
if (typeof config == 'object' && config != null) {
set(config, 'params.requestParams', {});
reqParams = config.params.requestParams;

if (isPlainObject(rtdConfig)) {
set(rtdConfig, 'params.requestParams.ortb2', config.getConfig('ortb2'));
reqParams = rtdConfig.params.requestParams;
}

const url = `https://seg.halo.ad.gt/api/v1/rtb_segments`;
const url = `https://seg.halo.ad.gt/api/v1/rtd`;
ajax(url, {
success: function (response, req) {
if (req.status === 200) {
try {
const data = JSON.parse(response);
if (data && data.audigent_segments) {
addSegmentData(adUnits, data.audigent_segments, config);
if (data && data.rtd) {
addRealTimeData(bidConfig, data.rtd, rtdConfig);
onDone();
storage.setDataInLocalStorage(SEG_LOCAL_NAME, JSON.stringify(data));
storage.setDataInLocalStorage(RTD_LOCAL_NAME, JSON.stringify(data));
} else {
onDone();
}
} catch (err) {
utils.logError('unable to parse audigent segment data');
logError('unable to parse audigent segment data');
onDone();
}
} else if (req.status === 204) {
Expand All @@ -169,7 +160,7 @@ export function getSegmentsAsync(adUnits, onDone, config, userConsent, userIds)
},
error: function () {
onDone();
utils.logError('unable to get audigent segment data');
logError('unable to get audigent segment data');
}
},
JSON.stringify({'userIds': userIds, 'config': reqParams}),
Expand All @@ -178,7 +169,7 @@ export function getSegmentsAsync(adUnits, onDone, config, userConsent, userIds)
}

/**
* module init
* Module init
* @param {Object} provider
* @param {Objkect} userConsent
* @return {boolean}
Expand All @@ -190,7 +181,7 @@ function init(provider, userConsent) {
/** @type {RtdSubmodule} */
export const haloSubmodule = {
name: SUBMODULE_NAME,
getBidRequestData: getSegments,
getBidRequestData: getRealTimeData,
init: init
};

Expand Down
Loading