From c477466ce51292a8763695a5fb2062344d4f3a13 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Wed, 17 Sep 2025 11:13:27 -0400 Subject: [PATCH 1/5] feat: added DisabledVault to disable id-cache when noFunctional is set --- src/mp-instance.ts | 11 ++-- src/vault.ts | 16 ++++++ test/src/tests-identity.ts | 30 +++++++++++ test/src/vault.spec.ts | 100 ++++++++++++++++++++++++++++++++++++- 4 files changed, 152 insertions(+), 5 deletions(-) diff --git a/src/mp-instance.ts b/src/mp-instance.ts index a3739bddd..0a3947575 100644 --- a/src/mp-instance.ts +++ b/src/mp-instance.ts @@ -37,7 +37,7 @@ import KitBlocker from './kitBlocking'; import ConfigAPIClient, { IKitConfigs } from './configAPIClient'; import IdentityAPIClient from './identityApiClient'; import { isFunction, parseConfig } from './utils'; -import { LocalStorageVault } from './vault'; +import { DisabledVault, LocalStorageVault } from './vault'; import { removeExpiredIdentityCacheDates } from './identity-utils'; import IntegrationCapture from './integrationCapture'; import { IPreInit, processReadyQueue } from './pre-init-utils'; @@ -1523,9 +1523,12 @@ function createKitBlocker(config, mpInstance) { } function createIdentityCache(mpInstance) { - return new LocalStorageVault(`${mpInstance._Store.storageName}-id-cache`, { - logger: mpInstance.Logger, - }); + const cacheKey = `${mpInstance._Store.storageName}-id-cache`; + if (mpInstance._Store.getNoFunctional()) { + console.log('createIdentityCache: noFunctional is true'); + return new DisabledVault(cacheKey, { logger: mpInstance.Logger }); + } + return new LocalStorageVault(cacheKey, { logger: mpInstance.Logger }); } function runPreConfigFetchInitialization(mpInstance, apiKey, config) { diff --git a/src/vault.ts b/src/vault.ts index 57d2a6dc1..eb0bcafbd 100644 --- a/src/vault.ts +++ b/src/vault.ts @@ -102,4 +102,20 @@ export class SessionStorageVault extends BaseVault { constructor(storageKey: string, options?: IVaultOptions) { super(storageKey, window.sessionStorage, options); } +} + +// DisabledVault is used when persistence is disabled by privacy flags. +export class DisabledVault extends BaseVault { + constructor(storageKey: string, options?: IVaultOptions) { + super(storageKey, window.localStorage, options); + this.contents = null; + } + + public store(_item: StorableItem): void { + this.contents = null; + } + + public retrieve(): StorableItem | null { + return this.contents; + } } \ No newline at end of file diff --git a/test/src/tests-identity.ts b/test/src/tests-identity.ts index 532898beb..b26203bf4 100644 --- a/test/src/tests-identity.ts +++ b/test/src/tests-identity.ts @@ -642,6 +642,36 @@ describe('identity', function() { }); }); + describe('privacy flags', function() { + beforeEach(function() { + mParticle._resetForTests(MPConfig); + mParticle.config.flags.cacheIdentity = 'True'; + localStorage.clear(); + }); + + describe('#createIdentityCache', function() { + it('should use LocalStorageVault when noFunctional is false by default', async () => { + mParticle.init(apiKey, window.mParticle.config); + await waitForCondition(hasIdentifyReturned); + expect(localStorage.getItem('mprtcl-v4_abcdef-id-cache')).to.be.ok; + }); + + it('should use DisabledVault when noFunctional is true', async () => { + mParticle.config.noFunctional = true; + mParticle.init(apiKey, window.mParticle.config); + await waitForCondition(hasIdentifyReturned); + expect(localStorage.getItem('mprtcl-v4_abcdef-id-cache')).not.to.be.ok; + }); + + it('should use LocalStorageVault when noFunctional is false', async () => { + mParticle.config.noFunctional = false; + mParticle.init(apiKey, window.mParticle.config); + await waitForCondition(hasIdentifyReturned); + expect(localStorage.getItem('mprtcl-v4_abcdef-id-cache')).to.be.ok; + }); + }); + }); + // https://go.mparticle.com/work/SQDSDKS-6849 // This test passes with no issue when it is run on its own, but fails when tests-forwarders.js are also ran. it('should respect consent rules on consent-change', async () => { diff --git a/test/src/vault.spec.ts b/test/src/vault.spec.ts index c9ee53085..f0bbbfa2d 100644 --- a/test/src/vault.spec.ts +++ b/test/src/vault.spec.ts @@ -1,7 +1,7 @@ import { Batch } from '@mparticle/event-models'; import { expect } from 'chai'; import { Dictionary } from '../../src/utils'; -import { SessionStorageVault, LocalStorageVault } from '../../src/vault'; +import { SessionStorageVault, LocalStorageVault, DisabledVault } from '../../src/vault'; const testObject: Dictionary> = { foo: { foo: 'bar', buzz: 'bazz' }, @@ -419,4 +419,102 @@ describe('Vault', () => { ); }); }); + + describe('DisabledVault', () => { + afterEach(() => { + window.localStorage.clear(); + }); + + describe('#store', () => { + it('should NOT write to localStorage', () => { + const storageKey = 'test-disabled-store-empty'; + const vault = new DisabledVault>>( + storageKey, + ); + + vault.store(testObject); + + expect(vault.contents).to.equal(null); + expect(window.localStorage.getItem(storageKey)).to.equal(null); + }); + + it('should NOT overwrite existing localStorage value and keep contents null', () => { + const storageKey = 'test-disabled-store-existing'; + window.localStorage.setItem( + storageKey, + JSON.stringify(testObject), + ); + + const vault = new DisabledVault>>( + storageKey, + ); + + vault.store({ pinky: { narf: 'poit' } }); + + expect(vault.contents).to.equal(null); + expect(window.localStorage.getItem(storageKey)).to.equal( + JSON.stringify(testObject), + ); + }); + }); + + describe('#retrieve', () => { + it('should return null when nothing is stored', () => { + const storageKey = 'test-disabled-retrieve-empty'; + const vault = new DisabledVault>>( + storageKey, + ); + const retrievedItem = vault.retrieve(); + expect(retrievedItem).to.equal(null); + }); + + it('should return null even if localStorage has a value', () => { + const storageKey = 'test-disabled-retrieve-existing'; + window.localStorage.setItem( + storageKey, + JSON.stringify(testObject), + ); + + const vault = new DisabledVault>>( + storageKey, + ); + const retrievedItem = vault.retrieve(); + expect(retrievedItem).to.equal(null); + expect(window.localStorage.getItem(storageKey)).to.equal( + JSON.stringify(testObject), + ); + }); + }); + + describe('#purge', () => { + it('should keep contents null when purging', () => { + const storageKey = 'test-disabled-purge-existing'; + window.localStorage.setItem( + storageKey, + JSON.stringify(testObject), + ); + + const vault = new DisabledVault>>( + storageKey, + ); + + vault.purge(); + + expect(vault.contents).to.equal(null); + expect(window.localStorage.getItem(storageKey)).to.equal(null); + }); + + it('should keep contents null when purging an empty key', () => { + const storageKey = 'test-disabled-purge-empty'; + const vault = new DisabledVault>>( + storageKey, + ); + + vault.purge(); + console.log(window.localStorage.getItem(storageKey)); + expect(vault.contents).to.equal(null); + expect(window.localStorage.getItem(storageKey)).to.equal(null); + }); + }); + }); }); \ No newline at end of file From 288e25900fd401db4835877eb35422622ec85456 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Fri, 19 Sep 2025 16:09:40 -0400 Subject: [PATCH 2/5] test: updated tests for DisabledVault --- test/src/tests-identity.ts | 6 ++--- test/src/vault.spec.ts | 52 ++++++++++---------------------------- 2 files changed, 16 insertions(+), 42 deletions(-) diff --git a/test/src/tests-identity.ts b/test/src/tests-identity.ts index b26203bf4..3e1e41d4f 100644 --- a/test/src/tests-identity.ts +++ b/test/src/tests-identity.ts @@ -650,20 +650,20 @@ describe('identity', function() { }); describe('#createIdentityCache', function() { - it('should use LocalStorageVault when noFunctional is false by default', async () => { + it('should save id cache to local storage when noFunctional is false by default', async () => { mParticle.init(apiKey, window.mParticle.config); await waitForCondition(hasIdentifyReturned); expect(localStorage.getItem('mprtcl-v4_abcdef-id-cache')).to.be.ok; }); - it('should use DisabledVault when noFunctional is true', async () => { + it('should NOT save id cache to local storage when noFunctional is true', async () => { mParticle.config.noFunctional = true; mParticle.init(apiKey, window.mParticle.config); await waitForCondition(hasIdentifyReturned); expect(localStorage.getItem('mprtcl-v4_abcdef-id-cache')).not.to.be.ok; }); - it('should use LocalStorageVault when noFunctional is false', async () => { + it('should save id cache to local storage when noFunctional is false', async () => { mParticle.config.noFunctional = false; mParticle.init(apiKey, window.mParticle.config); await waitForCondition(hasIdentifyReturned); diff --git a/test/src/vault.spec.ts b/test/src/vault.spec.ts index f0bbbfa2d..d264327ce 100644 --- a/test/src/vault.spec.ts +++ b/test/src/vault.spec.ts @@ -428,11 +428,9 @@ describe('Vault', () => { describe('#store', () => { it('should NOT write to localStorage', () => { const storageKey = 'test-disabled-store-empty'; - const vault = new DisabledVault>>( - storageKey, - ); + const vault = new DisabledVault(storageKey); - vault.store(testObject); + vault.store('testString'); expect(vault.contents).to.equal(null); expect(window.localStorage.getItem(storageKey)).to.equal(null); @@ -440,63 +438,42 @@ describe('Vault', () => { it('should NOT overwrite existing localStorage value and keep contents null', () => { const storageKey = 'test-disabled-store-existing'; - window.localStorage.setItem( - storageKey, - JSON.stringify(testObject), - ); + window.localStorage.setItem(storageKey, 'existingItem'); - const vault = new DisabledVault>>( - storageKey, - ); + const vault = new DisabledVault(storageKey); - vault.store({ pinky: { narf: 'poit' } }); + vault.store('newValue'); expect(vault.contents).to.equal(null); - expect(window.localStorage.getItem(storageKey)).to.equal( - JSON.stringify(testObject), - ); + expect(window.localStorage.getItem(storageKey)).to.equal('existingItem'); }); }); describe('#retrieve', () => { it('should return null when nothing is stored', () => { const storageKey = 'test-disabled-retrieve-empty'; - const vault = new DisabledVault>>( - storageKey, - ); + const vault = new DisabledVault(storageKey); const retrievedItem = vault.retrieve(); expect(retrievedItem).to.equal(null); }); it('should return null even if localStorage has a value', () => { const storageKey = 'test-disabled-retrieve-existing'; - window.localStorage.setItem( - storageKey, - JSON.stringify(testObject), - ); + window.localStorage.setItem(storageKey, 'existingItem'); - const vault = new DisabledVault>>( - storageKey, - ); + const vault = new DisabledVault(storageKey); const retrievedItem = vault.retrieve(); expect(retrievedItem).to.equal(null); - expect(window.localStorage.getItem(storageKey)).to.equal( - JSON.stringify(testObject), - ); + expect(window.localStorage.getItem(storageKey)).to.equal('existingItem'); }); }); describe('#purge', () => { it('should keep contents null when purging', () => { const storageKey = 'test-disabled-purge-existing'; - window.localStorage.setItem( - storageKey, - JSON.stringify(testObject), - ); + window.localStorage.setItem(storageKey, 'existing'); - const vault = new DisabledVault>>( - storageKey, - ); + const vault = new DisabledVault(storageKey); vault.purge(); @@ -506,12 +483,9 @@ describe('Vault', () => { it('should keep contents null when purging an empty key', () => { const storageKey = 'test-disabled-purge-empty'; - const vault = new DisabledVault>>( - storageKey, - ); + const vault = new DisabledVault(storageKey); vault.purge(); - console.log(window.localStorage.getItem(storageKey)); expect(vault.contents).to.equal(null); expect(window.localStorage.getItem(storageKey)).to.equal(null); }); From 9cf89edf6f23ea704d4b75ba25853c11b3ac9c6b Mon Sep 17 00:00:00 2001 From: Jaissica Date: Fri, 19 Sep 2025 16:17:06 -0400 Subject: [PATCH 3/5] chore: removed log statement --- src/mp-instance.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mp-instance.ts b/src/mp-instance.ts index 4493885c8..795111e87 100644 --- a/src/mp-instance.ts +++ b/src/mp-instance.ts @@ -1536,7 +1536,6 @@ function createKitBlocker(config, mpInstance) { function createIdentityCache(mpInstance) { const cacheKey = `${mpInstance._Store.storageName}-id-cache`; if (mpInstance._Store.getNoFunctional()) { - console.log('createIdentityCache: noFunctional is true'); return new DisabledVault(cacheKey, { logger: mpInstance.Logger }); } return new LocalStorageVault(cacheKey, { logger: mpInstance.Logger }); From b961fe8686ab3498b25687c3f50e62d8270a9d19 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Fri, 19 Sep 2025 17:08:21 -0400 Subject: [PATCH 4/5] refactor: updated getPrivacyFlag for identity cache --- src/mp-instance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mp-instance.ts b/src/mp-instance.ts index 795111e87..34d973a25 100644 --- a/src/mp-instance.ts +++ b/src/mp-instance.ts @@ -1535,7 +1535,7 @@ function createKitBlocker(config, mpInstance) { function createIdentityCache(mpInstance) { const cacheKey = `${mpInstance._Store.storageName}-id-cache`; - if (mpInstance._Store.getNoFunctional()) { + if (mpInstance._Store.getPrivacyFlag('IdentityCache')) { return new DisabledVault(cacheKey, { logger: mpInstance.Logger }); } return new LocalStorageVault(cacheKey, { logger: mpInstance.Logger }); From cba8679be5c513da261a3690be0821546ec5d2f7 Mon Sep 17 00:00:00 2001 From: Jaissica Date: Tue, 23 Sep 2025 13:46:45 -0400 Subject: [PATCH 5/5] test: source privacy flag from launcherOptions --- test/src/tests-identity.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/src/tests-identity.ts b/test/src/tests-identity.ts index 3e1e41d4f..0e8678f6e 100644 --- a/test/src/tests-identity.ts +++ b/test/src/tests-identity.ts @@ -657,14 +657,14 @@ describe('identity', function() { }); it('should NOT save id cache to local storage when noFunctional is true', async () => { - mParticle.config.noFunctional = true; + mParticle.config.launcherOptions = { noFunctional: true }; mParticle.init(apiKey, window.mParticle.config); await waitForCondition(hasIdentifyReturned); expect(localStorage.getItem('mprtcl-v4_abcdef-id-cache')).not.to.be.ok; }); it('should save id cache to local storage when noFunctional is false', async () => { - mParticle.config.noFunctional = false; + mParticle.config.launcherOptions = { noFunctional: false }; mParticle.init(apiKey, window.mParticle.config); await waitForCondition(hasIdentifyReturned); expect(localStorage.getItem('mprtcl-v4_abcdef-id-cache')).to.be.ok;