Skip to content

Commit

Permalink
Submodule system using hooks (prebid#3924)
Browse files Browse the repository at this point in the history
* prebid-core only contains src and a few node_modules

* add back removed neverBundle to webpack build

* add module and submodule hooks

* allow vargs for submodules for flexibility

* fix jsdoc type syntax

* updated id5 userid submodule for submodule bundle size duplication fix

* add id5 userid submodule to .submodules

* fix opt out logic

* spelling fix to comment

* update to automatically include 'pubcommon' and 'unifiedid' (uncomment to optional submodule for prebid3.0)

* additional update to automatically include 'pubcommon' and 'unifiedid'

* additional update to automatically include 'pubcommon' and 'unifiedid'

* merged differences from master

* adpod changes to support submodules

* fix --modules argument with .json to work correctly with submodules

* fix to remove included and duplicated submodules
  • Loading branch information
snapwich authored and Alex committed Aug 1, 2019
1 parent 14d2127 commit 40676f4
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 78 deletions.
15 changes: 14 additions & 1 deletion gulpHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ const MANIFEST = 'package.json';
const through = require('through2');
const _ = require('lodash');
const gutil = require('gulp-util');
const submodules = require('./modules/.submodules.json');

const MODULE_PATH = './modules';
const BUILD_PATH = './build/dist';
const DEV_PATH = './build/dev';
const ANALYTICS_PATH = '../analytics';


// get only subdirectories that contain package.json with 'main' property
function isModuleDirectory(filePath) {
try {
Expand Down Expand Up @@ -39,7 +41,9 @@ module.exports = {
.replace(/\/>/g, '\\/>');
},
getArgModules() {
var modules = (argv.modules || '').split(',').filter(module => !!module);
var modules = (argv.modules || '')
.split(',')
.filter(module => !!module);

try {
if (modules.length === 1 && path.extname(modules[0]).toLowerCase() === '.json') {
Expand All @@ -56,6 +60,15 @@ module.exports = {
});
}

Object.keys(submodules).forEach(parentModule => {
if (
!modules.includes(parentModule) &&
modules.some(module => submodules[parentModule].includes(module))
) {
modules.unshift(parentModule);
}
});

return modules;
},
getModules: _.memoize(function(externalModules) {
Expand Down
9 changes: 9 additions & 0 deletions modules/.submodules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"userId": [
"digiTrustIdSystem",
"id5IdSystem"
],
"adpod": [
"freeWheelAdserverVideo"
]
}
31 changes: 27 additions & 4 deletions modules/adpod.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ import * as utils from '../src/utils';
import { addBidToAuction, doCallbacksIfTimedout, AUCTION_IN_PROGRESS, callPrebidCache } from '../src/auction';
import { checkAdUnitSetup } from '../src/prebid';
import { checkVideoBidSetup } from '../src/video';
import { setupBeforeHookFnOnce } from '../src/hook';
import { setupBeforeHookFnOnce, module } from '../src/hook';
import { store } from '../src/videoCache';
import { config } from '../src/config';
import { ADPOD } from '../src/mediaTypes';
import Set from 'core-js/library/fn/set';
import find from 'core-js/library/fn/array/find';

const from = require('core-js/library/fn/array/from');

export const TARGETING_KEY_PB_CAT_DUR = 'hb_pb_cat_dur';
export const TARGETING_KEY_CACHE_ID = 'hb_cache_id'
const TARGETING_KEY_PB_CAT_DUR = 'hb_pb_cat_dur';
const TARGETING_KEY_CACHE_ID = 'hb_cache_id';

let queueTimeDelay = 50;
let queueSizeLimit = 5;
Expand Down Expand Up @@ -385,12 +386,13 @@ config.getConfig('adpod', config => adpodSetConfig(config.adpod));
/**
* This function initializes the adpod module's hooks. This is called by the corresponding adserver video module.
*/
export function initAdpodHooks() {
function initAdpodHooks() {
setupBeforeHookFnOnce(callPrebidCache, callPrebidCacheHook);
setupBeforeHookFnOnce(checkAdUnitSetup, checkAdUnitSetupHook);
setupBeforeHookFnOnce(checkVideoBidSetup, checkVideoBidSetupHook);
}

initAdpodHooks()
/**
*
* @param {Array[Object]} bids list of 'winning' bids that need to be cached
Expand Down Expand Up @@ -428,3 +430,24 @@ export function sortByPricePerSecond(a, b) {
}
return 0;
}

const sharedMethods = {
TARGETING_KEY_PB_CAT_DUR: TARGETING_KEY_PB_CAT_DUR,
TARGETING_KEY_CACHE_ID: TARGETING_KEY_CACHE_ID,
'sortByPricePerSecond': sortByPricePerSecond,
'callPrebidCacheAfterAuction': callPrebidCacheAfterAuction
}
Object.freeze(sharedMethods);

module('adpod', function shareAdpodUtilities(...args) {
if (!utils.isPlainObject(args[0])) {
utils.logError('Adpod module needs plain object to share methods with submodule');
return;
}
function addMethods(object, func) {
for (let name in func) {
object[name] = func[name];
}
}
addMethods(args[0], sharedMethods);
});
4 changes: 2 additions & 2 deletions modules/digiTrustIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// import { config } from 'src/config';
import * as utils from '../src/utils'
import { ajax } from '../src/ajax';
import { attachIdSystem } from '../modules/userId';
import { submodule } from '../src/hook';
// import { getGlobal } from 'src/prebidGlobal';

/**
Expand Down Expand Up @@ -336,4 +336,4 @@ export const digiTrustIdSubmodule = {
_testInit: surfaceTestHook
};

attachIdSystem(digiTrustIdSubmodule);
submodule('userId', digiTrustIdSubmodule);
23 changes: 12 additions & 11 deletions modules/freeWheelAdserverVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { auctionManager } from '../src/auctionManager';
import { groupBy, deepAccess, logError, compareOn } from '../src/utils';
import { config } from '../src/config';
import { ADPOD } from '../src/mediaTypes';
import { initAdpodHooks, TARGETING_KEY_PB_CAT_DUR, TARGETING_KEY_CACHE_ID, callPrebidCacheAfterAuction, sortByPricePerSecond } from './adpod';
import { getHook } from '../src/hook';
import { getHook, submodule } from '../src/hook';

export function notifyTranslationModule(fn) {
fn.call(this, 'freewheel');
Expand Down Expand Up @@ -37,7 +36,7 @@ export function getTargeting({codes, callback} = {}) {

let bids = getBidsForAdpod(bidsReceived, adPodAdUnits);
bids = (competiveExclusionEnabled || deferCachingEnabled) ? getExclusiveBids(bids) : bids;
bids.sort(sortByPricePerSecond);
bids.sort(adpodUtils.sortByPricePerSecond);

let targeting = {};
if (deferCachingEnabled === false) {
Expand All @@ -50,13 +49,13 @@ export function getTargeting({codes, callback} = {}) {
.forEach((bid, index, arr) => {
if (bid.video.durationBucket <= adPodDurationSeconds) {
adPodTargeting.push({
[TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR]
[adpodUtils.TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[adpodUtils.TARGETING_KEY_PB_CAT_DUR]
});
adPodDurationSeconds -= bid.video.durationBucket;
}
if (index === arr.length - 1 && adPodTargeting.length > 0) {
adPodTargeting.push({
[TARGETING_KEY_CACHE_ID]: bid.adserverTargeting[TARGETING_KEY_CACHE_ID]
[adpodUtils.TARGETING_KEY_CACHE_ID]: bid.adserverTargeting[adpodUtils.TARGETING_KEY_CACHE_ID]
});
}
});
Expand All @@ -79,7 +78,7 @@ export function getTargeting({codes, callback} = {}) {
});
});

callPrebidCacheAfterAuction(bidsToCache, function(error, bidsSuccessfullyCached) {
adpodUtils.callPrebidCacheAfterAuction(bidsToCache, function(error, bidsSuccessfullyCached) {
if (error) {
callback(error, null);
} else {
Expand All @@ -89,12 +88,12 @@ export function getTargeting({codes, callback} = {}) {

groupedBids[adUnitCode].forEach((bid, index, arr) => {
adPodTargeting.push({
[TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR]
[adpodUtils.TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[adpodUtils.TARGETING_KEY_PB_CAT_DUR]
});

if (index === arr.length - 1 && adPodTargeting.length > 0) {
adPodTargeting.push({
[TARGETING_KEY_CACHE_ID]: bid.adserverTargeting[TARGETING_KEY_CACHE_ID]
[adpodUtils.TARGETING_KEY_CACHE_ID]: bid.adserverTargeting[adpodUtils.TARGETING_KEY_CACHE_ID]
});
}
});
Expand Down Expand Up @@ -126,8 +125,8 @@ function getAdPodAdUnits(codes) {
*/
function getExclusiveBids(bidsReceived) {
let bids = bidsReceived
.map((bid) => Object.assign({}, bid, {[TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[TARGETING_KEY_PB_CAT_DUR]}));
bids = groupBy(bids, TARGETING_KEY_PB_CAT_DUR);
.map((bid) => Object.assign({}, bid, {[adpodUtils.TARGETING_KEY_PB_CAT_DUR]: bid.adserverTargeting[adpodUtils.TARGETING_KEY_PB_CAT_DUR]}));
bids = groupBy(bids, adpodUtils.TARGETING_KEY_PB_CAT_DUR);
let filteredBids = [];
Object.keys(bids).forEach((targetingKey) => {
bids[targetingKey].sort(compareOn('responseTimestamp'));
Expand All @@ -148,7 +147,9 @@ function getBidsForAdpod(bidsReceived, adPodAdUnits) {
.filter((bid) => adUnitCodes.indexOf(bid.adUnitCode) != -1 && (bid.video && bid.video.context === ADPOD))
}

initAdpodHooks();
registerVideoSupport('freewheel', {
getTargeting: getTargeting
});

export const adpodUtils = {};
submodule('adpod', adpodUtils);
10 changes: 5 additions & 5 deletions modules/id5IdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* @requires module:modules/userId
*/

import * as utils from '../src/utils.js'
import {ajax} from '../src/ajax.js';
import {isGDPRApplicable, attachIdSystem} from './userId.js';
import * as utils from '../src/utils'
import {ajax} from '../src/ajax';
import {submodule} from '../src/hook';

/** @type {Submodule} */
export const id5IdSubmodule = {
Expand Down Expand Up @@ -37,7 +37,7 @@ export const id5IdSubmodule = {
utils.logError(`User ID - ID5 submodule requires partner to be defined as a number`);
return;
}
const hasGdpr = isGDPRApplicable(consentData) ? 1 : 0;
const hasGdpr = (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) ? 1 : 0;
const gdprConsentString = hasGdpr ? consentData.consentString : '';
const url = `https://id5-sync.com/g/v1/${configParams.partner}.json?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}`;

Expand All @@ -57,4 +57,4 @@ export const id5IdSubmodule = {
}
};

attachIdSystem(id5IdSubmodule);
submodule('userId', id5IdSubmodule);
36 changes: 15 additions & 21 deletions modules/userId.js → modules/userId/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
* @name Submodule#getId
* @param {SubmoduleParams} configParams
* @param {ConsentData} consentData
* @return {(Object|function} id data or a callback, the callback is called on the auction end event
* @return {(Object|function)} id data or a callback, the callback is called on the auction end event
*/

/**
* @function
* @summary decode a stored value for passing to bid requests
* @name Submodule#decode
* @param {Object|string} value
* @return {(Object|undefined}
* @return {(Object|undefined)}
*/

/**
Expand Down Expand Up @@ -68,14 +68,15 @@
*/

import find from 'core-js/library/fn/array/find';
import {config} from '../src/config.js';
import events from '../src/events.js';
import * as utils from '../src/utils.js';
import {getGlobal} from '../src/prebidGlobal.js';
import {gdprDataHandler} from '../src/adapterManager.js';
import {config} from '../../src/config';
import events from '../../src/events';
import * as utils from '../../src/utils';
import {getGlobal} from '../../src/prebidGlobal';
import {gdprDataHandler} from '../../src/adapterManager';
import CONSTANTS from '../../src/constants.json';
import {module} from '../../src/hook';
import {unifiedIdSubmodule} from './unifiedIdSystem.js';
import {pubCommonIdSubmodule} from './pubCommonIdSystem.js';
import CONSTANTS from '../src/constants.json';

const MODULE_NAME = 'User ID';
const COOKIE = 'cookie';
Expand Down Expand Up @@ -158,22 +159,13 @@ function getStoredValue(storage) {
return storedValue;
}

/**
* test if consent module is present, and if GDPR applies
* @param {ConsentData} consentData
* @returns {boolean}
*/
export function isGDPRApplicable(consentData) {
return consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies;
}

/**
* test if consent module is present, applies, and is valid for local storage or cookies (purpose 1)
* @param {ConsentData} consentData
* @returns {boolean}
*/
export function hasGDPRConsent(consentData) {
if (isGDPRApplicable(consentData)) {
function hasGDPRConsent(consentData) {
if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) {
if (!consentData.consentString) {
return false;
}
Expand Down Expand Up @@ -331,7 +323,7 @@ function getValidSubmoduleConfigs(configRegistry, submoduleRegistry, activeStora
if (!config || utils.isEmptyStr(config.name)) {
return carry;
}
// alidate storage config contains 'type' and 'name' properties with non-empty string values
// Validate storage config contains 'type' and 'name' properties with non-empty string values
// 'type' must be a value currently enabled in the browser
if (config.storage &&
!utils.isEmptyStr(config.storage.type) &&
Expand Down Expand Up @@ -409,7 +401,7 @@ export function init(config) {
return;
}
// _pubcid_optout is checked for compatiblility with pubCommonId
if (validStorageTypes.indexOf(LOCAL_STORAGE) !== -1 && (localStorage.getItem('_pbjs_id_optout') && localStorage.getItem('_pubcid_optout'))) {
if (validStorageTypes.indexOf(LOCAL_STORAGE) !== -1 && (localStorage.getItem('_pbjs_id_optout') || localStorage.getItem('_pubcid_optout'))) {
utils.logInfo(`${MODULE_NAME} - opt-out localStorage found, exit module`);
return;
}
Expand All @@ -431,3 +423,5 @@ init(config);
// add submodules after init has been called
attachIdSystem(pubCommonIdSubmodule);
attachIdSystem(unifiedIdSubmodule);

module('userId', attachIdSystem);
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @requires module:modules/userId
*/

import * as utils from '../src/utils.js'
import * as utils from '../../src/utils';

/** @type {Submodule} */
export const pubCommonIdSubmodule = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* @requires module:modules/userId
*/

import * as utils from '../src/utils.js'
import {ajax} from '../src/ajax.js';
import * as utils from '../../src/utils'
import {ajax} from '../../src/ajax';

/** @type {Submodule} */
export const unifiedIdSubmodule = {
Expand Down
24 changes: 2 additions & 22 deletions modules/userId.md → modules/userId/userId.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
Example showing `cookie` storage for user id data for both submodules
```
pbjs.setConfig({
usersync: {
userSync: {
userIds: [{
name: "unifiedId",
params: {
partner: "prebid",
url: "http://match.adsrvr.org/track/rid?ttd_pid=prebid&fmt=json"
url: "//match.adsrvr.org/track/rid?ttd_pid=prebid&fmt=json"
},
storage: {
type: "cookie",
Expand All @@ -28,26 +28,6 @@ pbjs.setConfig({
});
```

Example showing `cookie` storage for user id data for id5 submodule
```
pbjs.setConfig({
usersync: {
userIds: [{
name: "id5Id",
params: {
partner: 173 // @TODO: Set your real ID5 partner ID here for production, please ask for one [email protected]
},
storage: {
type: "cookie",
name: "id5id",
expires: 90
}
}],
syncDelay: 5000
}
});
```

Example showing `localStorage` for user id data for both submodules
```
pbjs.setConfig({
Expand Down
Loading

0 comments on commit 40676f4

Please sign in to comment.