From 08ce0ee880d87d0ba7a3202bdd3c0da2c880e906 Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Tue, 12 May 2020 09:38:52 -0400 Subject: [PATCH 01/15] Add a LotameIdSystem as a new User ID module --- integrationExamples/gpt/userId_example.html | 8 ++ modules/lotameIdSystem.js | 93 +++++++++++++++++++++ modules/lotameIdSystem.md | 30 +++++++ test/spec/modules/lotameIdSystem_spec.js | 44 ++++++++++ 4 files changed, 175 insertions(+) create mode 100644 modules/lotameIdSystem.js create mode 100644 modules/lotameIdSystem.md create mode 100644 test/spec/modules/lotameIdSystem_spec.js diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 6d2c2ce677a..51d01783854 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -184,6 +184,14 @@ name: 'idl_env', expires: 30 } + }, + { + name: 'lotameId', + storage: { + type: 'cookie', + name: '_cc_pano', + expires: 7 + } }], syncDelay: 5000, auctionDelay: 1000 diff --git a/modules/lotameIdSystem.js b/modules/lotameIdSystem.js new file mode 100644 index 00000000000..47467eb5e37 --- /dev/null +++ b/modules/lotameIdSystem.js @@ -0,0 +1,93 @@ +/** + * This module adds LotameId to the User ID module + * The {@link module:modules/userId} module is required + * @module modules/lotameIdSystem + * @requires module:modules/userId + */ +import * as utils from '../src/utils.js'; +import { ajax } from '../src/ajax.js'; +import { submodule } from '../src/hook.js'; +import { getStorageManager } from '../src/storageManager.js'; + +const STORAGE_ID = '_cc_id'; +const MODULE_NAME = 'lotameId'; +const NINE_MONTHS_IN_SECONDS = 23328000; +export const storage = getStorageManager(null, MODULE_NAME); + +function setFirstPartyId(profileId) { + if (storage.cookiesAreEnabled()) { + let expirationDate = new Date(Date.now() + NINE_MONTHS_IN_SECONDS * 1000).toUTCString(); + storage.setCookie(STORAGE_ID, profileId, expirationDate, 'Lax', undefined, undefined); + } + if (storage.hasLocalStorage()) { + storage.setDataInLocalStorage(STORAGE_ID, profileId, undefined); + } +} + +function getFirstPartyId() { + if (storage.cookiesAreEnabled()) { + return storage.getCookie(STORAGE_ID, undefined); + } + if (storage.hasLocalStorage()) { + return storage.getDataFromLocalStorage(STORAGE_ID, undefined); + } +} + +/** @type {Submodule} */ +export const lotameIdSubmodule = { + /** + * used to link submodule with config + * @type {string} + */ + name: MODULE_NAME, + + /** + * Decode the stored id value for passing to bid requests + * @function decode + * @param {(Object|string)} value + * @returns {(Object|undefined)} + */ + decode(value, configParams) { + return value && typeof value['panorama_id'] === 'string' + ? { lotameId: value['panorama_id'] } + : undefined; + }, + + /** + * Retrieve the Lotame id + * @function + * @param {SubmoduleParams} [configParams] + * @param {ConsentData} [consentData] + * @param {(Object|undefined)} cacheIdObj + * @returns {IdResponse|undefined} + */ + getId(configParams, consentData, cacheIdObj) { + const storedUserId = getFirstPartyId(); + + const resolveIdFunction = function (callback) { + const param = storedUserId ? `?profile_id=${storedUserId}` : ''; + const url = 'https://mconrad.dev.lotame.com:5555/panorama/id' + param; + ajax( + url, + (response) => { + let responseObj = {}; + if (response) { + try { + responseObj = JSON.parse(response); + setFirstPartyId(responseObj.profile_id); + } catch (error) { + utils.logError(error); + } + } + callback(responseObj); + }, + undefined, + { method: 'GET', withCredentials: true } + ); + }; + + return { callback: resolveIdFunction }; + }, +}; + +submodule('userId', lotameIdSubmodule); diff --git a/modules/lotameIdSystem.md b/modules/lotameIdSystem.md new file mode 100644 index 00000000000..705f7c196e6 --- /dev/null +++ b/modules/lotameIdSystem.md @@ -0,0 +1,30 @@ +# Overview + +``` +Module Name: Lotame Id System +Module Type: Id System +Maintainer: prebid@lotame.com +``` + +# Description + +Retrieve Lotame's Id + +# Usage + +``` + pbjs.que.push(function() { + pbjs.setConfig({ + usersync: { + userIds: [ + { + name: 'lotameId', + storage: { + type: 'cookie', // cookie|html5 + name: '_cc_pano', // The name of the cookie or html5 local storage where the user Id will be stored + expires: 7 // How long (in days) the user ID will be stored + } + }], + } + }); +``` \ No newline at end of file diff --git a/test/spec/modules/lotameIdSystem_spec.js b/test/spec/modules/lotameIdSystem_spec.js new file mode 100644 index 00000000000..0bf122b8671 --- /dev/null +++ b/test/spec/modules/lotameIdSystem_spec.js @@ -0,0 +1,44 @@ +import {lotameIdSubmodule} from 'modules/lotameIdSystem.js'; +import * as utils from 'src/utils.js'; +import { server } from 'test/mocks/xhr.js'; + +const responseHeader = { 'Content-Type': 'application/json' }; + +describe('LotameId', function() { + let pixel = {}; + let logErrorStub; + let imgStub; + + beforeEach(function () { + imgStub = sinon.stub(window, 'Image').returns(pixel); + logErrorStub = sinon.stub(utils, 'logError'); + }); + + afterEach(function () { + pixel = {}; + imgStub.restore(); + logErrorStub.restore(); + }); + + it('should call the remote server when getId is called', function () { + let callBackSpy = sinon.spy(); + let submoduleCallback = lotameIdSubmodule.getId({}).callback; + submoduleCallback(callBackSpy); + + let request = server.requests[0]; + expect(request.url).to.be.eq( + 'https://mconrad.dev.lotame.com:5555/panorama/id' + ); + request.respond(200, responseHeader, JSON.stringify({})); + expect(callBackSpy.calledOnce).to.be.true; + }); + + it('should retrieve the panorama id when decode is called', function() { + var panoramaId = lotameIdSubmodule.decode({ + panorama_id: '1234' + }); + expect(panoramaId).to.be.eql({ + 'lotameId': '1234' + }); + }); +}); From 4ee8ccd3eee752cfe3c8dbf6b14bc9a4ed3cd17a Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Thu, 14 May 2020 09:50:34 -0400 Subject: [PATCH 02/15] Now using our own caching system --- integrationExamples/gpt/userId_example.html | 7 +- modules/lotameIdSystem.js | 91 +++++++++++++++++++-- modules/lotameIdSystem.md | 7 +- test/spec/modules/lotameIdSystem_spec.js | 48 ++++++++--- 4 files changed, 119 insertions(+), 34 deletions(-) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 51d01783854..46203e42bab 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -186,12 +186,7 @@ } }, { - name: 'lotameId', - storage: { - type: 'cookie', - name: '_cc_pano', - expires: 7 - } + name: 'lotameId' }], syncDelay: 5000, auctionDelay: 1000 diff --git a/modules/lotameIdSystem.js b/modules/lotameIdSystem.js index 47467eb5e37..e1dcd8ab158 100644 --- a/modules/lotameIdSystem.js +++ b/modules/lotameIdSystem.js @@ -9,27 +9,81 @@ import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { getStorageManager } from '../src/storageManager.js'; -const STORAGE_ID = '_cc_id'; +const PROFILE_ID_NAME = '_cc_id'; const MODULE_NAME = 'lotameId'; const NINE_MONTHS_IN_SECONDS = 23328000; +const DAYS_TO_CACHE = 7 +const DAYS_IN_MILLISECONDS = 60 * 60 * 24 * 1000; + export const storage = getStorageManager(null, MODULE_NAME); function setFirstPartyId(profileId) { if (storage.cookiesAreEnabled()) { let expirationDate = new Date(Date.now() + NINE_MONTHS_IN_SECONDS * 1000).toUTCString(); - storage.setCookie(STORAGE_ID, profileId, expirationDate, 'Lax', undefined, undefined); + storage.setCookie(PROFILE_ID_NAME, profileId, expirationDate, 'Lax', undefined, undefined); } if (storage.hasLocalStorage()) { - storage.setDataInLocalStorage(STORAGE_ID, profileId, undefined); + storage.setDataInLocalStorage(PROFILE_ID_NAME, profileId, undefined); } } function getFirstPartyId() { if (storage.cookiesAreEnabled()) { - return storage.getCookie(STORAGE_ID, undefined); + return storage.getCookie(PROFILE_ID_NAME, undefined); } if (storage.hasLocalStorage()) { - return storage.getDataFromLocalStorage(STORAGE_ID, undefined); + return storage.getDataFromLocalStorage(PROFILE_ID_NAME, undefined); + } +} + +function getFromStorage(key) { + return storage.getCookie(key) || storage.getDataFromLocalStorage(key); +} + +function saveLotameCache(key, value) { + if (key && value) { + if (storage.cookiesAreEnabled()) { + let expirationDate = new Date( + Date.now() + DAYS_TO_CACHE * DAYS_IN_MILLISECONDS).toUTCString(); + storage.setCookie(key, value, expirationDate, 'Lax', undefined, undefined); + } + if (storage.hasLocalStorage()) { + storage.setDataInLocalStorage(key, value, undefined); + } + } +} + +function getLotameLocalCache() { + let cache = { + lastUpdate: new Date(), + data: getFromStorage('_lota_pano'), + bundle: { + refreshSeconds: 3600 + } + }; + + try { + cache.bundle = JSON.parse(getFromStorage('_lota_pano_bundle')); + } catch (error) { + utils.logError(error); + } + try { + cache.lastUpdate = new Date(getFromStorage('_lota_last')); + } catch (error) { + utils.logError(error); + } + return cache; +} + +function clearLotameCache(key) { + if (key) { + if (storage.cookiesAreEnabled()) { + let expirationDate = new Date(0).toUTCString(); + storage.setCookie(key, '', expirationDate, 'Lax', undefined, undefined); + } + if (storage.hasLocalStorage()) { + storage.removeDataFromLocalStorage(key, undefined); + } } } @@ -48,9 +102,7 @@ export const lotameIdSubmodule = { * @returns {(Object|undefined)} */ decode(value, configParams) { - return value && typeof value['panorama_id'] === 'string' - ? { lotameId: value['panorama_id'] } - : undefined; + return value; }, /** @@ -62,8 +114,19 @@ export const lotameIdSubmodule = { * @returns {IdResponse|undefined} */ getId(configParams, consentData, cacheIdObj) { - const storedUserId = getFirstPartyId(); + let localCache = getLotameLocalCache(); + + let refreshNeeded = true; + if (!localCache.data) { + const lastUpdate = localCache.lastUpdate; + refreshNeeded = (Date.now() - lastUpdate.getTime()) > localCache.bundle.refreshSeconds * 1000; + } + if (!refreshNeeded) { + return {}; + } + + const storedUserId = getFirstPartyId(); const resolveIdFunction = function (callback) { const param = storedUserId ? `?profile_id=${storedUserId}` : ''; const url = 'https://mconrad.dev.lotame.com:5555/panorama/id' + param; @@ -75,6 +138,16 @@ export const lotameIdSubmodule = { try { responseObj = JSON.parse(response); setFirstPartyId(responseObj.profile_id); + saveLotameCache('_lota_last', new Date().toUTCString()); + if (utils.isStr(responseObj.panorama_id)) { + saveLotameCache('_lota_pano', response.panorama_id); + } else { + clearLotameCache('_lota_pano'); + } + // TODO: Get this from the response + saveLotameCache('_lota_pano_bundle', JSON.stringify({ + refreshSeconds: 10 + })); } catch (error) { utils.logError(error); } diff --git a/modules/lotameIdSystem.md b/modules/lotameIdSystem.md index 705f7c196e6..c85c9abd878 100644 --- a/modules/lotameIdSystem.md +++ b/modules/lotameIdSystem.md @@ -18,12 +18,7 @@ Retrieve Lotame's Id usersync: { userIds: [ { - name: 'lotameId', - storage: { - type: 'cookie', // cookie|html5 - name: '_cc_pano', // The name of the cookie or html5 local storage where the user Id will be stored - expires: 7 // How long (in days) the user ID will be stored - } + name: 'lotameId' // The only parameter that is needed }], } }); diff --git a/test/spec/modules/lotameIdSystem_spec.js b/test/spec/modules/lotameIdSystem_spec.js index 0bf122b8671..4c8a5bc0480 100644 --- a/test/spec/modules/lotameIdSystem_spec.js +++ b/test/spec/modules/lotameIdSystem_spec.js @@ -1,26 +1,42 @@ -import {lotameIdSubmodule} from 'modules/lotameIdSystem.js'; +import {lotameIdSubmodule, storage} from 'modules/lotameIdSystem.js'; import * as utils from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; const responseHeader = { 'Content-Type': 'application/json' }; describe('LotameId', function() { - let pixel = {}; let logErrorStub; - let imgStub; + let getCookieStub; + let setCookieStub; + let getLocalStorageStub; + let setLocalStorageStub; + let removeFromLocalStorageStub; beforeEach(function () { - imgStub = sinon.stub(window, 'Image').returns(pixel); logErrorStub = sinon.stub(utils, 'logError'); + getCookieStub = sinon.stub(storage, 'getCookie'); + setCookieStub = sinon.stub(storage, 'setCookie'); + getLocalStorageStub = sinon.stub(storage, 'getDataFromLocalStorage'); + setLocalStorageStub = sinon.stub(storage, 'setDataInLocalStorage'); + removeFromLocalStorageStub = sinon.stub( + storage, + 'removeDataFromLocalStorage' + ); }); afterEach(function () { - pixel = {}; - imgStub.restore(); logErrorStub.restore(); + getCookieStub.restore(); + setCookieStub.restore(); + getLocalStorageStub.restore(); + setLocalStorageStub.restore(); + removeFromLocalStorageStub.restore(); }); it('should call the remote server when getId is called', function () { + getCookieStub.withArgs('_lota_pano').returns(JSON.stringify({ + refreshSeconds: 10 + })); let callBackSpy = sinon.spy(); let submoduleCallback = lotameIdSubmodule.getId({}).callback; submoduleCallback(callBackSpy); @@ -29,16 +45,22 @@ describe('LotameId', function() { expect(request.url).to.be.eq( 'https://mconrad.dev.lotame.com:5555/panorama/id' ); - request.respond(200, responseHeader, JSON.stringify({})); + request.respond( + 200, + responseHeader, + JSON.stringify({ + profile_id: '4ec137245858469eb94a4e248f238694', + panorama_id: + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', + }) + ); expect(callBackSpy.calledOnce).to.be.true; + + // Check the calls to storage }); it('should retrieve the panorama id when decode is called', function() { - var panoramaId = lotameIdSubmodule.decode({ - panorama_id: '1234' - }); - expect(panoramaId).to.be.eql({ - 'lotameId': '1234' - }); + var panoramaId = lotameIdSubmodule.decode('1234'); + expect(panoramaId).to.be.eql('1234'); }); }); From c27c19a7b1bd817bb343d4de9f054f40226a521d Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Thu, 14 May 2020 14:14:25 -0400 Subject: [PATCH 03/15] Add more test cases; Switch names to be constants --- modules/lotameIdSystem.js | 40 +++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/modules/lotameIdSystem.js b/modules/lotameIdSystem.js index e1dcd8ab158..e1075411350 100644 --- a/modules/lotameIdSystem.js +++ b/modules/lotameIdSystem.js @@ -9,6 +9,9 @@ import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { getStorageManager } from '../src/storageManager.js'; +const KEY_LAST_UPDATE = '_lota_last'; +const KEY_ID = '_lota_pano'; +const KEY_EXPIRY = '_lota_pano_bundle'; const PROFILE_ID_NAME = '_cc_id'; const MODULE_NAME = 'lotameId'; const NINE_MONTHS_IN_SECONDS = 23328000; @@ -19,7 +22,9 @@ export const storage = getStorageManager(null, MODULE_NAME); function setFirstPartyId(profileId) { if (storage.cookiesAreEnabled()) { - let expirationDate = new Date(Date.now() + NINE_MONTHS_IN_SECONDS * 1000).toUTCString(); + let expirationDate = new Date( + utils.timestamp() + NINE_MONTHS_IN_SECONDS * 1000 + ).toUTCString(); storage.setCookie(PROFILE_ID_NAME, profileId, expirationDate, 'Lax', undefined, undefined); } if (storage.hasLocalStorage()) { @@ -44,7 +49,8 @@ function saveLotameCache(key, value) { if (key && value) { if (storage.cookiesAreEnabled()) { let expirationDate = new Date( - Date.now() + DAYS_TO_CACHE * DAYS_IN_MILLISECONDS).toUTCString(); + utils.timestamp() + DAYS_TO_CACHE * DAYS_IN_MILLISECONDS + ).toUTCString(); storage.setCookie(key, value, expirationDate, 'Lax', undefined, undefined); } if (storage.hasLocalStorage()) { @@ -55,20 +61,26 @@ function saveLotameCache(key, value) { function getLotameLocalCache() { let cache = { - lastUpdate: new Date(), - data: getFromStorage('_lota_pano'), + lastUpdate: new Date(0), + data: getFromStorage(KEY_ID), bundle: { - refreshSeconds: 3600 - } + refreshSeconds: 3600, + }, }; try { - cache.bundle = JSON.parse(getFromStorage('_lota_pano_bundle')); + const rawBundle = getFromStorage(KEY_EXPIRY); + if (utils.isStr(rawBundle)) { + cache.bundle = JSON.parse(rawBundle); + } } catch (error) { utils.logError(error); } try { - cache.lastUpdate = new Date(getFromStorage('_lota_last')); + const rawDate = getFromStorage(KEY_LAST_UPDATE); + if (utils.isStr(rawDate)) { + cache.lastUpdate = new Date(rawDate); + } } catch (error) { utils.logError(error); } @@ -116,8 +128,8 @@ export const lotameIdSubmodule = { getId(configParams, consentData, cacheIdObj) { let localCache = getLotameLocalCache(); - let refreshNeeded = true; - if (!localCache.data) { + let refreshNeeded = false; + if (!utils.isStr(localCache.data)) { const lastUpdate = localCache.lastUpdate; refreshNeeded = (Date.now() - lastUpdate.getTime()) > localCache.bundle.refreshSeconds * 1000; } @@ -138,14 +150,14 @@ export const lotameIdSubmodule = { try { responseObj = JSON.parse(response); setFirstPartyId(responseObj.profile_id); - saveLotameCache('_lota_last', new Date().toUTCString()); + saveLotameCache(KEY_LAST_UPDATE, new Date().toUTCString()); if (utils.isStr(responseObj.panorama_id)) { - saveLotameCache('_lota_pano', response.panorama_id); + saveLotameCache(KEY_ID, responseObj.panorama_id); } else { - clearLotameCache('_lota_pano'); + clearLotameCache(KEY_ID); } // TODO: Get this from the response - saveLotameCache('_lota_pano_bundle', JSON.stringify({ + saveLotameCache(KEY_EXPIRY, JSON.stringify({ refreshSeconds: 10 })); } catch (error) { From 9d41c903499b6072a7e693e54c69424e292f91d5 Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Thu, 14 May 2020 14:20:35 -0400 Subject: [PATCH 04/15] Add test cases --- test/spec/modules/lotameIdSystem_spec.js | 120 +++++++++++++++++++---- 1 file changed, 99 insertions(+), 21 deletions(-) diff --git a/test/spec/modules/lotameIdSystem_spec.js b/test/spec/modules/lotameIdSystem_spec.js index 4c8a5bc0480..8bd273cf1ff 100644 --- a/test/spec/modules/lotameIdSystem_spec.js +++ b/test/spec/modules/lotameIdSystem_spec.js @@ -11,6 +11,9 @@ describe('LotameId', function() { let getLocalStorageStub; let setLocalStorageStub; let removeFromLocalStorageStub; + let timeStampStub; + + const nowTimestamp = new Date().getTime(); beforeEach(function () { logErrorStub = sinon.stub(utils, 'logError'); @@ -22,6 +25,7 @@ describe('LotameId', function() { storage, 'removeDataFromLocalStorage' ); + timeStampStub = sinon.stub(utils, 'timestamp').returns(nowTimestamp); }); afterEach(function () { @@ -31,32 +35,106 @@ describe('LotameId', function() { getLocalStorageStub.restore(); setLocalStorageStub.restore(); removeFromLocalStorageStub.restore(); + timeStampStub.restore(); }); - it('should call the remote server when getId is called', function () { - getCookieStub.withArgs('_lota_pano').returns(JSON.stringify({ - refreshSeconds: 10 - })); + describe('caching initial data received from the remote server', function () { + let request; let callBackSpy = sinon.spy(); - let submoduleCallback = lotameIdSubmodule.getId({}).callback; - submoduleCallback(callBackSpy); - let request = server.requests[0]; - expect(request.url).to.be.eq( - 'https://mconrad.dev.lotame.com:5555/panorama/id' - ); - request.respond( - 200, - responseHeader, - JSON.stringify({ - profile_id: '4ec137245858469eb94a4e248f238694', - panorama_id: - 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', - }) - ); - expect(callBackSpy.calledOnce).to.be.true; + this.beforeEach(function() { + let submoduleCallback = lotameIdSubmodule.getId({}).callback; + submoduleCallback(callBackSpy); + + request = server.requests[0]; + + request.respond( + 200, + responseHeader, + JSON.stringify({ + profile_id: '4ec137245858469eb94a4e248f238694', + panorama_id: + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', + }) + ); + }); + + it('should call the remote server when getId is called', function () { + expect(request.url).to.be.eq( + 'https://mconrad.dev.lotame.com:5555/panorama/id' + ); + + expect(callBackSpy.calledOnce).to.be.true; + }); + + it('should save the last update date', function () { + // Check the calls to storage + sinon.assert.calledWith(setLocalStorageStub, '_lota_last'); + sinon.assert.calledWith(setCookieStub, '_lota_last'); + }); + + it('should save the first party id', function () { + sinon.assert.calledWith( + setLocalStorageStub, + '_cc_id', + '4ec137245858469eb94a4e248f238694' + ); + sinon.assert.calledWith( + setCookieStub, + '_cc_id', + '4ec137245858469eb94a4e248f238694' + ); + }); + + it('should save the expiry bundle', function () { + sinon.assert.calledWith( + setLocalStorageStub, + '_lota_pano_bundle', + JSON.stringify({ + refreshSeconds: 10, + }) + ); + + sinon.assert.calledWith( + setCookieStub, + '_lota_pano_bundle', + JSON.stringify({ + refreshSeconds: 10, + }) + ); + }); + + it('should save the id', function () { + sinon.assert.calledWith( + setLocalStorageStub, + '_lota_pano', + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a' + ); + + sinon.assert.calledWith( + setCookieStub, + '_lota_pano', + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a' + ); + }); + }); + + describe('existing id found', function () { + let request; + let submoduleCallback; + + this.beforeEach(function () { + getLocalStorageStub.withArgs('_lota_pano').returns( + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a'); + + submoduleCallback = lotameIdSubmodule.getId({}); + + request = server.requests[0]; + }); - // Check the calls to storage + it('should not call the remote server when getId is called', function () { + expect(submoduleCallback).to.be.eql({}); + }); }); it('should retrieve the panorama id when decode is called', function() { From ec826ec8dfedb50e798bade5537154460e684bca Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Thu, 14 May 2020 16:25:52 -0400 Subject: [PATCH 05/15] Handle "expiring" local storage like the built in code does --- modules/lotameIdSystem.js | 37 +++++++++++++----- test/spec/modules/lotameIdSystem_spec.js | 49 +++++++++++++++++++++--- 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/modules/lotameIdSystem.js b/modules/lotameIdSystem.js index e1075411350..713c5b4d7cd 100644 --- a/modules/lotameIdSystem.js +++ b/modules/lotameIdSystem.js @@ -14,17 +14,15 @@ const KEY_ID = '_lota_pano'; const KEY_EXPIRY = '_lota_pano_bundle'; const PROFILE_ID_NAME = '_cc_id'; const MODULE_NAME = 'lotameId'; -const NINE_MONTHS_IN_SECONDS = 23328000; +const NINE_MONTHS_MS = 23328000 * 1000; const DAYS_TO_CACHE = 7 -const DAYS_IN_MILLISECONDS = 60 * 60 * 24 * 1000; +const DAY_MS = 60 * 60 * 24 * 1000; export const storage = getStorageManager(null, MODULE_NAME); function setFirstPartyId(profileId) { if (storage.cookiesAreEnabled()) { - let expirationDate = new Date( - utils.timestamp() + NINE_MONTHS_IN_SECONDS * 1000 - ).toUTCString(); + let expirationDate = new Date(utils.timestamp() + NINE_MONTHS_MS).toUTCString(); storage.setCookie(PROFILE_ID_NAME, profileId, expirationDate, 'Lax', undefined, undefined); } if (storage.hasLocalStorage()) { @@ -42,18 +40,35 @@ function getFirstPartyId() { } function getFromStorage(key) { - return storage.getCookie(key) || storage.getDataFromLocalStorage(key); + let value = null; + if (storage.cookiesAreEnabled()) { + value = storage.getCookie(key, undefined); + } + if (storage.hasLocalStorage() && value === null) { + const storedValueExp = storage.getDataFromLocalStorage( + `${key}_exp`, undefined + ); + if (storedValueExp === '') { + value = storage.getDataFromLocalStorage(key, undefined); + } else if (storedValueExp) { + if ((new Date(storedValueExp)).getTime() - Date.now() > 0) { + value = storage.getDataFromLocalStorage(key, undefined); + } + } + } + return value; } function saveLotameCache(key, value) { if (key && value) { + let expirationDate = new Date( + utils.timestamp() + DAYS_TO_CACHE * DAY_MS + ).toUTCString(); if (storage.cookiesAreEnabled()) { - let expirationDate = new Date( - utils.timestamp() + DAYS_TO_CACHE * DAYS_IN_MILLISECONDS - ).toUTCString(); storage.setCookie(key, value, expirationDate, 'Lax', undefined, undefined); } if (storage.hasLocalStorage()) { + storage.setDataInLocalStorage(`${key}_exp`, expirationDate, undefined); storage.setDataInLocalStorage(key, value, undefined); } } @@ -135,7 +150,9 @@ export const lotameIdSubmodule = { } if (!refreshNeeded) { - return {}; + return { + id: localCache.data + }; } const storedUserId = getFirstPartyId(); diff --git a/test/spec/modules/lotameIdSystem_spec.js b/test/spec/modules/lotameIdSystem_spec.js index 8bd273cf1ff..e3ba681de48 100644 --- a/test/spec/modules/lotameIdSystem_spec.js +++ b/test/spec/modules/lotameIdSystem_spec.js @@ -120,20 +120,59 @@ describe('LotameId', function() { }); describe('existing id found', function () { - let request; let submoduleCallback; this.beforeEach(function () { - getLocalStorageStub.withArgs('_lota_pano').returns( - 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a'); + getCookieStub + .withArgs('_lota_pano') + .returns( + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a' + ); submoduleCallback = lotameIdSubmodule.getId({}); + }); + + it('should not call the remote server when getId is called', function () { + expect(submoduleCallback).to.be.eql({ + id: 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', + }); + }); + }); + + describe('expired id found', function () { + let request; + let callBackSpy = sinon.spy(); + + this.beforeEach(function () { + getLocalStorageStub + .withArgs('_lota_pano_exp') + .returns(new Date( + utils.timestamp() - (10 * 24 * 60 * 60 * 1000) + ).toUTCString()); + getLocalStorageStub + .withArgs('_lota_pano') + .returns( + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a' + ); + + let submoduleCallback = lotameIdSubmodule.getId({}).callback; + submoduleCallback(callBackSpy); request = server.requests[0]; + + request.respond( + 200, + responseHeader, + JSON.stringify({ + profile_id: '4ec137245858469eb94a4e248f238694', + panorama_id: + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', + }) + ); }); - it('should not call the remote server when getId is called', function () { - expect(submoduleCallback).to.be.eql({}); + it('should call the remote server when getId is called', function () { + expect(callBackSpy.calledOnce).to.be.true; }); }); From fc32767534ade2f8b6a6fdda9cd49c7c5bc15a48 Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Tue, 19 May 2020 09:10:21 -0400 Subject: [PATCH 06/15] Switch to using the expiry_ms from the endpoint as the cookie expiration --- modules/lotameIdSystem.js | 64 ++++++++++++-------- test/spec/modules/lotameIdSystem_spec.js | 74 +++++++++++++++++------- 2 files changed, 93 insertions(+), 45 deletions(-) diff --git a/modules/lotameIdSystem.js b/modules/lotameIdSystem.js index 713c5b4d7cd..ddf3bf73493 100644 --- a/modules/lotameIdSystem.js +++ b/modules/lotameIdSystem.js @@ -11,7 +11,7 @@ import { getStorageManager } from '../src/storageManager.js'; const KEY_LAST_UPDATE = '_lota_last'; const KEY_ID = '_lota_pano'; -const KEY_EXPIRY = '_lota_pano_bundle'; +const KEY_EXPIRY = '_lota_expiry'; const PROFILE_ID_NAME = '_cc_id'; const MODULE_NAME = 'lotameId'; const NINE_MONTHS_MS = 23328000 * 1000; @@ -59,13 +59,22 @@ function getFromStorage(key) { return value; } -function saveLotameCache(key, value) { +function saveLotameCache( + key, + value, + expirationTimestamp = utils.timestamp() + DAYS_TO_CACHE * DAY_MS +) { if (key && value) { - let expirationDate = new Date( - utils.timestamp() + DAYS_TO_CACHE * DAY_MS - ).toUTCString(); + let expirationDate = new Date(expirationTimestamp).toUTCString(); if (storage.cookiesAreEnabled()) { - storage.setCookie(key, value, expirationDate, 'Lax', undefined, undefined); + storage.setCookie( + key, + value, + expirationDate, + 'Lax', + undefined, + undefined + ); } if (storage.hasLocalStorage()) { storage.setDataInLocalStorage(`${key}_exp`, expirationDate, undefined); @@ -78,15 +87,13 @@ function getLotameLocalCache() { let cache = { lastUpdate: new Date(0), data: getFromStorage(KEY_ID), - bundle: { - refreshSeconds: 3600, - }, + expiryMs: 0, }; try { - const rawBundle = getFromStorage(KEY_EXPIRY); - if (utils.isStr(rawBundle)) { - cache.bundle = JSON.parse(rawBundle); + const rawExpiry = getFromStorage(KEY_EXPIRY); + if (utils.isStr(rawExpiry)) { + cache.expiryMs = parseInt(rawExpiry, 0); } } catch (error) { utils.logError(error); @@ -113,7 +120,6 @@ function clearLotameCache(key) { } } } - /** @type {Submodule} */ export const lotameIdSubmodule = { /** @@ -145,8 +151,7 @@ export const lotameIdSubmodule = { let refreshNeeded = false; if (!utils.isStr(localCache.data)) { - const lastUpdate = localCache.lastUpdate; - refreshNeeded = (Date.now() - lastUpdate.getTime()) > localCache.bundle.refreshSeconds * 1000; + refreshNeeded = Date.now() > localCache.expiryMs; } if (!refreshNeeded) { @@ -156,9 +161,25 @@ export const lotameIdSubmodule = { } const storedUserId = getFirstPartyId(); + const hasGdprData = + consentData && + utils.isBoolean(consentData.gdprApplies) && + consentData.gdprApplies; const resolveIdFunction = function (callback) { - const param = storedUserId ? `?profile_id=${storedUserId}` : ''; - const url = 'https://mconrad.dev.lotame.com:5555/panorama/id' + param; + let queryParams = {}; + if (storedUserId) { + queryParams.fp = storedUserId + } + + if (hasGdprData) { + queryParams.gdpr_consent = consentData.consentString; + } + const url = utils.buildUrl({ + protocol: 'https', + host: `mconrad.dev.lotame.com:5555`, + pathname: '/id', + search: utils.isEmpty(queryParams) ? undefined : queryParams + }); ajax( url, (response) => { @@ -168,15 +189,12 @@ export const lotameIdSubmodule = { responseObj = JSON.parse(response); setFirstPartyId(responseObj.profile_id); saveLotameCache(KEY_LAST_UPDATE, new Date().toUTCString()); - if (utils.isStr(responseObj.panorama_id)) { - saveLotameCache(KEY_ID, responseObj.panorama_id); + saveLotameCache(KEY_EXPIRY, responseObj.expiry_ms); + if (utils.isStr(responseObj.core_id)) { + saveLotameCache(KEY_ID, responseObj.core_id, responseObj.expiry_ms); } else { clearLotameCache(KEY_ID); } - // TODO: Get this from the response - saveLotameCache(KEY_EXPIRY, JSON.stringify({ - refreshSeconds: 10 - })); } catch (error) { utils.logError(error); } diff --git a/test/spec/modules/lotameIdSystem_spec.js b/test/spec/modules/lotameIdSystem_spec.js index e3ba681de48..30e6a05df21 100644 --- a/test/spec/modules/lotameIdSystem_spec.js +++ b/test/spec/modules/lotameIdSystem_spec.js @@ -53,7 +53,8 @@ describe('LotameId', function() { responseHeader, JSON.stringify({ profile_id: '4ec137245858469eb94a4e248f238694', - panorama_id: + expiry_ms: 10, + core_id: 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', }) ); @@ -61,7 +62,7 @@ describe('LotameId', function() { it('should call the remote server when getId is called', function () { expect(request.url).to.be.eq( - 'https://mconrad.dev.lotame.com:5555/panorama/id' + 'https://mconrad.dev.lotame.com:5555/id' ); expect(callBackSpy.calledOnce).to.be.true; @@ -86,21 +87,14 @@ describe('LotameId', function() { ); }); - it('should save the expiry bundle', function () { + it('should save the expiry', function () { sinon.assert.calledWith( setLocalStorageStub, - '_lota_pano_bundle', - JSON.stringify({ - refreshSeconds: 10, - }) - ); + '_lota_expiry', 10); sinon.assert.calledWith( setCookieStub, - '_lota_pano_bundle', - JSON.stringify({ - refreshSeconds: 10, - }) + '_lota_expiry', 10 ); }); @@ -119,10 +113,11 @@ describe('LotameId', function() { }); }); - describe('existing id found', function () { + describe('a non-expired id found', function () { let submoduleCallback; this.beforeEach(function () { + getLocalStorageStub.withArgs('_lota_expiry').returns(Date.now() + 100000); getCookieStub .withArgs('_lota_pano') .returns( @@ -139,16 +134,14 @@ describe('LotameId', function() { }); }); - describe('expired id found', function () { + describe('an expired id found', function () { let request; let callBackSpy = sinon.spy(); this.beforeEach(function () { getLocalStorageStub - .withArgs('_lota_pano_exp') - .returns(new Date( - utils.timestamp() - (10 * 24 * 60 * 60 * 1000) - ).toUTCString()); + .withArgs('_lota_expiry') + .returns(1000); getLocalStorageStub .withArgs('_lota_pano') .returns( @@ -165,8 +158,39 @@ describe('LotameId', function() { responseHeader, JSON.stringify({ profile_id: '4ec137245858469eb94a4e248f238694', - panorama_id: + core_id: + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', + expiry_ms: 3600000 + }) + ); + }); + + it('should call the remote server when getId is called', function () { + expect(callBackSpy.calledOnce).to.be.true; + }); + }); + + describe('gdpr applies', function () { + let request; + let callBackSpy = sinon.spy(); + + this.beforeEach(function () { + let submoduleCallback = lotameIdSubmodule.getId({}, { + gdprApplies: true, + consentString: 'consentGiven' + }).callback; + submoduleCallback(callBackSpy); + + request = server.requests[0]; + + request.respond( + 200, + responseHeader, + JSON.stringify({ + profile_id: '4ec137245858469eb94a4e248f238694', + core_id: 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', + expiry_ms: 3600000, }) ); }); @@ -174,10 +198,16 @@ describe('LotameId', function() { it('should call the remote server when getId is called', function () { expect(callBackSpy.calledOnce).to.be.true; }); + + it('should pass the gdpr consent string back', function() { + expect(request.url).to.be.eq( + 'https://mconrad.dev.lotame.com:5555/id?gdpr_consent=consentGiven' + ); + }); }); - it('should retrieve the panorama id when decode is called', function() { - var panoramaId = lotameIdSubmodule.decode('1234'); - expect(panoramaId).to.be.eql('1234'); + it('should retrieve the id when decode is called', function() { + var id = lotameIdSubmodule.decode('1234'); + expect(id).to.be.eql('1234'); }); }); From 864339e969651fe3127d44d8867393d1c3b4cb34 Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Wed, 3 Jun 2020 12:15:14 -0400 Subject: [PATCH 07/15] Update to the official naming; Remove the lastUpdate storage as we don't use it and depend on what comes from the endpoint; Update to use the dev bcp endpoint for now --- integrationExamples/gpt/userId_example.html | 2 +- ...eIdSystem.js => lotamePanoramaIdSystem.js} | 104 +++++++++++------- ...eIdSystem.md => lotamePanoramaIdSystem.md} | 6 +- ...spec.js => lotamePanoramaIdSystem_spec.js} | 53 +++++---- 4 files changed, 95 insertions(+), 70 deletions(-) rename modules/{lotameIdSystem.js => lotamePanoramaIdSystem.js} (66%) rename modules/{lotameIdSystem.md => lotamePanoramaIdSystem.md} (64%) rename test/spec/modules/{lotameIdSystem_spec.js => lotamePanoramaIdSystem_spec.js} (80%) diff --git a/integrationExamples/gpt/userId_example.html b/integrationExamples/gpt/userId_example.html index 46203e42bab..dbef2cc0af2 100644 --- a/integrationExamples/gpt/userId_example.html +++ b/integrationExamples/gpt/userId_example.html @@ -186,7 +186,7 @@ } }, { - name: 'lotameId' + name: 'lotamePanoramaId' }], syncDelay: 5000, auctionDelay: 1000 diff --git a/modules/lotameIdSystem.js b/modules/lotamePanoramaIdSystem.js similarity index 66% rename from modules/lotameIdSystem.js rename to modules/lotamePanoramaIdSystem.js index ddf3bf73493..99230ddba28 100644 --- a/modules/lotameIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -1,7 +1,7 @@ /** - * This module adds LotameId to the User ID module + * This module adds LotamePanoramaId to the User ID module * The {@link module:modules/userId} module is required - * @module modules/lotameIdSystem + * @module modules/lotamePanoramaId * @requires module:modules/userId */ import * as utils from '../src/utils.js'; @@ -9,36 +9,46 @@ import { ajax } from '../src/ajax.js'; import { submodule } from '../src/hook.js'; import { getStorageManager } from '../src/storageManager.js'; -const KEY_LAST_UPDATE = '_lota_last'; -const KEY_ID = '_lota_pano'; -const KEY_EXPIRY = '_lota_expiry'; -const PROFILE_ID_NAME = '_cc_id'; -const MODULE_NAME = 'lotameId'; +const KEY_ID = 'panoramaId'; +const KEY_EXPIRY = `${KEY_ID}_expiry`; +const KEY_PROFILE = '_cc_id'; +const MODULE_NAME = 'lotamePanoramaId'; const NINE_MONTHS_MS = 23328000 * 1000; -const DAYS_TO_CACHE = 7 +const DAYS_TO_CACHE = 7; const DAY_MS = 60 * 60 * 24 * 1000; export const storage = getStorageManager(null, MODULE_NAME); -function setFirstPartyId(profileId) { +/** + * Set the Lotame First Party Profile ID in the first party namespace + * @param {String} profileId + */ +function setProfileId(profileId) { if (storage.cookiesAreEnabled()) { let expirationDate = new Date(utils.timestamp() + NINE_MONTHS_MS).toUTCString(); - storage.setCookie(PROFILE_ID_NAME, profileId, expirationDate, 'Lax', undefined, undefined); + storage.setCookie(KEY_PROFILE, profileId, expirationDate, 'Lax', undefined, undefined); } if (storage.hasLocalStorage()) { - storage.setDataInLocalStorage(PROFILE_ID_NAME, profileId, undefined); + storage.setDataInLocalStorage(KEY_PROFILE, profileId, undefined); } } -function getFirstPartyId() { +/** + * Get the Lotame profile id by checking cookies first and then local storage + */ +function getProfileId() { if (storage.cookiesAreEnabled()) { - return storage.getCookie(PROFILE_ID_NAME, undefined); + return storage.getCookie(KEY_PROFILE, undefined); } if (storage.hasLocalStorage()) { - return storage.getDataFromLocalStorage(PROFILE_ID_NAME, undefined); + return storage.getDataFromLocalStorage(KEY_PROFILE, undefined); } } +/** + * Get a value from browser storage by checking cookies first and then local storage + * @param {String} key + */ function getFromStorage(key) { let value = null; if (storage.cookiesAreEnabled()) { @@ -59,6 +69,12 @@ function getFromStorage(key) { return value; } +/** + * Save a key/value pair to the browser cache (cookies and local storage) + * @param {String} key + * @param {String} value + * @param {Number} expirationTimestamp + */ function saveLotameCache( key, value, @@ -83,32 +99,31 @@ function saveLotameCache( } } +/** + * Retrieve all the cached values from cookies and/or local storage + */ function getLotameLocalCache() { let cache = { - lastUpdate: new Date(0), data: getFromStorage(KEY_ID), - expiryMs: 0, + expiryTimestampMs: 0, }; try { const rawExpiry = getFromStorage(KEY_EXPIRY); if (utils.isStr(rawExpiry)) { - cache.expiryMs = parseInt(rawExpiry, 0); - } - } catch (error) { - utils.logError(error); - } - try { - const rawDate = getFromStorage(KEY_LAST_UPDATE); - if (utils.isStr(rawDate)) { - cache.lastUpdate = new Date(rawDate); + cache.expiryTimestampMs = parseInt(rawExpiry, 0); } } catch (error) { utils.logError(error); } + return cache; } +/** + * Clear a cached value from cookies and local storage + * @param {String} key + */ function clearLotameCache(key) { if (key) { if (storage.cookiesAreEnabled()) { @@ -121,7 +136,8 @@ function clearLotameCache(key) { } } /** @type {Submodule} */ -export const lotameIdSubmodule = { +export const lotamePanoramaIdSubmodule = { + /** * used to link submodule with config * @type {string} @@ -135,11 +151,11 @@ export const lotameIdSubmodule = { * @returns {(Object|undefined)} */ decode(value, configParams) { - return value; + return utils.isStr(value) ? { 'lotamePanoramaId': value } : undefined; }, /** - * Retrieve the Lotame id + * Retrieve the Lotame Panorama Id * @function * @param {SubmoduleParams} [configParams] * @param {ConsentData} [consentData] @@ -151,7 +167,7 @@ export const lotameIdSubmodule = { let refreshNeeded = false; if (!utils.isStr(localCache.data)) { - refreshNeeded = Date.now() > localCache.expiryMs; + refreshNeeded = Date.now() > localCache.expiryTimestampMs; } if (!refreshNeeded) { @@ -160,7 +176,7 @@ export const lotameIdSubmodule = { }; } - const storedUserId = getFirstPartyId(); + const storedUserId = getProfileId(); const hasGdprData = consentData && utils.isBoolean(consentData.gdprApplies) && @@ -176,9 +192,9 @@ export const lotameIdSubmodule = { } const url = utils.buildUrl({ protocol: 'https', - host: `mconrad.dev.lotame.com:5555`, + host: `bcp.dev.lotame.com`, pathname: '/id', - search: utils.isEmpty(queryParams) ? undefined : queryParams + search: utils.isEmpty(queryParams) ? undefined : queryParams, }); ajax( url, @@ -187,14 +203,23 @@ export const lotameIdSubmodule = { if (response) { try { responseObj = JSON.parse(response); - setFirstPartyId(responseObj.profile_id); - saveLotameCache(KEY_LAST_UPDATE, new Date().toUTCString()); - saveLotameCache(KEY_EXPIRY, responseObj.expiry_ms); + saveLotameCache(KEY_EXPIRY, responseObj.expiry_ts); + if (utils.isStr(responseObj.core_id)) { - saveLotameCache(KEY_ID, responseObj.core_id, responseObj.expiry_ms); + saveLotameCache( + KEY_ID, + responseObj.core_id, + responseObj.expiry_ts + ); } else { clearLotameCache(KEY_ID); } + + if (utils.isStr(responseObj.profile_id)) { + setProfileId(responseObj.profile_id); + } else { + clearLotameCache(KEY_PROFILE); + } } catch (error) { utils.logError(error); } @@ -202,7 +227,10 @@ export const lotameIdSubmodule = { callback(responseObj); }, undefined, - { method: 'GET', withCredentials: true } + { + method: 'GET', + withCredentials: true + } ); }; @@ -210,4 +238,4 @@ export const lotameIdSubmodule = { }, }; -submodule('userId', lotameIdSubmodule); +submodule('userId', lotamePanoramaIdSubmodule); diff --git a/modules/lotameIdSystem.md b/modules/lotamePanoramaIdSystem.md similarity index 64% rename from modules/lotameIdSystem.md rename to modules/lotamePanoramaIdSystem.md index c85c9abd878..e960f4b5695 100644 --- a/modules/lotameIdSystem.md +++ b/modules/lotamePanoramaIdSystem.md @@ -1,14 +1,14 @@ # Overview ``` -Module Name: Lotame Id System +Module Name: Lotame Panorama Id System Module Type: Id System Maintainer: prebid@lotame.com ``` # Description -Retrieve Lotame's Id +Retrieve the Lotame Panorama Id # Usage @@ -18,7 +18,7 @@ Retrieve Lotame's Id usersync: { userIds: [ { - name: 'lotameId' // The only parameter that is needed + name: 'lotamePanoramaId' // The only parameter that is needed }], } }); diff --git a/test/spec/modules/lotameIdSystem_spec.js b/test/spec/modules/lotamePanoramaIdSystem_spec.js similarity index 80% rename from test/spec/modules/lotameIdSystem_spec.js rename to test/spec/modules/lotamePanoramaIdSystem_spec.js index 30e6a05df21..2bc0f428754 100644 --- a/test/spec/modules/lotameIdSystem_spec.js +++ b/test/spec/modules/lotamePanoramaIdSystem_spec.js @@ -1,4 +1,7 @@ -import {lotameIdSubmodule, storage} from 'modules/lotameIdSystem.js'; +import { + lotamePanoramaIdSubmodule, + storage, +} from 'modules/lotamePanoramaIdSystem.js'; import * as utils from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; @@ -43,7 +46,7 @@ describe('LotameId', function() { let callBackSpy = sinon.spy(); this.beforeEach(function() { - let submoduleCallback = lotameIdSubmodule.getId({}).callback; + let submoduleCallback = lotamePanoramaIdSubmodule.getId({}).callback; submoduleCallback(callBackSpy); request = server.requests[0]; @@ -53,7 +56,7 @@ describe('LotameId', function() { responseHeader, JSON.stringify({ profile_id: '4ec137245858469eb94a4e248f238694', - expiry_ms: 10, + expiry_ts: 10, core_id: 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', }) @@ -61,19 +64,11 @@ describe('LotameId', function() { }); it('should call the remote server when getId is called', function () { - expect(request.url).to.be.eq( - 'https://mconrad.dev.lotame.com:5555/id' - ); + expect(request.url).to.be.eq('https://bcp.dev.lotame.com/id'); expect(callBackSpy.calledOnce).to.be.true; }); - it('should save the last update date', function () { - // Check the calls to storage - sinon.assert.calledWith(setLocalStorageStub, '_lota_last'); - sinon.assert.calledWith(setCookieStub, '_lota_last'); - }); - it('should save the first party id', function () { sinon.assert.calledWith( setLocalStorageStub, @@ -90,24 +85,24 @@ describe('LotameId', function() { it('should save the expiry', function () { sinon.assert.calledWith( setLocalStorageStub, - '_lota_expiry', 10); + 'panoramaId_expiry', 10); sinon.assert.calledWith( setCookieStub, - '_lota_expiry', 10 + 'panoramaId_expiry', 10 ); }); it('should save the id', function () { sinon.assert.calledWith( setLocalStorageStub, - '_lota_pano', + 'panoramaId', 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a' ); sinon.assert.calledWith( setCookieStub, - '_lota_pano', + 'panoramaId', 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a' ); }); @@ -117,14 +112,14 @@ describe('LotameId', function() { let submoduleCallback; this.beforeEach(function () { - getLocalStorageStub.withArgs('_lota_expiry').returns(Date.now() + 100000); + getLocalStorageStub.withArgs('panoramaId_expiry').returns(Date.now() + 100000); getCookieStub - .withArgs('_lota_pano') + .withArgs('panoramaId') .returns( 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a' ); - submoduleCallback = lotameIdSubmodule.getId({}); + submoduleCallback = lotamePanoramaIdSubmodule.getId({}); }); it('should not call the remote server when getId is called', function () { @@ -140,15 +135,15 @@ describe('LotameId', function() { this.beforeEach(function () { getLocalStorageStub - .withArgs('_lota_expiry') + .withArgs('panoramaId_expiry') .returns(1000); getLocalStorageStub - .withArgs('_lota_pano') + .withArgs('panoramaId') .returns( 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a' ); - let submoduleCallback = lotameIdSubmodule.getId({}).callback; + let submoduleCallback = lotamePanoramaIdSubmodule.getId({}).callback; submoduleCallback(callBackSpy); request = server.requests[0]; @@ -160,7 +155,7 @@ describe('LotameId', function() { profile_id: '4ec137245858469eb94a4e248f238694', core_id: 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', - expiry_ms: 3600000 + expiry_ts: 3600000 }) ); }); @@ -175,7 +170,7 @@ describe('LotameId', function() { let callBackSpy = sinon.spy(); this.beforeEach(function () { - let submoduleCallback = lotameIdSubmodule.getId({}, { + let submoduleCallback = lotamePanoramaIdSubmodule.getId({}, { gdprApplies: true, consentString: 'consentGiven' }).callback; @@ -190,7 +185,7 @@ describe('LotameId', function() { profile_id: '4ec137245858469eb94a4e248f238694', core_id: 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', - expiry_ms: 3600000, + expiry_ts: 3600000, }) ); }); @@ -201,13 +196,15 @@ describe('LotameId', function() { it('should pass the gdpr consent string back', function() { expect(request.url).to.be.eq( - 'https://mconrad.dev.lotame.com:5555/id?gdpr_consent=consentGiven' + 'https://bcp.dev.lotame.com/id?gdpr_consent=consentGiven' ); }); }); it('should retrieve the id when decode is called', function() { - var id = lotameIdSubmodule.decode('1234'); - expect(id).to.be.eql('1234'); + var id = lotamePanoramaIdSubmodule.decode('1234'); + expect(id).to.be.eql({ + 'lotamePanoramaId': '1234' + }); }); }); From f5532baf9153b346b4488a47ea23a4057122bd4b Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Thu, 4 Jun 2020 11:04:18 -0400 Subject: [PATCH 08/15] Fix tests, numbers vs strings....; Add gdpr_applies query param --- modules/lotamePanoramaIdSystem.js | 16 +- .../modules/lotamePanoramaIdSystem_spec.js | 254 +++++++++++++++--- 2 files changed, 220 insertions(+), 50 deletions(-) diff --git a/modules/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js index 99230ddba28..a9283ee508d 100644 --- a/modules/lotamePanoramaIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -177,18 +177,18 @@ export const lotamePanoramaIdSubmodule = { } const storedUserId = getProfileId(); - const hasGdprData = - consentData && - utils.isBoolean(consentData.gdprApplies) && - consentData.gdprApplies; + const resolveIdFunction = function (callback) { let queryParams = {}; if (storedUserId) { queryParams.fp = storedUserId } - if (hasGdprData) { - queryParams.gdpr_consent = consentData.consentString; + if (consentData && utils.isBoolean(consentData.gdprApplies)) { + queryParams.gdpr_applies = consentData.gdprApplies; + if (consentData.gdprApplies) { + queryParams.gdpr_consent = consentData.consentString; + } } const url = utils.buildUrl({ protocol: 'https', @@ -205,7 +205,7 @@ export const lotamePanoramaIdSubmodule = { responseObj = JSON.parse(response); saveLotameCache(KEY_EXPIRY, responseObj.expiry_ts); - if (utils.isStr(responseObj.core_id)) { + if (!utils.isEmptyStr(responseObj.core_id)) { saveLotameCache( KEY_ID, responseObj.core_id, @@ -215,7 +215,7 @@ export const lotamePanoramaIdSubmodule = { clearLotameCache(KEY_ID); } - if (utils.isStr(responseObj.profile_id)) { + if (!utils.isEmptyStr(responseObj.profile_id)) { setProfileId(responseObj.profile_id); } else { clearLotameCache(KEY_PROFILE); diff --git a/test/spec/modules/lotamePanoramaIdSystem_spec.js b/test/spec/modules/lotamePanoramaIdSystem_spec.js index 2bc0f428754..1741936d16a 100644 --- a/test/spec/modules/lotamePanoramaIdSystem_spec.js +++ b/test/spec/modules/lotamePanoramaIdSystem_spec.js @@ -45,7 +45,7 @@ describe('LotameId', function() { let request; let callBackSpy = sinon.spy(); - this.beforeEach(function() { + beforeEach(function() { let submoduleCallback = lotamePanoramaIdSubmodule.getId({}).callback; submoduleCallback(callBackSpy); @@ -108,60 +108,230 @@ describe('LotameId', function() { }); }); - describe('a non-expired id found', function () { - let submoduleCallback; + describe('No stored values', function() { + describe('and receives the profile id but no panorama id', function() { + let request; + let callBackSpy = sinon.spy(); + + beforeEach(function() { + let submoduleCallback = lotamePanoramaIdSubmodule.getId({}).callback; + submoduleCallback(callBackSpy); + request = server.requests[0]; + + request.respond( + 200, + responseHeader, + JSON.stringify({ + profile_id: '4ec137245858469eb94a4e248f238694', + expiry_ts: 3800000, + }) + ); + }); + + it('should save the profile id', function() { + sinon.assert.calledWith( + setLocalStorageStub, + '_cc_id', + '4ec137245858469eb94a4e248f238694' + ); + sinon.assert.calledWith( + setCookieStub, + '_cc_id', + '4ec137245858469eb94a4e248f238694' + ); + }); - this.beforeEach(function () { - getLocalStorageStub.withArgs('panoramaId_expiry').returns(Date.now() + 100000); - getCookieStub - .withArgs('panoramaId') - .returns( - 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a' + it('should save the panorama id expiry', function () { + sinon.assert.calledWith( + setLocalStorageStub, + 'panoramaId_expiry', + 3800000 ); - submoduleCallback = lotamePanoramaIdSubmodule.getId({}); + sinon.assert.calledWith(setCookieStub, 'panoramaId_expiry', 3800000); + }); + + it('should NOT save the panorama id', function () { + sinon.assert.neverCalledWith( + setLocalStorageStub, + 'panoramaId', + sinon.match.any + ); + + sinon.assert.neverCalledWith(setCookieStub, 'panoramaId', sinon.match.any); + }); }); - it('should not call the remote server when getId is called', function () { - expect(submoduleCallback).to.be.eql({ - id: 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', + describe('and receives both the profile id and the panorama id', function () { + let request; + let callBackSpy = sinon.spy(); + + beforeEach(function () { + let submoduleCallback = lotamePanoramaIdSubmodule.getId({}).callback; + submoduleCallback(callBackSpy); + request = server.requests[0]; + + request.respond( + 200, + responseHeader, + JSON.stringify({ + profile_id: '4ec137245858469eb94a4e248f238694', + core_id: + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87b', + expiry_ts: 3600000, + }) + ); + }); + it('should save the profile id', function () { + sinon.assert.calledWith( + setLocalStorageStub, + '_cc_id', + '4ec137245858469eb94a4e248f238694' + ); + sinon.assert.calledWith( + setCookieStub, + '_cc_id', + '4ec137245858469eb94a4e248f238694' + ); + }); + + it('should save the panorama id expiry', function () { + sinon.assert.calledWith( + setLocalStorageStub, + 'panoramaId_expiry', + 3600000 + ); + + sinon.assert.calledWith(setCookieStub, 'panoramaId_expiry', 3600000); + }); + + it('should save the panorama id', function () { + sinon.assert.calledWith( + setLocalStorageStub, + 'panoramaId', + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87b' + ); + + sinon.assert.calledWith( + setCookieStub, + 'panoramaId', + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87b' + ); }); }); }); - describe('an expired id found', function () { - let request; - let callBackSpy = sinon.spy(); + describe('With a panorama id found', function() { + describe('and it is too early to try again', function () { + let submoduleCallback; + + beforeEach(function () { + getLocalStorageStub.withArgs('panoramaId_expiry').returns(Date.now() + 100000 + ''); + getCookieStub + .withArgs('panoramaId') + .returns( + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87c' + ); - this.beforeEach(function () { - getLocalStorageStub - .withArgs('panoramaId_expiry') - .returns(1000); - getLocalStorageStub - .withArgs('panoramaId') - .returns( - 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a' + submoduleCallback = lotamePanoramaIdSubmodule.getId({}); + }); + + it('should not call the remote server when getId is called', function () { + expect(submoduleCallback).to.be.eql({ + id: 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87c', + }); + }); + }); + + describe('and can try again', function () { + let request; + let callBackSpy = sinon.spy(); + + beforeEach(function () { + getLocalStorageStub + .withArgs('panoramaId_expiry') + .returns('1000'); + getLocalStorageStub + .withArgs('panoramaId') + .returns( + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87d' + ); + + let submoduleCallback = lotamePanoramaIdSubmodule.getId({}).callback; + submoduleCallback(callBackSpy); + + request = server.requests[0]; + + request.respond( + 200, + responseHeader, + JSON.stringify({ + profile_id: '4ec137245858469eb94a4e248f238694', + core_id: + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87d', + expiry_ts: 3600000 + }) ); + }); - let submoduleCallback = lotamePanoramaIdSubmodule.getId({}).callback; - submoduleCallback(callBackSpy); + it('should call the remote server when getId is called', function () { + expect(callBackSpy.calledOnce).to.be.true; + }); + }); + }); - request = server.requests[0]; + describe('With no panorama id found', function() { + beforeEach(function() { + getCookieStub.withArgs('panoramaId').returns(null); + getLocalStorageStub.withArgs('panoramaId').returns(null); + }) + describe('and it is too early to try again', function () { + let submoduleCallback; - request.respond( - 200, - responseHeader, - JSON.stringify({ - profile_id: '4ec137245858469eb94a4e248f238694', - core_id: - 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', - expiry_ts: 3600000 - }) - ); + beforeEach(function () { + getCookieStub + .withArgs('panoramaId_expiry') + .returns(Date.now() + 100000 + ''); + + submoduleCallback = lotamePanoramaIdSubmodule.getId({}); + }); + + it('should not call the remote server when getId is called', function () { + expect(submoduleCallback).to.be.eql({ + id: null + }); + }); }); - it('should call the remote server when getId is called', function () { - expect(callBackSpy.calledOnce).to.be.true; + describe('and can try again', function () { + let request; + let callBackSpy = sinon.spy(); + + beforeEach(function () { + getLocalStorageStub + .withArgs('panoramaId_expiry') + .returns('1000'); + + let submoduleCallback = lotamePanoramaIdSubmodule.getId({}).callback; + submoduleCallback(callBackSpy); + + request = server.requests[0]; + + request.respond( + 200, + responseHeader, + JSON.stringify({ + profile_id: '4ec137245858469eb94a4e248f238694', + core_id: + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87e', + expiry_ts: 3600000 + }) + ); + }); + + it('should call the remote server when getId is called', function () { + expect(callBackSpy.calledOnce).to.be.true; + }); }); }); @@ -169,7 +339,7 @@ describe('LotameId', function() { let request; let callBackSpy = sinon.spy(); - this.beforeEach(function () { + beforeEach(function () { let submoduleCallback = lotamePanoramaIdSubmodule.getId({}, { gdprApplies: true, consentString: 'consentGiven' @@ -184,7 +354,7 @@ describe('LotameId', function() { JSON.stringify({ profile_id: '4ec137245858469eb94a4e248f238694', core_id: - 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87a', + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87f', expiry_ts: 3600000, }) ); @@ -196,7 +366,7 @@ describe('LotameId', function() { it('should pass the gdpr consent string back', function() { expect(request.url).to.be.eq( - 'https://bcp.dev.lotame.com/id?gdpr_consent=consentGiven' + 'https://bcp.dev.lotame.com/id?gdpr_applies=true&gdpr_consent=consentGiven' ); }); }); From 8231cc6bb9c7af9bbf6bc4898ce86f182895980f Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Thu, 4 Jun 2020 17:05:48 -0400 Subject: [PATCH 09/15] Fix the timestamp becoming an invalid date --- modules/lotamePanoramaIdSystem.js | 11 ++++++----- test/spec/modules/lotamePanoramaIdSystem_spec.js | 6 ++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/modules/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js index a9283ee508d..976ba2d68bf 100644 --- a/modules/lotamePanoramaIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -93,7 +93,11 @@ function saveLotameCache( ); } if (storage.hasLocalStorage()) { - storage.setDataInLocalStorage(`${key}_exp`, expirationDate, undefined); + storage.setDataInLocalStorage( + `${key}_exp`, + String(expirationTimestamp), + undefined + ); storage.setDataInLocalStorage(key, value, undefined); } } @@ -165,10 +169,7 @@ export const lotamePanoramaIdSubmodule = { getId(configParams, consentData, cacheIdObj) { let localCache = getLotameLocalCache(); - let refreshNeeded = false; - if (!utils.isStr(localCache.data)) { - refreshNeeded = Date.now() > localCache.expiryTimestampMs; - } + let refreshNeeded = Date.now() > localCache.expiryTimestampMs; if (!refreshNeeded) { return { diff --git a/test/spec/modules/lotamePanoramaIdSystem_spec.js b/test/spec/modules/lotamePanoramaIdSystem_spec.js index 1741936d16a..d8c98fa74ca 100644 --- a/test/spec/modules/lotamePanoramaIdSystem_spec.js +++ b/test/spec/modules/lotamePanoramaIdSystem_spec.js @@ -226,7 +226,9 @@ describe('LotameId', function() { let submoduleCallback; beforeEach(function () { - getLocalStorageStub.withArgs('panoramaId_expiry').returns(Date.now() + 100000 + ''); + getCookieStub + .withArgs('panoramaId_expiry') + .returns(String(Date.now() + 100000)); getCookieStub .withArgs('panoramaId') .returns( @@ -291,7 +293,7 @@ describe('LotameId', function() { beforeEach(function () { getCookieStub .withArgs('panoramaId_expiry') - .returns(Date.now() + 100000 + ''); + .returns(String(Date.now() + 100000)); submoduleCallback = lotamePanoramaIdSubmodule.getId({}); }); From 627633b24e323391b76c4f90c248bf5f62f33c82 Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Fri, 5 Jun 2020 10:26:48 -0400 Subject: [PATCH 10/15] Clear out the panorama id when on optout --- modules/lotamePanoramaIdSystem.js | 5 +- .../modules/lotamePanoramaIdSystem_spec.js | 77 +++++++++++++++++-- 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/modules/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js index 976ba2d68bf..173c7df87b9 100644 --- a/modules/lotamePanoramaIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -206,7 +206,7 @@ export const lotamePanoramaIdSubmodule = { responseObj = JSON.parse(response); saveLotameCache(KEY_EXPIRY, responseObj.expiry_ts); - if (!utils.isEmptyStr(responseObj.core_id)) { + if (utils.isStr(responseObj.core_id)) { saveLotameCache( KEY_ID, responseObj.core_id, @@ -216,10 +216,11 @@ export const lotamePanoramaIdSubmodule = { clearLotameCache(KEY_ID); } - if (!utils.isEmptyStr(responseObj.profile_id)) { + if (utils.isStr(responseObj.profile_id)) { setProfileId(responseObj.profile_id); } else { clearLotameCache(KEY_PROFILE); + clearLotameCache(KEY_ID); } } catch (error) { utils.logError(error); diff --git a/test/spec/modules/lotamePanoramaIdSystem_spec.js b/test/spec/modules/lotamePanoramaIdSystem_spec.js index d8c98fa74ca..85917505b84 100644 --- a/test/spec/modules/lotamePanoramaIdSystem_spec.js +++ b/test/spec/modules/lotamePanoramaIdSystem_spec.js @@ -158,7 +158,18 @@ describe('LotameId', function() { sinon.match.any ); - sinon.assert.neverCalledWith(setCookieStub, 'panoramaId', sinon.match.any); + sinon.assert.calledWith( + removeFromLocalStorageStub, + 'panoramaId' + ); + + sinon.assert.calledWith( + setCookieStub, + 'panoramaId', + '', + 'Thu, 01 Jan 1970 00:00:00 GMT', + 'Lax' + ); }); }); @@ -250,10 +261,8 @@ describe('LotameId', function() { let callBackSpy = sinon.spy(); beforeEach(function () { - getLocalStorageStub - .withArgs('panoramaId_expiry') - .returns('1000'); - getLocalStorageStub + getCookieStub.withArgs('panoramaId_expiry').returns('1000'); + getCookieStub .withArgs('panoramaId') .returns( 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87d' @@ -280,6 +289,64 @@ describe('LotameId', function() { expect(callBackSpy.calledOnce).to.be.true; }); }); + + describe('receives an optout request', function () { + let request; + let callBackSpy = sinon.spy(); + + beforeEach(function () { + getCookieStub.withArgs('panoramaId_expiry').returns('1000'); + getCookieStub + .withArgs('panoramaId') + .returns( + 'ca22992567e3cd4d116a5899b88a55d0d857a23610db939ae6ac13ba2335d87d' + ); + + let submoduleCallback = lotamePanoramaIdSubmodule.getId({}).callback; + submoduleCallback(callBackSpy); + + request = server.requests[0]; + + request.respond( + 200, + responseHeader, + JSON.stringify({ + expiry_ts: Date.now() + (30 * 24 * 60 * 60 * 1000), + }) + ); + }); + + it('should call the remote server when getId is called', function () { + expect(callBackSpy.calledOnce).to.be.true; + }); + + it('should clear the panorama id', function () { + sinon.assert.calledWith( + removeFromLocalStorageStub, + 'panoramaId' + ); + + sinon.assert.calledWith( + setCookieStub, + 'panoramaId', + '', + 'Thu, 01 Jan 1970 00:00:00 GMT', + 'Lax' + ); + }); + + it('should clear the profile id', function () { + sinon.assert.calledWith(removeFromLocalStorageStub, '_cc_id'); + + sinon.assert.calledWith( + setCookieStub, + '_cc_id', + '', + 'Thu, 01 Jan 1970 00:00:00 GMT', + 'Lax' + ); + }); + }); }); describe('With no panorama id found', function() { From beba5112e8de64053427b18752310885bc4de36a Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Mon, 8 Jun 2020 11:44:14 -0400 Subject: [PATCH 11/15] Add eid support --- modules/.submodules.json | 1 + modules/userId/eids.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/modules/.submodules.json b/modules/.submodules.json index ce3eb8fa137..e6c74e934a2 100644 --- a/modules/.submodules.json +++ b/modules/.submodules.json @@ -7,6 +7,7 @@ "parrableIdSystem", "britepoolIdSystem", "liveIntentIdSystem", + "lotamePanoramaId", "criteoIdSystem", "netIdSystem", "identityLinkIdSystem" diff --git a/modules/userId/eids.js b/modules/userId/eids.js index 5ca9e40866b..c880c2b1797 100644 --- a/modules/userId/eids.js +++ b/modules/userId/eids.js @@ -62,6 +62,12 @@ const USER_IDS_CONFIG = { atype: 1 }, + // lotamePanoramaId + lotamePanoramaId: { + source: 'crwdcntrl.net', + atype: 1, + }, + // DigiTrust 'digitrustid': { getValue: function(data) { From be46374c625774bd9ab69b30d35011bfe5531111 Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Wed, 17 Jun 2020 11:19:48 -0400 Subject: [PATCH 12/15] Switch to the prod version of the url --- modules/lotamePanoramaIdSystem.js | 2 +- test/spec/modules/lotamePanoramaIdSystem_spec.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js index 173c7df87b9..beaad46d8b3 100644 --- a/modules/lotamePanoramaIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -193,7 +193,7 @@ export const lotamePanoramaIdSubmodule = { } const url = utils.buildUrl({ protocol: 'https', - host: `bcp.dev.lotame.com`, + host: `id.crwdcntrl.net`, pathname: '/id', search: utils.isEmpty(queryParams) ? undefined : queryParams, }); diff --git a/test/spec/modules/lotamePanoramaIdSystem_spec.js b/test/spec/modules/lotamePanoramaIdSystem_spec.js index 85917505b84..4bb961ee0b6 100644 --- a/test/spec/modules/lotamePanoramaIdSystem_spec.js +++ b/test/spec/modules/lotamePanoramaIdSystem_spec.js @@ -64,7 +64,7 @@ describe('LotameId', function() { }); it('should call the remote server when getId is called', function () { - expect(request.url).to.be.eq('https://bcp.dev.lotame.com/id'); + expect(request.url).to.be.eq('https://id.crwdcntrl.net/id'); expect(callBackSpy.calledOnce).to.be.true; }); @@ -435,7 +435,7 @@ describe('LotameId', function() { it('should pass the gdpr consent string back', function() { expect(request.url).to.be.eq( - 'https://bcp.dev.lotame.com/id?gdpr_applies=true&gdpr_consent=consentGiven' + 'https://id.crwdcntrl.net/id?gdpr_applies=true&gdpr_consent=consentGiven' ); }); }); From 1c5e954ed80f0b7cd2f1be7e158242fbf1e27ab3 Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Wed, 17 Jun 2020 16:40:23 -0400 Subject: [PATCH 13/15] Update test wording --- test/spec/modules/lotamePanoramaIdSystem_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/lotamePanoramaIdSystem_spec.js b/test/spec/modules/lotamePanoramaIdSystem_spec.js index 4bb961ee0b6..c6d3383374a 100644 --- a/test/spec/modules/lotamePanoramaIdSystem_spec.js +++ b/test/spec/modules/lotamePanoramaIdSystem_spec.js @@ -404,7 +404,7 @@ describe('LotameId', function() { }); }); - describe('gdpr applies', function () { + describe('when gdpr applies', function () { let request; let callBackSpy = sinon.spy(); From 74bf928a835005c8e9816540cbffa828bd465ddb Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Thu, 18 Jun 2020 15:10:11 -0400 Subject: [PATCH 14/15] From PR feedback Only care about the core_id if a profile_id is also returned so it won't look like we can store a core_id and then promptly delete it Return just the core_id from getId() --- modules/lotamePanoramaIdSystem.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/modules/lotamePanoramaIdSystem.js b/modules/lotamePanoramaIdSystem.js index beaad46d8b3..cdf9131dd68 100644 --- a/modules/lotamePanoramaIdSystem.js +++ b/modules/lotamePanoramaIdSystem.js @@ -200,24 +200,25 @@ export const lotamePanoramaIdSubmodule = { ajax( url, (response) => { - let responseObj = {}; + let coreId; if (response) { try { - responseObj = JSON.parse(response); + let responseObj = JSON.parse(response); saveLotameCache(KEY_EXPIRY, responseObj.expiry_ts); - if (utils.isStr(responseObj.core_id)) { - saveLotameCache( - KEY_ID, - responseObj.core_id, - responseObj.expiry_ts - ); - } else { - clearLotameCache(KEY_ID); - } - if (utils.isStr(responseObj.profile_id)) { setProfileId(responseObj.profile_id); + + if (utils.isStr(responseObj.core_id)) { + saveLotameCache( + KEY_ID, + responseObj.core_id, + responseObj.expiry_ts + ); + coreId = responseObj.core_id; + } else { + clearLotameCache(KEY_ID); + } } else { clearLotameCache(KEY_PROFILE); clearLotameCache(KEY_ID); @@ -226,7 +227,7 @@ export const lotamePanoramaIdSubmodule = { utils.logError(error); } } - callback(responseObj); + callback(coreId); }, undefined, { From 33b026892dbe0f3c0a552f2014522b36412aa93c Mon Sep 17 00:00:00 2001 From: Mark Conrad Date: Fri, 26 Jun 2020 15:02:07 -0400 Subject: [PATCH 15/15] Add eid test for lotamePanoramaId --- test/spec/modules/eids_spec.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/spec/modules/eids_spec.js b/test/spec/modules/eids_spec.js index 160277204df..1cbc2911ef5 100644 --- a/test/spec/modules/eids_spec.js +++ b/test/spec/modules/eids_spec.js @@ -107,6 +107,18 @@ describe('eids array generation for known sub-modules', function() { }); }); + it('lotamePanoramaId', function () { + const userId = { + lotamePanoramaId: 'some-random-id-value', + }; + const newEids = createEidsArray(userId); + expect(newEids.length).to.equal(1); + expect(newEids[0]).to.deep.equal({ + source: 'crwdcntrl.net', + uids: [{ id: 'some-random-id-value', atype: 1 }], + }); + }); + it('DigiTrust; getValue call', function() { const userId = { digitrustid: {