From 2d65845b6ad07592895324b460f17a6a81a22bba Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Durgeat Date: Wed, 1 May 2019 19:03:50 +0200 Subject: [PATCH 1/8] Extract GDPRApplies in userId.js to make it available to cookie-sync requiring it --- modules/userId.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/userId.js b/modules/userId.js index bddada7ffbc..2796bbe46cf 100644 --- a/modules/userId.js +++ b/modules/userId.js @@ -175,13 +175,22 @@ export function getStoredValue(storage) { return storedValue; } +/** + * test if consent module is present, and if GDPR applies + * @param {Object} consentData + * @returns {boolean} + */ +export function GDPRApplies(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 {Object} consentData * @returns {boolean} */ export function hasGDPRConsent(consentData) { - if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { + if (GDPRApplies(consentData)) { if (!consentData.consentString) { return false; } From b75049ec1560bc9e219e78e02c062f3f965b5dbd Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Durgeat Date: Wed, 1 May 2019 19:04:46 +0200 Subject: [PATCH 2/8] Adding id5 userId submodule, tests and documentation --- integrationExamples/gpt/userId_example.html | 12 +++++++- modules/userId.js | 33 ++++++++++++++++++++- modules/userId.md | 20 +++++++++++++ test/spec/modules/userId_spec.js | 32 +++++++++++--------- 4 files changed, 81 insertions(+), 16 deletions(-) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index d64e22e44c7..febe61628fe 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -140,7 +140,17 @@ name: "unifiedid", expires: 30 }, - + }, { + name: "id5Id", + params: { + partner: 173 // @TODO: Set your real ID5 partner ID here for production, please ask for one contact@id5.io + }, + storage: { + type: "cookie", + name: "id5id", + expires: 90 + }, + }, { name: "pubCommonId", storage: { diff --git a/modules/userId.js b/modules/userId.js index 2796bbe46cf..b26f3f6383e 100644 --- a/modules/userId.js +++ b/modules/userId.js @@ -102,6 +102,37 @@ export const unifiedIdSubmodule = { } }; +// @type {Submodule} +export const id5Submodule = { + name: 'id5Id', + decode(value) { + return (value && typeof value['ID5ID'] === 'string') ? { 'id5id': value['ID5ID'] } : undefined; + }, + getId(submoduleConfigParams, consentData) { + if (!submoduleConfigParams || typeof submoduleConfigParams.partner !== 'number') { + utils.logError(`${MODULE_NAME} - ID5 submodule requires partner to be defined as a number`); + return; + } + const hasGdpr = GDPRApplies(consentData) ? 1 : 0; + const gdprConsentString = hasGdpr ? consentData.consentString : ''; + const url = `https://id5-sync.com/g/v1/${submoduleConfigParams.partner}.json?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}`; + + return function (callback) { + ajax(url, response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + utils.logError(error); + } + } + callback(responseObj); + }, undefined, { method: 'GET' }); + } + } +}; + // @type {Submodule} export const pubCommonIdSubmodule = { name: 'pubCommonId', @@ -435,4 +466,4 @@ export function init (config, enabledSubmodules) { }); } -init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); +init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); diff --git a/modules/userId.md b/modules/userId.md index a873a76674f..b36b9b1007c 100644 --- a/modules/userId.md +++ b/modules/userId.md @@ -28,6 +28,26 @@ 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 contact@id5.io + }, + storage: { + type: "cookie", + name: "id5id", + expires: 90 + } + }], + syncDelay: 5000 + } +}); +``` + Example showing `localStorage` for user id data for both submodules ``` pbjs.setConfig({ diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index c7901106538..c212cda9cda 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -3,6 +3,7 @@ import { syncDelay, pubCommonIdSubmodule, unifiedIdSubmodule, + id5Submodule, requestBidsHook } from 'modules/userId'; import {config} from 'src/config'; @@ -56,7 +57,7 @@ describe('User ID', function() { let pubcid = utils.getCookie('pubcid'); expect(pubcid).to.be.null; // there should be no cookie initially - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); requestBidsHook(config => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); @@ -81,7 +82,7 @@ describe('User ID', function() { let pubcid1; let pubcid2; - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); requestBidsHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); pubcid1 = utils.getCookie('pubcid'); // get first cookie @@ -94,7 +95,7 @@ describe('User ID', function() { }); }); - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); requestBidsHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); @@ -114,7 +115,7 @@ describe('User ID', function() { let adUnits = [createAdUnit()]; let innerAdUnits; - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { syncDelay: 0, @@ -152,13 +153,13 @@ describe('User ID', function() { }); it('fails initialization if opt out cookie exists', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - opt-out cookie found, exit module'); }); it('initializes if no opt out cookie exists', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig() ] } }); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); }); @@ -176,20 +177,20 @@ describe('User ID', function() { }); it('handles config with no usersync object', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with empty usersync object', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: {} }); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { userIds: [{}] @@ -199,7 +200,7 @@ describe('User ID', function() { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { userIds: [{ @@ -215,7 +216,7 @@ describe('User ID', function() { }); it('config with 1 configurations should create 1 submodules', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { syncDelay: 0, @@ -229,7 +230,7 @@ describe('User ID', function() { }); it('config with 2 configurations should result in 2 submodules add', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { syncDelay: 0, @@ -238,14 +239,17 @@ describe('User ID', function() { }, { name: 'unifiedId', storage: { name: 'unifiedid', type: 'cookie' } + }, { + name: 'id5Id', + storage: { name: 'id5id', type: 'cookie' } }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 2 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 3 submodules'); }); it('config syncDelay updates module correctly', function () { - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { syncDelay: 99, From 70239a096b69c0d354415fc69fe99fc57724dcbd Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Durgeat Date: Wed, 1 May 2019 20:08:00 +0200 Subject: [PATCH 3/8] Fixed typo in test name for unifiedid --- test/spec/modules/userId_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index c212cda9cda..530662a343a 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -315,7 +315,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from pubcommonid html5', function(done) { + it('test hook from unifiedid html5', function(done) { // simulate existing browser local storage values localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); localStorage.setItem('unifiedid_alt_exp', ''); From d9d7588d170b238a500887ead3697c200a4bc38c Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Durgeat Date: Wed, 1 May 2019 20:33:47 +0200 Subject: [PATCH 4/8] Adding test to userId for ID5 --- test/spec/modules/userId_spec.js | 35 +++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index 530662a343a..cd9fb0504f7 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -340,17 +340,42 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook when both pubCommonId and unifiedId have data to pass', function(done) { + it('test hook from id5id cookie', function(done) { + // simulate existing browser cookie values + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + + init(config, [id5Submodule]); + config.setConfig({ + usersync: { + syncDelay: 0, + userIds: [createStorageConfig('id5Id', 'id5id', 'cookie')]} + }); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); + }); + }); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook when pubCommonId, unifiedId and id5Id have data to pass', function(done) { utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); utils.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); - init(config, [pubCommonIdSubmodule, unifiedIdSubmodule]); + init(config, [pubCommonIdSubmodule, unifiedIdSubmodule, id5Submodule]); config.setConfig({ usersync: { syncDelay: 0, userIds: [ createStorageConfig('pubCommonId', 'pubcid', 'cookie'), - createStorageConfig('unifiedId', 'unifiedid', 'cookie') + createStorageConfig('unifiedId', 'unifiedid', 'cookie'), + createStorageConfig('id5Id', 'id5id', 'cookie') ]} }); @@ -363,10 +388,14 @@ describe('User ID', function() { // also check that UnifiedId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.tdid'); expect(bid.userId.tdid).to.equal('testunifiedid'); + // also check that ID5 id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); }); }); utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); From 5a006f5a63d9a3259157ca545e23a72ddd422abc Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Durgeat Date: Thu, 9 May 2019 21:02:40 +0200 Subject: [PATCH 5/8] Follow correct naming convention for GDPRApplies: renamed to isGDPRApplicable --- modules/userId.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/userId.js b/modules/userId.js index b26f3f6383e..7559bef2976 100644 --- a/modules/userId.js +++ b/modules/userId.js @@ -113,7 +113,7 @@ export const id5Submodule = { utils.logError(`${MODULE_NAME} - ID5 submodule requires partner to be defined as a number`); return; } - const hasGdpr = GDPRApplies(consentData) ? 1 : 0; + const hasGdpr = isGDPRApplicable(consentData) ? 1 : 0; const gdprConsentString = hasGdpr ? consentData.consentString : ''; const url = `https://id5-sync.com/g/v1/${submoduleConfigParams.partner}.json?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}`; @@ -211,7 +211,7 @@ export function getStoredValue(storage) { * @param {Object} consentData * @returns {boolean} */ -export function GDPRApplies(consentData) { +export function isGDPRApplicable(consentData) { return consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies; } @@ -221,7 +221,7 @@ export function GDPRApplies(consentData) { * @returns {boolean} */ export function hasGDPRConsent(consentData) { - if (GDPRApplies(consentData)) { + if (isGDPRApplicable(consentData)) { if (!consentData.consentString) { return false; } From 02cd85ed58b43c500d9938a685d4eaaf89eadc3b Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Durgeat Date: Mon, 27 May 2019 06:45:13 +0200 Subject: [PATCH 6/8] Refactoring of id5 module following refactoring of userId module. --- modules/id5IdSystem.js | 58 +++++++++++++++++++ modules/userId.js | 15 ++++- modules/userId.md | 4 +- test/spec/modules/userId_spec.js | 95 ++++++++++++++++++++++++-------- 4 files changed, 144 insertions(+), 28 deletions(-) create mode 100644 modules/id5IdSystem.js diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js new file mode 100644 index 00000000000..de4a24fffb3 --- /dev/null +++ b/modules/id5IdSystem.js @@ -0,0 +1,58 @@ +/** + * This module adds ID5 to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/unifiedIdSystem + * @requires module:modules/userId + */ + +import * as utils from '../src/utils.js' +import {ajax} from '../src/ajax.js'; +import {isGDPRApplicable} from './userId.js'; + +/** @type {Submodule} */ +export const id5IdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: 'id5Id', + /** + * decode the stored id value for passing to bid requests + * @function + * @param {{ID5ID:Object}} value + * @returns {{id5id:String}} + */ + decode(value) { + return (value && typeof value['ID5ID'] === 'string') ? { 'id5id': value['ID5ID'] } : undefined; + }, + /** + * performs action to obtain id and return a value in the callback's response argument + * @function + * @param {SubmoduleParams} [configParams] + * @param {ConsentData} [consentData] + * @returns {function(callback:function)} + */ + getId(configParams, consentData) { + if (!configParams || typeof configParams.partner !== 'number') { + utils.logError(`User ID - ID5 submodule requires partner to be defined as a number`); + return; + } + const hasGdpr = isGDPRApplicable(consentData) ? 1 : 0; + const gdprConsentString = hasGdpr ? consentData.consentString : ''; + const url = `https://id5-sync.com/g/v1/${configParams.partner}.json?gdpr=${hasGdpr}&gdpr_consent=${gdprConsentString}`; + + return function (callback) { + ajax(url, response => { + let responseObj; + if (response) { + try { + responseObj = JSON.parse(response); + } catch (error) { + utils.logError(error); + } + } + callback(responseObj); + }, undefined, { method: 'GET' }); + } + } +}; diff --git a/modules/userId.js b/modules/userId.js index ae06dfc4027..f1447995d03 100644 --- a/modules/userId.js +++ b/modules/userId.js @@ -75,6 +75,7 @@ import {getGlobal} from '../src/prebidGlobal.js'; import {gdprDataHandler} from '../src/adapterManager.js'; import {unifiedIdSubmodule} from './unifiedIdSystem.js'; import {pubCommonIdSubmodule} from './pubCommonIdSystem.js'; +import {id5IdSubmodule} from './id5IdSystem.js'; import CONSTANTS from '../src/constants.json'; const MODULE_NAME = 'User ID'; @@ -158,13 +159,22 @@ 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} */ -function hasGDPRConsent(consentData) { - if (consentData && typeof consentData.gdprApplies === 'boolean' && consentData.gdprApplies) { +export function hasGDPRConsent(consentData) { + if (isGDPRApplicable(consentData)) { if (!consentData.consentString) { return false; } @@ -422,3 +432,4 @@ init(config); // add submodules after init has been called attachIdSystem(pubCommonIdSubmodule); attachIdSystem(unifiedIdSubmodule); +attachIdSystem(id5IdSubmodule); diff --git a/modules/userId.md b/modules/userId.md index 2c181848dc9..b36b9b1007c 100644 --- a/modules/userId.md +++ b/modules/userId.md @@ -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: "//match.adsrvr.org/track/rid?ttd_pid=prebid&fmt=json" + url: "http://match.adsrvr.org/track/rid?ttd_pid=prebid&fmt=json" }, storage: { type: "cookie", diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index d0f5e06cdad..60df468a903 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -9,18 +9,20 @@ import {config} from 'src/config'; import * as utils from 'src/utils'; import {unifiedIdSubmodule} from 'modules/unifiedIdSystem'; import {pubCommonIdSubmodule} from 'modules/pubCommonIdSystem'; +import {id5IdSubmodule} from 'modules/id5IdSystem'; let assert = require('chai').assert; let expect = require('chai').expect; const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; describe('User ID', function() { - function getConfigMock(configArr1, configArr2) { + function getConfigMock(configArr1, configArr2, configArr3) { return { userSync: { syncDelay: 0, userIds: [ (configArr1 && configArr1.length === 3) ? getStorageMock.apply(null, configArr1) : null, - (configArr2 && configArr2.length === 3) ? getStorageMock.apply(null, configArr2) : null + (configArr2 && configArr2.length === 3) ? getStorageMock.apply(null, configArr2) : null, + (configArr3 && configArr3.length === 3) ? getStorageMock.apply(null, configArr3) : null ].filter(i => i)} } } @@ -68,7 +70,7 @@ describe('User ID', function() { let pubcid = utils.getCookie('pubcid'); expect(pubcid).to.be.null; // there should be no cookie initially - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); @@ -94,7 +96,7 @@ describe('User ID', function() { let pubcid1; let pubcid2; - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook((config) => { innerAdUnits1 = config.adUnits }, {adUnits: adUnits1}); @@ -108,7 +110,7 @@ describe('User ID', function() { }); }); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); requestBidsHook((config) => { innerAdUnits2 = config.adUnits }, {adUnits: adUnits2}); @@ -129,7 +131,7 @@ describe('User ID', function() { let adUnits = [getAdUnitMock()]; let innerAdUnits; - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid_alt', 'cookie'])); requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); @@ -164,14 +166,14 @@ describe('User ID', function() { }); it('fails initialization if opt out cookie exists', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - opt-out cookie found, exit module'); }); it('initializes if no opt out cookie exists', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); @@ -190,7 +192,7 @@ describe('User ID', function() { }); it('handles config with no usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({}); // usersync is undefined, and no logInfo message for 'User ID - usersync config updated' @@ -198,14 +200,14 @@ describe('User ID', function() { }); it('handles config with empty usersync object', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ usersync: {} }); expect(typeof utils.logInfo.args[0]).to.equal('undefined'); }); it('handles config with usersync and userIds that are empty objs', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ usersync: { @@ -216,7 +218,7 @@ describe('User ID', function() { }); it('handles config with usersync and userIds with empty names or that dont match a submodule.name', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ usersync: { @@ -233,15 +235,15 @@ describe('User ID', function() { }); it('config with 1 configurations should create 1 submodules', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig(getConfigMock(['unifiedId', 'unifiedid', 'cookie'])); expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 1 submodules'); }); - it('config with 2 configurations should result in 2 submodules add', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + it('config with 3 configurations should result in 3 submodules add', function () { + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ usersync: { @@ -251,14 +253,17 @@ describe('User ID', function() { }, { name: 'unifiedId', storage: { name: 'unifiedid', type: 'cookie' } + }, { + name: 'id5Id', + storage: { name: 'id5id', type: 'cookie' } }] } }); - expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 2 submodules'); + expect(utils.logInfo.args[0][0]).to.exist.and.to.equal('User ID - usersync config updated for 3 submodules'); }); it('config syncDelay updates module correctly', function () { - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ usersync: { @@ -322,7 +327,7 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook from pubcommonid html5', function(done) { + it('test hook from unifiedid html5', function(done) { // simulate existing browser local storage values localStorage.setItem('unifiedid_alt', JSON.stringify({'TDID': 'testunifiedid_alt'})); localStorage.setItem('unifiedid_alt_exp', ''); @@ -344,13 +349,36 @@ describe('User ID', function() { }, {adUnits}); }); - it('test hook when both pubCommonId and unifiedId have data to pass', function(done) { + it('test hook from id5id cookies', function(done) { + // simulate existing browser local storage values + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); + + setSubmoduleRegistry([id5IdSubmodule]); + init(config); + config.setConfig(getConfigMock(['id5Id', 'id5id', 'cookie'])); + + requestBidsHook(function() { + adUnits.forEach(unit => { + unit.bids.forEach(bid => { + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); + }); + }); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); + done(); + }, {adUnits}); + }); + + it('test hook when pubCommonId, unifiedId and id5Id have data to pass', function(done) { utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); utils.setCookie('unifiedid', JSON.stringify({'TDID': 'testunifiedid'}), (new Date(Date.now() + 5000).toUTCString())); + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'])); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], + ['unifiedId', 'unifiedid', 'cookie'], + ['id5Id', 'id5id', 'cookie'])); requestBidsHook(function() { adUnits.forEach(unit => { @@ -361,17 +389,22 @@ describe('User ID', function() { // also check that UnifiedId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.tdid'); expect(bid.userId.tdid).to.equal('testunifiedid'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); }); }); utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); - it('test hook when pubCommonId and unifiedId have their modules added before and after init', function(done) { + it('test hook when pubCommonId, unifiedId and id5Id have their modules added before and after init', function(done) { utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); utils.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); setSubmoduleRegistry([]); @@ -382,8 +415,11 @@ describe('User ID', function() { // attaching after init attachIdSystem(unifiedIdSubmodule); + attachIdSystem(id5IdSubmodule); - config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], ['unifiedId', 'unifiedid', 'cookie'])); + config.setConfig(getConfigMock(['pubCommonId', 'pubcid', 'cookie'], + ['unifiedId', 'unifiedid', 'cookie'], + ['id5Id', 'id5id', 'cookie'])); requestBidsHook(function() { adUnits.forEach(unit => { @@ -394,10 +430,14 @@ describe('User ID', function() { // also check that UnifiedId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.tdid'); expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); }); }); utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); }); @@ -405,9 +445,10 @@ describe('User ID', function() { it('should add new id system ', function(done) { utils.setCookie('pubcid', 'testpubcid', (new Date(Date.now() + 5000).toUTCString())); utils.setCookie('unifiedid', JSON.stringify({'TDID': 'cookie-value-add-module-variations'}), new Date(Date.now() + 5000).toUTCString()); + utils.setCookie('id5id', JSON.stringify({'ID5ID': 'testid5id'}), (new Date(Date.now() + 5000).toUTCString())); utils.setCookie('MOCKID', JSON.stringify({'MOCKID': '123456778'}), new Date(Date.now() + 5000).toUTCString()); - setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule]); + setSubmoduleRegistry([pubCommonIdSubmodule, unifiedIdSubmodule, id5IdSubmodule]); init(config); config.setConfig({ @@ -417,6 +458,8 @@ describe('User ID', function() { name: 'pubCommonId', storage: { name: 'pubcid', type: 'cookie' } }, { name: 'unifiedId', storage: { name: 'unifiedid', type: 'cookie' } + }, { + name: 'id5Id', storage: { name: 'id5id', type: 'cookie' } }, { name: 'mockId', storage: { name: 'MOCKID', type: 'cookie' } }] @@ -445,6 +488,9 @@ describe('User ID', function() { // check UnifiedId id data was copied to bid expect(bid).to.have.deep.nested.property('userId.tdid'); expect(bid.userId.tdid).to.equal('cookie-value-add-module-variations'); + // also check that Id5Id id data was copied to bid + expect(bid).to.have.deep.nested.property('userId.id5id'); + expect(bid.userId.id5id).to.equal('testid5id'); // check MockId data was copied to bid expect(bid).to.have.deep.nested.property('userId.mid'); expect(bid.userId.mid).to.equal('123456778'); @@ -452,6 +498,7 @@ describe('User ID', function() { }); utils.setCookie('pubcid', '', EXPIRED_COOKIE_DATE); utils.setCookie('unifiedid', '', EXPIRED_COOKIE_DATE); + utils.setCookie('id5id', '', EXPIRED_COOKIE_DATE); utils.setCookie('MOCKID', '', EXPIRED_COOKIE_DATE); done(); }, {adUnits}); From 54ced0b08f02babf92f7eb84a6b3f9e71cd662a1 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Durgeat Date: Thu, 6 Jun 2019 07:33:34 +0200 Subject: [PATCH 7/8] Moved init of id5 user module at the end of the id5 module itself --- modules/id5IdSystem.js | 3 +++ modules/userId.js | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index de4a24fffb3..94ed2a17ae3 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -8,6 +8,7 @@ import * as utils from '../src/utils.js' import {ajax} from '../src/ajax.js'; import {isGDPRApplicable} from './userId.js'; +import {attachIdSystem} from './userId'; /** @type {Submodule} */ export const id5IdSubmodule = { @@ -56,3 +57,5 @@ export const id5IdSubmodule = { } } }; + +attachIdSystem(id5IdSubmodule); diff --git a/modules/userId.js b/modules/userId.js index f1447995d03..c80ea21a0a0 100644 --- a/modules/userId.js +++ b/modules/userId.js @@ -75,7 +75,6 @@ import {getGlobal} from '../src/prebidGlobal.js'; import {gdprDataHandler} from '../src/adapterManager.js'; import {unifiedIdSubmodule} from './unifiedIdSystem.js'; import {pubCommonIdSubmodule} from './pubCommonIdSystem.js'; -import {id5IdSubmodule} from './id5IdSystem.js'; import CONSTANTS from '../src/constants.json'; const MODULE_NAME = 'User ID'; @@ -432,4 +431,3 @@ init(config); // add submodules after init has been called attachIdSystem(pubCommonIdSubmodule); attachIdSystem(unifiedIdSubmodule); -attachIdSystem(id5IdSubmodule); From df8a35648946aecbab4cd20fa0502412dcdce476 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Durgeat Date: Wed, 19 Jun 2019 21:11:33 +0200 Subject: [PATCH 8/8] regroup import to avoid a CircleCI Bug --- modules/id5IdSystem.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/id5IdSystem.js b/modules/id5IdSystem.js index 94ed2a17ae3..39ab256a81e 100644 --- a/modules/id5IdSystem.js +++ b/modules/id5IdSystem.js @@ -7,8 +7,7 @@ import * as utils from '../src/utils.js' import {ajax} from '../src/ajax.js'; -import {isGDPRApplicable} from './userId.js'; -import {attachIdSystem} from './userId'; +import {isGDPRApplicable, attachIdSystem} from './userId.js'; /** @type {Submodule} */ export const id5IdSubmodule = {