From 0540240a311b953c2ffa5ab427aeae7532a0d515 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Mon, 27 Apr 2020 21:04:09 -0400 Subject: [PATCH 01/21] Add unit coverage for parrableIdSystem getId callback --- test/spec/modules/parrableIdSystem_spec.js | 66 +++++++++++++++++++--- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 93415126a0a..9321c342678 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -1,15 +1,19 @@ import { expect } from 'chai'; -import {config} from 'src/config.js'; +import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; import { parrableIdSubmodule } from 'modules/parrableIdSystem.js'; import { newStorageManager } from 'src/storageManager.js'; +import { getRefererInfo } from 'src/refererDetection.js'; + +import { server } from 'test/mocks/xhr'; const storage = newStorageManager(); const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; const P_COOKIE_NAME = '_parrable_eid'; -const P_COOKIE_VALUE = '01.1563917337.test-eid'; +const P_COOKIE_EID = '01.1563917337.test-eid'; +const P_XHR_EID = '01.1588030911.test-new-eid' const P_CONFIG_MOCK = { name: 'parrableId', params: { @@ -31,6 +35,7 @@ describe('Parrable ID System', function() { } } } + function getAdUnitMock(code = 'adUnit-code') { return { code, @@ -46,33 +51,76 @@ describe('Parrable ID System', function() { }; } + describe('parrableIdSystem.getId()', function() { + let submoduleCallback; + let callbackSpy = sinon.spy(); + + beforeEach(function() { + submoduleCallback = parrableIdSubmodule.getId( + P_CONFIG_MOCK.params, + null, + P_COOKIE_EID + ).callback; + callbackSpy.reset(); + }); + + it('returns a callback used to refresh the ID', function() { + expect(submoduleCallback).to.be.a('function'); + }); + + it('invoked callback creates an xhr request to Parrable with id and telemetry', function() { + submoduleCallback(callbackSpy); + + let request = server.requests[0]; + let queryParams = utils.parseQS(request.url.split('?')[1]); + let data = JSON.parse(atob(queryParams.data)); + + expect(request.url).to.contain('h.parrable.com/prebid'); + expect(data).to.deep.equal({ + eid: P_COOKIE_EID, + trackers: P_CONFIG_MOCK.params.partner.split(','), + url: getRefererInfo().referer + }); + }); + + it('callback responds with updated eid from Parrable backend', function() { + submoduleCallback(callbackSpy); + server.requests[0].respond(200, + { 'Content-Type': 'text/plain' }, + JSON.stringify({ eid: P_XHR_EID }) + ); + expect(callbackSpy.calledWith(P_XHR_EID)).to.be.true; + }); + }); + describe('Parrable ID in Bid Request', function() { let adUnits; beforeEach(function() { adUnits = [getAdUnitMock()]; - }); - - it('should append parrableid to bid request', function(done) { // simulate existing browser local storage values storage.setCookie( P_COOKIE_NAME, - P_COOKIE_VALUE, + P_COOKIE_EID, (new Date(Date.now() + 5000).toUTCString()) ); - setSubmoduleRegistry([parrableIdSubmodule]); init(config); config.setConfig(getConfigMock()); + }); + + afterEach(function() { + storage.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); + }); + it('provides the parrableid in the bid request', function(done) { requestBidsHook(function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.parrableid'); - expect(bid.userId.parrableid).to.equal(P_COOKIE_VALUE); + expect(bid.userId.parrableid).to.equal(P_COOKIE_EID); }); }); - storage.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); done(); }, { adUnits }); }); From 4af9a6f3e85751f9ea04ee93c32077c054e38b30 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Wed, 29 Apr 2020 14:06:51 -0400 Subject: [PATCH 02/21] PBID-14: Pass uspString to Parrable as us_privacy query parameter --- modules/parrableIdSystem.js | 9 ++- test/spec/modules/parrableIdSystem_spec.js | 64 +++++++++++++++++----- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index cc569a79062..e4e8fb5c5f4 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -9,6 +9,7 @@ import * as utils from '../src/utils.js' import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { getRefererInfo } from '../src/refererDetection.js'; +import { uspDataHandler } from '../src/adapterManager.js'; const PARRABLE_URL = 'https://h.parrable.com/prebid'; @@ -24,10 +25,11 @@ function isValidConfig(configParams) { return true; } -function fetchId(configParams, consentData, currentStoredId) { +function fetchId(configParams, currentStoredId) { if (!isValidConfig(configParams)) return; const refererInfo = getRefererInfo(); + const uspString = uspDataHandler.getConsentData(); const data = { eid: currentStoredId || null, @@ -37,6 +39,7 @@ function fetchId(configParams, consentData, currentStoredId) { const searchParams = { data: btoa(JSON.stringify(data)), + us_privacy: uspString, _rand: Math.random() }; @@ -88,8 +91,8 @@ export const parrableIdSubmodule = { * @param {ConsentData} [consentData] * @returns {function(callback:function)} */ - getId(configParams, consentData, currentStoredId) { - return fetchId(configParams, consentData, currentStoredId); + getId(configParams, gdprConsentData, currentStoredId) { + return fetchId(configParams, currentStoredId); } }; diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 9321c342678..9595cba1700 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -1,11 +1,11 @@ import { expect } from 'chai'; import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; -import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; -import { parrableIdSubmodule } from 'modules/parrableIdSystem.js'; import { newStorageManager } from 'src/storageManager.js'; import { getRefererInfo } from 'src/refererDetection.js'; - +import * as consentUsp from 'modules/consentManagementUsp.js'; +import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; +import { parrableIdSubmodule } from 'modules/parrableIdSystem.js'; import { server } from 'test/mocks/xhr'; const storage = newStorageManager(); @@ -52,45 +52,79 @@ describe('Parrable ID System', function() { } describe('parrableIdSystem.getId()', function() { - let submoduleCallback; let callbackSpy = sinon.spy(); beforeEach(function() { - submoduleCallback = parrableIdSubmodule.getId( - P_CONFIG_MOCK.params, - null, - P_COOKIE_EID - ).callback; callbackSpy.reset(); }); it('returns a callback used to refresh the ID', function() { - expect(submoduleCallback).to.be.a('function'); + let getIdResponse = parrableIdSubmodule.getId( + P_CONFIG_MOCK.params, + null, + P_COOKIE_EID + ); + expect(getIdResponse.callback).to.be.a('function'); }); - it('invoked callback creates an xhr request to Parrable with id and telemetry', function() { - submoduleCallback(callbackSpy); + it('callback creates xhr to Parrable that synchronizes the ID', function() { + let getIdCallback = parrableIdSubmodule.getId( + P_CONFIG_MOCK.params, + null, + P_COOKIE_EID + ).callback; + + getIdCallback(callbackSpy); let request = server.requests[0]; let queryParams = utils.parseQS(request.url.split('?')[1]); let data = JSON.parse(atob(queryParams.data)); expect(request.url).to.contain('h.parrable.com/prebid'); + expect(queryParams.us_privacy).to.equal('null'); expect(data).to.deep.equal({ eid: P_COOKIE_EID, trackers: P_CONFIG_MOCK.params.partner.split(','), url: getRefererInfo().referer }); - }); - it('callback responds with updated eid from Parrable backend', function() { - submoduleCallback(callbackSpy); server.requests[0].respond(200, { 'Content-Type': 'text/plain' }, JSON.stringify({ eid: P_XHR_EID }) ); + expect(callbackSpy.calledWith(P_XHR_EID)).to.be.true; }); + + it('passes the uspString to Parrable', function(done) { + let adUnits = [getAdUnitMock()]; + let uspString = '1YNN'; + let consentManagementConfig = { + usp: { + cmpApi: 'static', + consentData: { + getUSPData: { + uspString + } + } + } + }; + + // Outside of tests the consentManagementUsp module is run at priority 50 + // in the requestBids hook, where userId runs at 40 (later than 50) + // So when userId module collects userIdSystem callbacks the Usp data + // should be present. + consentUsp.setConsentConfig(consentManagementConfig); + consentUsp.requestBidsHook(function() { + parrableIdSubmodule.getId( + P_CONFIG_MOCK.params, + null, + P_COOKIE_EID + ).callback(callbackSpy); + expect(server.requests[0].url).to.contain('us_privacy=' + uspString); + done(); + }, { adUnits }); + }); }); describe('Parrable ID in Bid Request', function() { From 11b457b27d473a8220f01cd7dcb44190b77446bb Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 30 Apr 2020 13:02:23 -0400 Subject: [PATCH 03/21] PBID-14: Simplify parrableIdSystem us_privacy test --- test/spec/modules/parrableIdSystem_spec.js | 39 ++++++---------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 9595cba1700..317f8be7b2f 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -3,7 +3,7 @@ import { config } from 'src/config.js'; import * as utils from 'src/utils.js'; import { newStorageManager } from 'src/storageManager.js'; import { getRefererInfo } from 'src/refererDetection.js'; -import * as consentUsp from 'modules/consentManagementUsp.js'; +import { uspDataHandler } from 'src/adapterManager.js'; import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; import { parrableIdSubmodule } from 'modules/parrableIdSystem.js'; import { server } from 'test/mocks/xhr'; @@ -55,7 +55,7 @@ describe('Parrable ID System', function() { let callbackSpy = sinon.spy(); beforeEach(function() { - callbackSpy.reset(); + callbackSpy.resetHistory(); }); it('returns a callback used to refresh the ID', function() { @@ -96,34 +96,15 @@ describe('Parrable ID System', function() { expect(callbackSpy.calledWith(P_XHR_EID)).to.be.true; }); - it('passes the uspString to Parrable', function(done) { - let adUnits = [getAdUnitMock()]; + it('passes the uspString to Parrable', function() { let uspString = '1YNN'; - let consentManagementConfig = { - usp: { - cmpApi: 'static', - consentData: { - getUSPData: { - uspString - } - } - } - }; - - // Outside of tests the consentManagementUsp module is run at priority 50 - // in the requestBids hook, where userId runs at 40 (later than 50) - // So when userId module collects userIdSystem callbacks the Usp data - // should be present. - consentUsp.setConsentConfig(consentManagementConfig); - consentUsp.requestBidsHook(function() { - parrableIdSubmodule.getId( - P_CONFIG_MOCK.params, - null, - P_COOKIE_EID - ).callback(callbackSpy); - expect(server.requests[0].url).to.contain('us_privacy=' + uspString); - done(); - }, { adUnits }); + uspDataHandler.setConsentData(uspString); + parrableIdSubmodule.getId( + P_CONFIG_MOCK.params, + null, + P_COOKIE_EID + ).callback(callbackSpy); + expect(server.requests[0].url).to.contain('us_privacy=' + uspString); }); }); From 914503eef30718bf671622d87236e93184492b84 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 30 Apr 2020 13:40:37 -0400 Subject: [PATCH 04/21] PBID-14: Only send us_privacy to Parrable when a value exists --- modules/parrableIdSystem.js | 5 ++++- test/spec/modules/parrableIdSystem_spec.js | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index e4e8fb5c5f4..ba5fae81952 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -39,10 +39,13 @@ function fetchId(configParams, currentStoredId) { const searchParams = { data: btoa(JSON.stringify(data)), - us_privacy: uspString, _rand: Math.random() }; + if (uspString) { + searchParams.us_privacy = uspString; + } + const options = { method: 'GET', withCredentials: true diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 317f8be7b2f..d0bd75a9b2d 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -81,7 +81,7 @@ describe('Parrable ID System', function() { let data = JSON.parse(atob(queryParams.data)); expect(request.url).to.contain('h.parrable.com/prebid'); - expect(queryParams.us_privacy).to.equal('null'); + expect(queryParams).to.not.have.property('us_privacy'); expect(data).to.deep.equal({ eid: P_COOKIE_EID, trackers: P_CONFIG_MOCK.params.partner.split(','), From 98835fa1014d51e5530b2676365011ef778ee13f Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Tue, 5 May 2020 15:29:34 -0400 Subject: [PATCH 05/21] PBID-11: Read new Parrable compound cookie _parrable_id Migrating from legacy _parrable_eid cookie. The new cookie contains ibaOptout and ccpaOptout status fields --- modules/parrableIdSystem.js | 73 +++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 7 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index ba5fae81952..9f018aaab03 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -10,8 +10,40 @@ import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { getRefererInfo } from '../src/refererDetection.js'; import { uspDataHandler } from '../src/adapterManager.js'; +import { getStorageManager } from '../src/storageManager.js'; const PARRABLE_URL = 'https://h.parrable.com/prebid'; +const LEGACY_COOKIE_NAME = '_parrable_eid'; + +const storage = getStorageManager(); +const cookieExpireDate = new Date(0).toString(); + +function deserializeParrableId(parrableIdStr) { + const parrableId = {}; + const values = parrableIdStr.split(','); + + values.forEach(function(value) { + const pair = value.split(':'); + // unpack a value of 1 as true + parrableId[pair[0]] = +pair[1] === 1 ? true : pair[1]; + }); + + return parrableId; +} + +function serializeParrableId(parrableId) { + let str = ''; + if (parrableId.eid) { + str += 'eid:' + parrableId.eid; + } + if (parrableId.ibaOptout) { + str += ',ibaOptout:1'; + } + if (parrableId.ccpaOptout) { + str += ',ccpaOptout:1'; + } + return str; +} function isValidConfig(configParams) { if (!configParams) { @@ -25,14 +57,25 @@ function isValidConfig(configParams) { return true; } -function fetchId(configParams, currentStoredId) { +function readLegacyEid() { + legacyEid = storage.getCookie(LEGACY_COOKIE_NAME); + if (legacyEid) { + storage.setCookie(LEGACY_COOKIE_NAME, '', cookieExpireDate); + return legacyEid; + } + return undefined; +} + +function fetchId(configParams, parrableIdStr) { if (!isValidConfig(configParams)) return; + const parrableId = deserializeParrableId(parrableIdStr); + const legacyEid = readLegacyEid(); const refererInfo = getRefererInfo(); const uspString = uspDataHandler.getConsentData(); const data = { - eid: currentStoredId || null, + eid: (parrableId && parrableId.eid) || legacyEid || null, trackers: configParams.partner.split(','), url: refererInfo.referer }; @@ -53,21 +96,33 @@ function fetchId(configParams, currentStoredId) { const callback = function (cb) { const onSuccess = (response) => { - let eid; + let parrableId = {}; if (response) { try { let responseObj = JSON.parse(response); + if (responseObj) { + parrableId = { + eid: responseObj.eid, + ibaOptout: responseObj.ibaOptout, + ccpaOptout: responseObj.ccpaOptout + }; + } eid = responseObj ? responseObj.eid : undefined; } catch (error) { utils.logError(error); } } - cb(eid); + cb(serializeParrableId(parrableId)); }; ajax(PARRABLE_URL, onSuccess, searchParams, options); }; - return { callback }; + // provide the legacyStoredId so it gets used in auction on the first + // impression where the legacy cookie is migrated to the new cookie + return { + callback, + id: serializeParrableId({ eid: legacyStoredId }) + }; }; /** @type {Submodule} */ @@ -80,11 +135,15 @@ export const parrableIdSubmodule = { /** * decode the stored id value for passing to bid requests * @function - * @param {Object|string} value + * @param {string} value * @return {(Object|undefined} */ decode(value) { - return (value && typeof value === 'string') ? { 'parrableid': value } : undefined; + if (value && utils.isStr(value)) { + const idObject = deserializeParrableId(value); + return { 'parrableId': idObject }; + } + return undefined; }, /** From 43724a336f742e8ad21dfbf0c7ffc84c8fd61ef6 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Tue, 5 May 2020 16:19:41 -0400 Subject: [PATCH 06/21] Remove path check from parrableIdSystem url test --- test/spec/modules/parrableIdSystem_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index d0bd75a9b2d..4a02e16362c 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -80,7 +80,7 @@ describe('Parrable ID System', function() { let queryParams = utils.parseQS(request.url.split('?')[1]); let data = JSON.parse(atob(queryParams.data)); - expect(request.url).to.contain('h.parrable.com/prebid'); + expect(request.url).to.contain('h.parrable.com'); expect(queryParams).to.not.have.property('us_privacy'); expect(data).to.deep.equal({ eid: P_COOKIE_EID, From e88c929061e9550cf5b3dd2fa489aad020c6960b Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 7 May 2020 13:53:35 -0400 Subject: [PATCH 07/21] PBID-11: Integrate Parrable compound cookie, consolidating old cookies --- modules/parrableIdSystem.js | 92 +++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 9f018aaab03..3806ca18ef5 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -13,10 +13,18 @@ import { uspDataHandler } from '../src/adapterManager.js'; import { getStorageManager } from '../src/storageManager.js'; const PARRABLE_URL = 'https://h.parrable.com/prebid'; -const LEGACY_COOKIE_NAME = '_parrable_eid'; +const PARRABLE_COOKIE_NAME = '_parrable_id'; +const LEGACY_ID_COOKIE_NAME = '_parrable_eid'; +const LEGACY_OPTOUT_COOKIE_NAME = '_parrable_optout'; +const ONE_YEAR_MS = 364 * 24 * 60 * 60 * 1000; const storage = getStorageManager(); -const cookieExpireDate = new Date(0).toString(); +const expiredCookieDate = new Date(0).toString(); + +function getExpirationDate() { + const oneYearFromNow = new Date(utils.timestamp() + ONE_YEAR_MS); + return oneYearFromNow.toGMTString(); +} function deserializeParrableId(parrableIdStr) { const parrableId = {}; @@ -54,28 +62,62 @@ function isValidConfig(configParams) { utils.logError('User ID - parrableId submodule requires partner list'); return false; } + if (configParams.storage) { + utils.logWarn('User ID - parrableId submodule does not require a storage config'); + } return true; } -function readLegacyEid() { - legacyEid = storage.getCookie(LEGACY_COOKIE_NAME); - if (legacyEid) { - storage.setCookie(LEGACY_COOKIE_NAME, '', cookieExpireDate); - return legacyEid; +function readCookie() { + const parrableIdStr = storage.getCookie(PARRABLE_COOKIE_NAME); + if (parrableIdStr) { + return deserializeParrableId(decodeURIComponent(parrableIdStr)); } return undefined; } -function fetchId(configParams, parrableIdStr) { +function writeCookie(parrableId) { + const parrableIdStr = encodeURIComponent(serializeParrableId(parrableId)); + storage.setCookie(PARRABLE_COOKIE_NAME, parrableIdStr, getExpirationDate(), 'lax'); +} + +function readLegacyCookies() { + let legacyParrableId = {}; + let legacyEid = storage.getCookie(LEGACY_ID_COOKIE_NAME); + if (legacyEid) { + legacyParrableId.eid = legacyEid; + } + let legacyOptout = storage.getCookie(LEGACY_OPTOUT_COOKIE_NAME); + if (legacyOptout) { + legacyParrableId.ibaOptout = legacyOptout; + } + return legacyParrableId; +} + +function migrateLegacyCookies(parrableId) { + writeCookie(parrableId); + if (parrableId.eid) { + storage.setCookie(LEGACY_ID_COOKIE_NAME, '', expiredCookieDate); + } + if (parrableId.ibaOptout) { + storage.setCookie(LEGACY_OPTOUT_COOKIE_NAME, '', expiredCookieDate); + } +} + +function fetchId(configParams) { if (!isValidConfig(configParams)) return; - const parrableId = deserializeParrableId(parrableIdStr); - const legacyEid = readLegacyEid(); + let parrableId = readCookie(); + if (!parrableId) { + parrableId = readLegacyCookies(); + migrateLegacyCookies(parrableId); + } + const refererInfo = getRefererInfo(); const uspString = uspDataHandler.getConsentData(); const data = { - eid: (parrableId && parrableId.eid) || legacyEid || null, + eid: (parrableId && parrableId.eid) || null, trackers: configParams.partner.split(','), url: refererInfo.referer }; @@ -101,27 +143,28 @@ function fetchId(configParams, parrableIdStr) { try { let responseObj = JSON.parse(response); if (responseObj) { - parrableId = { - eid: responseObj.eid, - ibaOptout: responseObj.ibaOptout, - ccpaOptout: responseObj.ccpaOptout - }; + if (responseObj.ccpaOptout !== true) { + parrableId.eid = responseObj.eid; + } else { + parrableId.ccpaOptout = true; + } + if (responseObj.ibaOptout === true) { + parrableId.ibaOptout = true; + } + writeCookie(responseObj); } - eid = responseObj ? responseObj.eid : undefined; } catch (error) { utils.logError(error); } } - cb(serializeParrableId(parrableId)); + cb(parrableId); }; ajax(PARRABLE_URL, onSuccess, searchParams, options); }; - // provide the legacyStoredId so it gets used in auction on the first - // impression where the legacy cookie is migrated to the new cookie return { callback, - id: serializeParrableId({ eid: legacyStoredId }) + id: parrableId }; }; @@ -139,9 +182,8 @@ export const parrableIdSubmodule = { * @return {(Object|undefined} */ decode(value) { - if (value && utils.isStr(value)) { - const idObject = deserializeParrableId(value); - return { 'parrableId': idObject }; + if (value && utils.isPlainObject(value)) { + return { 'parrableid': value.eid }; } return undefined; }, @@ -154,7 +196,7 @@ export const parrableIdSubmodule = { * @returns {function(callback:function)} */ getId(configParams, gdprConsentData, currentStoredId) { - return fetchId(configParams, currentStoredId); + return fetchId(configParams); } }; From 1286adc56bf2380cef7b2234a97b0dd655a83348 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 7 May 2020 13:54:13 -0400 Subject: [PATCH 08/21] PBID-11: Update parrableIdSystem requestBids hook test to support compound cookie value --- test/spec/modules/parrableIdSystem_spec.js | 59 ++++++++++------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index d0bd75a9b2d..02078581165 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -11,45 +11,41 @@ import { server } from 'test/mocks/xhr'; const storage = newStorageManager(); const EXPIRED_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:01 GMT'; -const P_COOKIE_NAME = '_parrable_eid'; +const P_COOKIE_NAME = '_parrable_id'; const P_COOKIE_EID = '01.1563917337.test-eid'; const P_XHR_EID = '01.1588030911.test-new-eid' const P_CONFIG_MOCK = { name: 'parrableId', params: { partner: 'parrable_test_partner_123,parrable_test_partner_456' - }, - storage: { - name: '_parrable_eid', - type: 'cookie', - expires: 364 } }; -describe('Parrable ID System', function() { - function getConfigMock() { - return { - userSync: { - syncDelay: 0, - userIds: [P_CONFIG_MOCK] - } +function getConfigMock() { + return { + userSync: { + syncDelay: 0, + userIds: [P_CONFIG_MOCK] } } +} + +function getAdUnitMock(code = 'adUnit-code') { + return { + code, + mediaTypes: {banner: {}, native: {}}, + sizes: [ + [300, 200], + [300, 600] + ], + bids: [{ + bidder: 'sampleBidder', + params: { placementId: 'banner-only-bidder' } + }] + }; +} - function getAdUnitMock(code = 'adUnit-code') { - return { - code, - mediaTypes: {banner: {}, native: {}}, - sizes: [ - [300, 200], - [300, 600] - ], - bids: [{ - bidder: 'sampleBidder', - params: { placementId: 'banner-only-bidder' } - }] - }; - } +describe('Parrable ID System', function() { describe('parrableIdSystem.getId()', function() { let callbackSpy = sinon.spy(); @@ -108,7 +104,8 @@ describe('Parrable ID System', function() { }); }); - describe('Parrable ID in Bid Request', function() { + describe('userId requestBids hook', function() { + const parrableEid = '01.234.test-eid'; let adUnits; beforeEach(function() { @@ -116,7 +113,7 @@ describe('Parrable ID System', function() { // simulate existing browser local storage values storage.setCookie( P_COOKIE_NAME, - P_COOKIE_EID, + encodeURIComponent('eid:' + parrableEid), (new Date(Date.now() + 5000).toUTCString()) ); setSubmoduleRegistry([parrableIdSubmodule]); @@ -128,12 +125,12 @@ describe('Parrable ID System', function() { storage.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); }); - it('provides the parrableid in the bid request', function(done) { + it('when a stored Parrable ID exists it is added to bids', function(done) { requestBidsHook(function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.parrableid'); - expect(bid.userId.parrableid).to.equal(P_COOKIE_EID); + expect(bid.userId.parrableid).to.equal(parrableEid); }); }); done(); From 2be1fb36c4a66a4615353c19d1912fde89723232 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 7 May 2020 14:20:52 -0400 Subject: [PATCH 09/21] PBID-11: Small refactor to parrableIdSystem spec to support compound cookie --- test/spec/modules/parrableIdSystem_spec.js | 84 +++++++++++++++------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 02078581165..0c0dd9f9d0f 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -45,37 +45,61 @@ function getAdUnitMock(code = 'adUnit-code') { }; } -describe('Parrable ID System', function() { +function serializeParrableId(parrableId) { + let str = ''; + if (parrableId.eid) { + str += 'eid:' + parrableId.eid; + } + if (parrableId.ibaOptout) { + str += ',ibaOptout:1'; + } + if (parrableId.ccpaOptout) { + str += ',ccpaOptout:1'; + } + return str; +} - describe('parrableIdSystem.getId()', function() { +function writeParrableCookie(parrableId) { + let cookieValue = encodeURIComponent(serializeParrableId(parrableId)); + storage.setCookie( + P_COOKIE_NAME, + cookieValue, + (new Date(Date.now() + 5000).toUTCString()), + 'lax' + ); +} + +function removeParrableCookie() { + storage.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); +} + +describe('Parrable ID System', function() { + describe('parrableIdSystem.getId() callback', function() { let callbackSpy = sinon.spy(); beforeEach(function() { callbackSpy.resetHistory(); + writeParrableCookie({ eid: P_COOKIE_EID }); }); - it('returns a callback used to refresh the ID', function() { - let getIdResponse = parrableIdSubmodule.getId( - P_CONFIG_MOCK.params, - null, - P_COOKIE_EID - ); - expect(getIdResponse.callback).to.be.a('function'); - }); + afterEach(function() { + removeParrableCookie(); + }) - it('callback creates xhr to Parrable that synchronizes the ID', function() { - let getIdCallback = parrableIdSubmodule.getId( + it('creates xhr to Parrable that synchronizes the ID', function() { + let getIdResult = parrableIdSubmodule.getId( P_CONFIG_MOCK.params, null, - P_COOKIE_EID - ).callback; + null + ); - getIdCallback(callbackSpy); + getIdResult.callback(callbackSpy); let request = server.requests[0]; let queryParams = utils.parseQS(request.url.split('?')[1]); let data = JSON.parse(atob(queryParams.data)); + expect(getIdResult.callback).to.be.a('function'); expect(request.url).to.contain('h.parrable.com/prebid'); expect(queryParams).to.not.have.property('us_privacy'); expect(data).to.deep.equal({ @@ -89,40 +113,46 @@ describe('Parrable ID System', function() { JSON.stringify({ eid: P_XHR_EID }) ); - expect(callbackSpy.calledWith(P_XHR_EID)).to.be.true; + expect(callbackSpy.lastCall.lastArg).to.deep.equal({ + eid: P_XHR_EID + }); }); - it('passes the uspString to Parrable', function() { + it('xhr passes the uspString to Parrable', function() { let uspString = '1YNN'; uspDataHandler.setConsentData(uspString); parrableIdSubmodule.getId( P_CONFIG_MOCK.params, null, - P_COOKIE_EID + null ).callback(callbackSpy); + uspDataHandler.setConsentData(null); expect(server.requests[0].url).to.contain('us_privacy=' + uspString); }); }); + describe('parrableIdSystem.getId() id', function() { + it('provides the stored Parrable values if a cookie exists'); + it('migrates legacy cookies to new compound cookie format'); + }); + + describe('parrableIdSystem.decode()', function() { + it('provides the Parrable ID (EID) from a stored object'); + }); + describe('userId requestBids hook', function() { - const parrableEid = '01.234.test-eid'; let adUnits; beforeEach(function() { adUnits = [getAdUnitMock()]; - // simulate existing browser local storage values - storage.setCookie( - P_COOKIE_NAME, - encodeURIComponent('eid:' + parrableEid), - (new Date(Date.now() + 5000).toUTCString()) - ); + writeParrableCookie({ eid: P_COOKIE_EID }); setSubmoduleRegistry([parrableIdSubmodule]); init(config); config.setConfig(getConfigMock()); }); afterEach(function() { - storage.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); + removeParrableCookie(); }); it('when a stored Parrable ID exists it is added to bids', function(done) { @@ -130,7 +160,7 @@ describe('Parrable ID System', function() { adUnits.forEach(unit => { unit.bids.forEach(bid => { expect(bid).to.have.deep.nested.property('userId.parrableid'); - expect(bid.userId.parrableid).to.equal(parrableEid); + expect(bid.userId.parrableid).to.equal(P_COOKIE_EID); }); }); done(); From a9d67ee76b9071aed88eda0c92e500c8e1130b32 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 7 May 2020 14:42:45 -0400 Subject: [PATCH 10/21] PBID-11: Handle legacy ibaOptout as truthy value when migrating to compound cookie --- modules/parrableIdSystem.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 3806ca18ef5..c38132f8f07 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -88,8 +88,8 @@ function readLegacyCookies() { legacyParrableId.eid = legacyEid; } let legacyOptout = storage.getCookie(LEGACY_OPTOUT_COOKIE_NAME); - if (legacyOptout) { - legacyParrableId.ibaOptout = legacyOptout; + if (legacyOptout === 'true') { + legacyParrableId.ibaOptout = true; } return legacyParrableId; } From b71f7fd602da5d66ae60a8e4a8da75f7897cda7d Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 7 May 2020 14:43:02 -0400 Subject: [PATCH 11/21] PBID-11: Add parrableIdSystem spec tests covering migration of legacy cookies --- test/spec/modules/parrableIdSystem_spec.js | 55 ++++++++++++++++++---- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 0c0dd9f9d0f..646f2c952f6 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -87,11 +87,7 @@ describe('Parrable ID System', function() { }) it('creates xhr to Parrable that synchronizes the ID', function() { - let getIdResult = parrableIdSubmodule.getId( - P_CONFIG_MOCK.params, - null, - null - ); + let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); getIdResult.callback(callbackSpy); @@ -116,6 +112,10 @@ describe('Parrable ID System', function() { expect(callbackSpy.lastCall.lastArg).to.deep.equal({ eid: P_XHR_EID }); + + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + P_XHR_EID) + ); }); it('xhr passes the uspString to Parrable', function() { @@ -132,12 +132,51 @@ describe('Parrable ID System', function() { }); describe('parrableIdSystem.getId() id', function() { - it('provides the stored Parrable values if a cookie exists'); - it('migrates legacy cookies to new compound cookie format'); + it('provides the stored Parrable values if a cookie exists', function() { + writeParrableCookie({ eid: P_COOKIE_EID }); + let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); + removeParrableCookie(); + + expect(getIdResult.id).to.deep.equal({ + eid: P_COOKIE_EID + }); + }); + + it('provides the stored legacy Parrable ID values if cookies exist', function() { + let oldEid = '01.111.old-eid'; + let oldEidCookieName = '_parrable_eid'; + let oldOptoutCookieName = '_parrable_optout'; + + storage.setCookie(oldEidCookieName, oldEid); + storage.setCookie(oldOptoutCookieName, 'true'); + + let getIdResult = parrableIdSubmodule.getId(P_CONFIG_MOCK.params); + expect(getIdResult.id).to.deep.equal({ + eid: oldEid, + ibaOptout: true + }); + + // The ID system is expected to migrate old cookies to the new format + expect(storage.getCookie(P_COOKIE_NAME)).to.equal( + encodeURIComponent('eid:' + oldEid + ',ibaOptout:1') + ); + expect(storage.getCookie(oldEidCookieName)).to.equal(null); + expect(storage.getCookie(oldOptoutCookieName)).to.equal(null); + }); }); describe('parrableIdSystem.decode()', function() { - it('provides the Parrable ID (EID) from a stored object'); + it('provides the Parrable ID (EID) from a stored object', function() { + let eid = '01.123.4567890'; + let parrableId = { + eid, + ccpaOptout: true + }; + + expect(parrableIdSubmodule.decode(parrableId)).to.deep.equal({ + parrableid: eid + }); + }); }); describe('userId requestBids hook', function() { From 742e3d9d9b52a561f1008caf62540cfce737acce Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 7 May 2020 14:46:02 -0400 Subject: [PATCH 12/21] PBID-11: Remove storage documentation from test pages and userId module docs --- integrationExamples/gpt/audigentSegments_example.html | 5 ----- integrationExamples/gpt/userId_example.html | 5 ----- modules/userId/userId.md | 5 ----- 3 files changed, 15 deletions(-) diff --git a/integrationExamples/gpt/audigentSegments_example.html b/integrationExamples/gpt/audigentSegments_example.html index 9b72da76d23..7739b558327 100644 --- a/integrationExamples/gpt/audigentSegments_example.html +++ b/integrationExamples/gpt/audigentSegments_example.html @@ -162,11 +162,6 @@ params: { // change to Parrable Partner Client ID(s) you received from the Parrable Partners you are using partner: '30182847-e426-4ff9-b2b5-9ca1324ea09b' - }, - storage: { - type: "cookie", - name: "_parrable_eid", // create a cookie with this name - expires: 365 // cookie can last for a year } }, { name: "pubCommonId", diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 6d2c2ce677a..3bb8ce2df66 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -157,11 +157,6 @@ params: { // change to Parrable Partner Client ID(s) you received from the Parrable Partners you are using partner: '30182847-e426-4ff9-b2b5-9ca1324ea09b' - }, - storage: { - type: "cookie", - name: "_parrable_eid", // create a cookie with this name - expires: 365 // cookie can last for a year } }, { name: "pubCommonId", diff --git a/modules/userId/userId.md b/modules/userId/userId.md index e6da02a6811..5ce31a4e3a9 100644 --- a/modules/userId/userId.md +++ b/modules/userId/userId.md @@ -38,11 +38,6 @@ pbjs.setConfig({ params: { // Replace partner with comma-separated (if more than one) Parrable Partner Client ID(s) for Parrable-aware bid adapters in use partner: "30182847-e426-4ff9-b2b5-9ca1324ea09b" - }, - storage: { - type: 'cookie', - name: '_parrable_eid', - expires: 365 } }, { name: 'identityLink', From 16dd0837db32b69819fa0165734c789936266e1e Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Thu, 7 May 2020 14:49:17 -0400 Subject: [PATCH 13/21] PBID-11: Remove SUBMODULES_THAT_ALWAYS_REFRESH_ID feature from userId system --- modules/userId/index.js | 4 -- src/constants.json | 3 -- test/spec/modules/userId_spec.js | 76 -------------------------------- 3 files changed, 83 deletions(-) diff --git a/modules/userId/index.js b/modules/userId/index.js index 5de8ad75978..4ab39493b92 100644 --- a/modules/userId/index.js +++ b/modules/userId/index.js @@ -426,10 +426,6 @@ function initSubmodules(submodules, consentData) { refreshNeeded = storedDate && (Date.now() - storedDate.getTime() > submodule.config.storage.refreshInSeconds * 1000); } - if (CONSTANTS.SUBMODULES_THAT_ALWAYS_REFRESH_ID[submodule.config.name] === true) { - refreshNeeded = true; - } - if (!storedId || refreshNeeded) { // No previously saved id. Request one from submodule. response = submodule.submodule.getId(submodule.config.params, consentData, storedId); diff --git a/src/constants.json b/src/constants.json index 7ffef9db1aa..5965d77a6c4 100644 --- a/src/constants.json +++ b/src/constants.json @@ -97,8 +97,5 @@ "BID_TARGETING_SET": "targetingSet", "RENDERED": "rendered", "BID_REJECTED": "bidRejected" - }, - "SUBMODULES_THAT_ALWAYS_REFRESH_ID": { - "parrableId": true } } diff --git a/test/spec/modules/userId_spec.js b/test/spec/modules/userId_spec.js index b48d0a9a98c..b5497b55ed1 100644 --- a/test/spec/modules/userId_spec.js +++ b/test/spec/modules/userId_spec.js @@ -1231,81 +1231,5 @@ describe('User ID', function() { events.emit(CONSTANTS.EVENTS.AUCTION_END, {}); expect(server.requests[0].url).to.equal('https://match.adsrvr.org/track/rid?ttd_pid=rubicon&fmt=json'); }); - - it('callback for submodules that always need to refresh stored id', function(done) { - let adUnits = [getAdUnitMock()]; - let innerAdUnits; - const parrableStoredId = '01.1111111111.test-eid'; - const parrableRefreshedId = '02.2222222222.test-eid'; - coreStorage.setCookie('_parrable_eid', parrableStoredId, (new Date(Date.now() + 5000).toUTCString())); - - const parrableIdSubmoduleMock = { - name: 'parrableId', - decode: function(value) { - return { 'parrableid': value }; - }, - getId: function() { - return { - callback: function(cb) { - cb(parrableRefreshedId); - } - }; - } - }; - - const parrableConfigMock = { - userSync: { - syncDelay: 0, - userIds: [{ - name: 'parrableId', - storage: { - type: 'cookie', - name: '_parrable_eid' - } - }] - } - }; - - setSubmoduleRegistry([parrableIdSubmoduleMock]); - attachIdSystem(parrableIdSubmoduleMock); - init(config); - config.setConfig(parrableConfigMock); - - // make first bid request, should use stored id value - requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - innerAdUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.parrableid'); - expect(bid.userId.parrableid).to.equal(parrableStoredId); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'parrable.com', - uids: [{id: parrableStoredId, atype: 1}] - }); - }); - }); - - // attach a handler for auction end event to run the second bid request - events.on(CONSTANTS.EVENTS.AUCTION_END, function handler(submodule) { - if (submodule === 'parrableIdSubmoduleMock') { - // make the second bid request, id should have been refreshed - requestBidsHook((config) => { innerAdUnits = config.adUnits }, {adUnits}); - innerAdUnits.forEach(unit => { - unit.bids.forEach(bid => { - expect(bid).to.have.deep.nested.property('userId.parrableid'); - expect(bid.userId.parrableid).to.equal(parrableRefreshedId); - expect(bid.userIdAsEids[0]).to.deep.equal({ - source: 'parrable.com', - uids: [{id: parrableRefreshedId, atype: 1}] - }); - }); - }); - events.off(CONSTANTS.EVENTS.AUCTION_END, handler); - done(); - } - }); - - // emit an auction end event to run the submodule callback - events.emit(CONSTANTS.EVENTS.AUCTION_END, 'parrableIdSubmoduleMock'); - }); }); }); From 7bf438b3605ecfb530082c030ee5bfa8f29bd44e Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Fri, 8 May 2020 17:56:13 -0400 Subject: [PATCH 14/21] PBID-11: Use better serialize implementation for Parrable compound cookie --- modules/parrableIdSystem.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index c38132f8f07..a4617e2dc60 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -40,17 +40,19 @@ function deserializeParrableId(parrableIdStr) { } function serializeParrableId(parrableId) { - let str = ''; + let components = []; + if (parrableId.eid) { - str += 'eid:' + parrableId.eid; + components.push('eid:' + parrableId.eid); } if (parrableId.ibaOptout) { - str += ',ibaOptout:1'; + components.push('ibaOptout:1'); } if (parrableId.ccpaOptout) { - str += ',ccpaOptout:1'; + components.push('ccpaOptout:1'); } - return str; + + return components.join(','); } function isValidConfig(configParams) { From 527303e23b020e4e3acba128fc79347365e2298f Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Fri, 8 May 2020 17:56:34 -0400 Subject: [PATCH 15/21] PBID-11: Update parrableIdSystem interface documentation --- modules/parrableIdSystem.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index a4617e2dc60..91a018b70ce 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -180,12 +180,12 @@ export const parrableIdSubmodule = { /** * decode the stored id value for passing to bid requests * @function - * @param {string} value + * @param {ParrableId} parrableId * @return {(Object|undefined} */ - decode(value) { - if (value && utils.isPlainObject(value)) { - return { 'parrableid': value.eid }; + decode(parrableId) { + if (parrableId && utils.isPlainObject(parrableId)) { + return { 'parrableid': parrableId.eid }; } return undefined; }, @@ -195,7 +195,7 @@ export const parrableIdSubmodule = { * @function * @param {SubmoduleParams} [configParams] * @param {ConsentData} [consentData] - * @returns {function(callback:function)} + * @returns {function(callback:function), id:ParrableId} */ getId(configParams, gdprConsentData, currentStoredId) { return fetchId(configParams); From 8f717daeae8762b3fd59b2773b985df29f7dee96 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Mon, 11 May 2020 09:52:35 -0400 Subject: [PATCH 16/21] Add missing extension to mock xhr import --- test/spec/modules/parrableIdSystem_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 4a02e16362c..662f63f6638 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -6,7 +6,7 @@ import { getRefererInfo } from 'src/refererDetection.js'; import { uspDataHandler } from 'src/adapterManager.js'; import { init, requestBidsHook, setSubmoduleRegistry } from 'modules/userId/index.js'; import { parrableIdSubmodule } from 'modules/parrableIdSystem.js'; -import { server } from 'test/mocks/xhr'; +import { server } from 'test/mocks/xhr.js'; const storage = newStorageManager(); From 6ba511b8561f8dd768ef360581e1c85d7d54df33 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Wed, 13 May 2020 11:31:03 -0400 Subject: [PATCH 17/21] PBID-11: Try to access eid property only when parrableId object exists --- modules/parrableIdSystem.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 91a018b70ce..fbf8254db64 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -115,11 +115,12 @@ function fetchId(configParams) { migrateLegacyCookies(parrableId); } + const eid = (parrableId) ? parrableId.eid : null; const refererInfo = getRefererInfo(); const uspString = uspDataHandler.getConsentData(); const data = { - eid: (parrableId && parrableId.eid) || null, + eid, trackers: configParams.partner.split(','), url: refererInfo.referer }; From 8e084ab7cf5aeeea9886a591e424bab37212286c Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Wed, 13 May 2020 12:30:47 -0400 Subject: [PATCH 18/21] PBID-11: Construct parrableId from legacy cookies in same manner as compound cookie --- modules/parrableIdSystem.js | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index fbf8254db64..25ac2d2275e 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -75,25 +75,30 @@ function readCookie() { if (parrableIdStr) { return deserializeParrableId(decodeURIComponent(parrableIdStr)); } - return undefined; + return null; } function writeCookie(parrableId) { - const parrableIdStr = encodeURIComponent(serializeParrableId(parrableId)); - storage.setCookie(PARRABLE_COOKIE_NAME, parrableIdStr, getExpirationDate(), 'lax'); + if (parrableId) { + const parrableIdStr = encodeURIComponent(serializeParrableId(parrableId)); + storage.setCookie(PARRABLE_COOKIE_NAME, parrableIdStr, getExpirationDate(), 'lax'); + } } function readLegacyCookies() { - let legacyParrableId = {}; - let legacyEid = storage.getCookie(LEGACY_ID_COOKIE_NAME); - if (legacyEid) { - legacyParrableId.eid = legacyEid; - } - let legacyOptout = storage.getCookie(LEGACY_OPTOUT_COOKIE_NAME); - if (legacyOptout === 'true') { - legacyParrableId.ibaOptout = true; + const eid = storage.getCookie(LEGACY_ID_COOKIE_NAME); + const ibaOptout = (storage.getCookie(LEGACY_OPTOUT_COOKIE_NAME) === 'true'); + if (eid || ibaOptout) { + const parrableId = {}; + if (eid) { + parrableId.eid = eid; + } + if (ibaOptout) { + parrableId.ibaOptout = ibaOptout; + } + return parrableId; } - return legacyParrableId; + return null; } function migrateLegacyCookies(parrableId) { From e34e55fcb3fa67474191b9da229f15152749b403 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Wed, 13 May 2020 12:57:20 -0400 Subject: [PATCH 19/21] Use hardcoded expiration date for legacy cookies --- modules/parrableIdSystem.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 25ac2d2275e..536497b4177 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -17,9 +17,9 @@ const PARRABLE_COOKIE_NAME = '_parrable_id'; const LEGACY_ID_COOKIE_NAME = '_parrable_eid'; const LEGACY_OPTOUT_COOKIE_NAME = '_parrable_optout'; const ONE_YEAR_MS = 364 * 24 * 60 * 60 * 1000; +const EXPIRE_COOKIE_DATE = 'Thu, 01 Jan 1970 00:00:00 GMT'; const storage = getStorageManager(); -const expiredCookieDate = new Date(0).toString(); function getExpirationDate() { const oneYearFromNow = new Date(utils.timestamp() + ONE_YEAR_MS); @@ -104,10 +104,10 @@ function readLegacyCookies() { function migrateLegacyCookies(parrableId) { writeCookie(parrableId); if (parrableId.eid) { - storage.setCookie(LEGACY_ID_COOKIE_NAME, '', expiredCookieDate); + storage.setCookie(LEGACY_ID_COOKIE_NAME, '', EXPIRE_COOKIE_DATE); } if (parrableId.ibaOptout) { - storage.setCookie(LEGACY_OPTOUT_COOKIE_NAME, '', expiredCookieDate); + storage.setCookie(LEGACY_OPTOUT_COOKIE_NAME, '', EXPIRE_COOKIE_DATE); } } From 373075fe0a8a9b18d2866b9272e07330a403e85f Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Mon, 18 May 2020 17:21:36 -0400 Subject: [PATCH 20/21] parrableIdSystem: Relocate new unit test from upstream --- modules/parrableIdSystem.js | 17 +++++----- test/spec/modules/parrableIdSystem_spec.js | 36 +++++++++++----------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 9b0ffa28dc5..4fe8a1b1359 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -102,12 +102,14 @@ function readLegacyCookies() { } function migrateLegacyCookies(parrableId) { - writeCookie(parrableId); - if (parrableId.eid) { - storage.setCookie(LEGACY_ID_COOKIE_NAME, '', EXPIRE_COOKIE_DATE); - } - if (parrableId.ibaOptout) { - storage.setCookie(LEGACY_OPTOUT_COOKIE_NAME, '', EXPIRE_COOKIE_DATE); + if (parrableId) { + writeCookie(parrableId); + if (parrableId.eid) { + storage.setCookie(LEGACY_ID_COOKIE_NAME, '', EXPIRE_COOKIE_DATE); + } + if (parrableId.ibaOptout) { + storage.setCookie(LEGACY_OPTOUT_COOKIE_NAME, '', EXPIRE_COOKIE_DATE); + } } } @@ -161,10 +163,11 @@ function fetchId(configParams) { parrableId.ibaOptout = true; } writeCookie(responseObj); + cb(parrableId); } } catch (error) { utils.logError(error); - cb(eid); + cb(); } } }, diff --git a/test/spec/modules/parrableIdSystem_spec.js b/test/spec/modules/parrableIdSystem_spec.js index 80e3d02e55b..fdfd3042561 100644 --- a/test/spec/modules/parrableIdSystem_spec.js +++ b/test/spec/modules/parrableIdSystem_spec.js @@ -75,15 +75,18 @@ function removeParrableCookie() { describe('Parrable ID System', function() { describe('parrableIdSystem.getId() callback', function() { + let logErrorStub; let callbackSpy = sinon.spy(); beforeEach(function() { + logErrorStub = sinon.stub(utils, 'logError'); callbackSpy.resetHistory(); writeParrableCookie({ eid: P_COOKIE_EID }); }); afterEach(function() { removeParrableCookie(); + logErrorStub.restore(); }) it('creates xhr to Parrable that synchronizes the ID', function() { @@ -130,6 +133,21 @@ describe('Parrable ID System', function() { uspDataHandler.setConsentData(null); expect(server.requests[0].url).to.contain('us_privacy=' + uspString); }); + + it('should log an error and continue to callback if ajax request errors', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = parrableIdSubmodule.getId({partner: 'prebid'}).callback; + submoduleCallback(callBackSpy); + let request = server.requests[0]; + expect(request.url).to.contain('h.parrable.com'); + request.respond( + 503, + null, + 'Unavailable' + ); + expect(logErrorStub.calledOnce).to.be.true; + expect(callBackSpy.calledOnce).to.be.true; + }); }); describe('parrableIdSystem.getId() id', function() { @@ -182,7 +200,6 @@ describe('Parrable ID System', function() { describe('userId requestBids hook', function() { let adUnits; - let logErrorStub; beforeEach(function() { adUnits = [getAdUnitMock()]; @@ -190,13 +207,11 @@ describe('Parrable ID System', function() { setSubmoduleRegistry([parrableIdSubmodule]); init(config); config.setConfig(getConfigMock()); - logErrorStub = sinon.stub(utils, 'logError'); }); afterEach(function() { removeParrableCookie(); storage.setCookie(P_COOKIE_NAME, '', EXPIRED_COOKIE_DATE); - logErrorStub.restore(); }); it('when a stored Parrable ID exists it is added to bids', function(done) { @@ -210,20 +225,5 @@ describe('Parrable ID System', function() { done(); }, { adUnits }); }); - - it('should log an error and continue to callback if ajax request errors', function () { - let callBackSpy = sinon.spy(); - let submoduleCallback = parrableIdSubmodule.getId({partner: 'prebid'}).callback; - submoduleCallback(callBackSpy); - let request = server.requests[0]; - expect(request.url).to.contain('h.parrable.com'); - request.respond( - 503, - null, - 'Unavailable' - ); - expect(logErrorStub.calledOnce).to.be.true; - expect(callBackSpy.calledOnce).to.be.true; - }); }); }); From df73ab9448d8baf8f6baac10ea53322c6c2c64e7 Mon Sep 17 00:00:00 2001 From: Ian Flournoy Date: Tue, 19 May 2020 16:06:33 -0400 Subject: [PATCH 21/21] PBID-39: Fallback to cookie values when backend response is missing components Also handle another missed callback scenario if the response object parses to nothing --- modules/parrableIdSystem.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/parrableIdSystem.js b/modules/parrableIdSystem.js index 4fe8a1b1359..ec033e62983 100644 --- a/modules/parrableIdSystem.js +++ b/modules/parrableIdSystem.js @@ -149,26 +149,30 @@ function fetchId(configParams) { const callback = function (cb) { const callbacks = { success: response => { - let parrableId = {}; + let newParrableId = parrableId ? utils.deepClone(parrableId) : {}; if (response) { try { let responseObj = JSON.parse(response); if (responseObj) { if (responseObj.ccpaOptout !== true) { - parrableId.eid = responseObj.eid; + newParrableId.eid = responseObj.eid; } else { - parrableId.ccpaOptout = true; + newParrableId.eid = null; + newParrableId.ccpaOptout = true; } if (responseObj.ibaOptout === true) { - parrableId.ibaOptout = true; + newParrableId.ibaOptout = true; } - writeCookie(responseObj); - cb(parrableId); } } catch (error) { utils.logError(error); cb(); } + writeCookie(newParrableId); + cb(newParrableId); + } else { + utils.logError('parrableId: ID fetch returned an empty result'); + cb(); } }, error: error => {