Skip to content

Commit

Permalink
support multiple providers
Browse files Browse the repository at this point in the history
  • Loading branch information
omerDotan committed Sep 18, 2019
1 parent 1a80b14 commit 3b85815
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
* @property {string} siteKey
* @property {string} pubKey
* @property {string} url
* @property {string} keyName
* @property {?string} keyName
*/

import {config} from '../../src/config.js';
import * as utils from '../../src/utils';
import {submodule} from '../../src/hook';
import {config} from '../src/config.js';
import * as utils from '../src/utils';
import {submodule} from '../src/hook';

/** @type {string} */
const MODULE_NAME = 'realTimeData';
Expand Down Expand Up @@ -47,6 +47,7 @@ export function addBrowsiTag(bptUrl) {
*/
function collectData() {
const win = window.top;
const doc = win.document;
let historicalData = null;
try {
historicalData = JSON.parse(utils.getDataFromLocalStorage('__brtd'))
Expand All @@ -59,6 +60,7 @@ function collectData() {
sk: _moduleParams.siteKey,
sw: (win.screen && win.screen.width) || -1,
sh: (win.screen && win.screen.height) || -1,
url: encodeURIComponent(`${doc.location.protocol}//${doc.location.host}${doc.location.pathname}`)
},
...(historicalData && historicalData.pi ? {pi: historicalData.pi} : {}),
...(historicalData && historicalData.pv ? {pv: historicalData.pv} : {}),
Expand All @@ -76,13 +78,14 @@ function collectData() {
*/
function sendDataToModule(adUnits) {
return _waitForData
.then((_predictions) => {
if (!_predictions) {
resolve({})
.then((_predictionsData) => {
const _predictions = _predictionsData.p;
if (!_predictions || !Object.keys(_predictions).length) {
return ({})
}
const slots = getAllSlots();
if (!slots) {
resolve({})
return ({})
}
let dataToResolve = adUnits.reduce((rp, cau) => {
const adUnitCode = cau && cau.code;
Expand All @@ -94,13 +97,13 @@ function sendDataToModule(adUnits) {
if (!isIdMatchingAdUnit(adUnitCode, slots, predictionData.w)) {
return rp;
}
rp[adUnitCode] = getKVObject(predictionData.p);
rp[adUnitCode] = getKVObject(predictionData.p, _predictionsData.kn);
}
return rp;
}, {});
return (dataToResolve);
})
.catch(() => {
.catch((e) => {
return ({});
});
}
Expand All @@ -115,12 +118,13 @@ function getAllSlots() {
/**
* get prediction and return valid object for key value set
* @param {number} p
* @param {string?} keyName
* @return {Object} key:value
*/
function getKVObject(p) {
function getKVObject(p, keyName) {
const prValue = p < 0 ? 'NA' : (Math.floor(p * 10) / 10).toFixed(2);
let prObject = {};
prObject[(_moduleParams['keyName'].toString())] = prValue.toString();
prObject[((_moduleParams['keyName'] || keyName).toString())] = prValue.toString();
return prObject;
}
/**
Expand Down Expand Up @@ -149,7 +153,7 @@ function getPredictionsFromServer(url) {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
try {
var data = JSON.parse(xmlhttp.responseText);
_resolvePromise(data.p);
_resolvePromise({p: data.p, kn: data.kn});
addBrowsiTag(data.u);
} catch (err) {
utils.logError('unable to parse data');
Expand All @@ -158,12 +162,12 @@ function getPredictionsFromServer(url) {
};
xmlhttp.onloadend = function() {
if (xmlhttp.status === 404) {
_resolvePromise(false);
_resolvePromise({});
utils.logError('unable to get prediction data');
}
};
xmlhttp.open('GET', url, true);
xmlhttp.onerror = function() { _resolvePromise(false) };
xmlhttp.onerror = function() { _resolvePromise({}) };
xmlhttp.send();
}

Expand All @@ -173,8 +177,8 @@ function getPredictionsFromServer(url) {
* @return {string}
*/
function serialize(obj) {
var str = [];
for (var p in obj) {
let str = [];
for (let p in obj) {
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
}
Expand All @@ -200,9 +204,13 @@ export const browsiSubmodule = {

export function init(config) {
const confListener = config.getConfig(MODULE_NAME, ({realTimeData}) => {
_moduleParams = realTimeData.params || {};
if (_moduleParams.siteKey && _moduleParams.pubKey && _moduleParams.url && _moduleParams.keyName &&
realTimeData.name && realTimeData.name.toLowerCase() === 'browsi') {
try {
_moduleParams = realTimeData.dataProviders && realTimeData.dataProviders.filter(
pr => pr.name && pr.name.toLowerCase() === 'browsi')[0].params;
} catch (e) {
_moduleParams = {};
}
if (_moduleParams.siteKey && _moduleParams.pubKey && _moduleParams.url) {
confListener();
collectData();
} else {
Expand Down
88 changes: 48 additions & 40 deletions modules/rtdModules/index.js → modules/rtdModule/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@

/**
* @property
* @summary timeout
* @name ModuleConfig#timeout
* @summary auction delay
* @name ModuleConfig#auctionDelay
* @type {number}
*/

Expand All @@ -47,13 +47,6 @@
* @type {Object}
*/

/**
* @property
* @summary primary ad server only
* @name ModuleConfig#primary_only
* @type {boolean}
*/

import {getGlobal} from '../../src/prebidGlobal';
import {config} from '../../src/config.js';
import {targeting} from '../../src/targeting';
Expand All @@ -64,8 +57,6 @@ import * as utils from '../../src/utils';
const MODULE_NAME = 'realTimeData';
/** @type {RtdSubmodule[]} */
let subModules = [];
/** @type {RtdSubmodule | null} */
let _subModule = null;
/** @type {ModuleConfig} */
let _moduleConfig;

Expand All @@ -76,33 +67,17 @@ let _moduleConfig;
export function attachRealTimeDataProvider(submodule) {
subModules.push(submodule);
}
/**
* get registered sub module
* @returns {RtdSubmodule}
*/
function getSubModule() {
if (!_moduleConfig.name) {
return null;
}
const subModule = subModules.filter(m => m.name === _moduleConfig.name)[0] || null;
if (!subModule) {
throw new Error('unable to use real time data module without provider');
}
return subModules.filter(m => m.name === _moduleConfig.name)[0] || null;
}

export function init(config) {
const confListener = config.getConfig(MODULE_NAME, ({realTimeData}) => {
if (!realTimeData.name || typeof (realTimeData.auctionDelay) == 'undefined') {
if (!realTimeData.dataProviders || typeof (realTimeData.auctionDelay) == 'undefined') {
utils.logError('missing parameters for real time module');
return;
}
confListener(); // unsubscribe config listener
_moduleConfig = realTimeData;
// get submodule
_subModule = getSubModule();
// delay bidding process only if primary ad server only is false
if (_moduleConfig['primary_only']) {
// delay bidding process only if auctionDelay > 0
if (!_moduleConfig.auctionDelay > 0) {
getHook('bidsBackCallback').before(setTargetsAfterRequestBids);
} else {
getGlobal().requestBids.before(requestBidsHook);
Expand All @@ -115,17 +90,18 @@ export function init(config) {
* @returns {Promise} promise race - will return submodule config or false if time out
*/
function getProviderData(adUnits) {
const promises = subModules.map(sm => sm.getData(adUnits));

// promise for timeout
const timeOutPromise = new Promise((resolve) => {
setTimeout(() => {
resolve(false);
resolve({});
}, _moduleConfig.auctionDelay)
});

return Promise.race([
timeOutPromise,
_subModule.getData(adUnits)
]);
return Promise.all(promises.map(p => {
return Promise.race([p, timeOutPromise]);
}));
}

/**
Expand All @@ -136,14 +112,43 @@ function getProviderData(adUnits) {
*/
export function setTargetsAfterRequestBids(next, adUnits) {
getProviderData(adUnits).then(data => {
if (data && Object.keys(data).length) { // utils.isEmpty
setDataForPrimaryAdServer(data);
if (data && Object.keys(data).length) {
const _mergedData = deepMerge(data);
if (Object.keys(_mergedData).length) {
setDataForPrimaryAdServer(_mergedData);
}
}
next(adUnits);
}
);
}

/**
* deep merge array of objects
* @param {array} arr - objects array
* @return {Object} merged object
*/
export function deepMerge(arr) {
if (!arr.length) {
return {};
}
return arr.reduce((merged, obj) => {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (!merged.hasOwnProperty(key)) merged[key] = obj[key];
else {
// duplicate key - merge values
const dp = obj[key];
for (let dk in dp) {
if (dp.hasOwnProperty(dk)) merged[key][dk] = dp[dk];
}
}
}
}
return merged;
}, {});
}

/**
* run hook before bids request
* get data from provider and set key values to primary ad server & bidders
Expand All @@ -153,10 +158,13 @@ export function setTargetsAfterRequestBids(next, adUnits) {
export function requestBidsHook(fn, reqBidsConfigObj) {
getProviderData(reqBidsConfigObj.adUnits || getGlobal().adUnits).then(data => {
if (data && Object.keys(data).length) {
setDataForPrimaryAdServer(data);
addIdDataToAdUnitBids(reqBidsConfigObj.adUnits || getGlobal().adUnits, data);
const _mergedData = deepMerge(data);
if (Object.keys(_mergedData).length) {
setDataForPrimaryAdServer(_mergedData);
addIdDataToAdUnitBids(reqBidsConfigObj.adUnits || getGlobal().adUnits, _mergedData);
}
}
return fn.call(this, reqBidsConfigObj.adUnits);
return fn.call(this, reqBidsConfigObj);
});
}

Expand Down
File renamed without changes.
32 changes: 32 additions & 0 deletions modules/rtdModule/realTimeData.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Real Time Data Configuration Example

Example showing config using `browsi` sub module
```
pbjs.setConfig({
"realTimeData": {
"auctionDelay": 1000,
dataProviders[{
"name": "browsi",
"params": {
"url": "testUrl.com",
"siteKey": "testKey",
"pubKey": "testPub",
"keyName":"bv"
}
}]
}
});
```

Example showing real time data object received form `browsi` real time data provider
```
{
"slotPlacementId":{
"key":"value",
"key2":"value"
},
"slotBPlacementId":{
"dataKey":"dataValue",
}
}
```
30 changes: 0 additions & 30 deletions modules/rtdModules/realTimeData.md

This file was deleted.

Loading

0 comments on commit 3b85815

Please sign in to comment.