diff --git a/src/mp-instance.ts b/src/mp-instance.ts index 81c3368a8..34d973a25 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, valueof } 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'; @@ -1534,9 +1534,11 @@ 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.getPrivacyFlag('IdentityCache')) { + 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..0e8678f6e 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 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 NOT save id cache to local storage when noFunctional is true', async () => { + 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.launcherOptions = { 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..d264327ce 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,76 @@ 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('testString'); + + 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, 'existingItem'); + + const vault = new DisabledVault(storageKey); + + vault.store('newValue'); + + expect(vault.contents).to.equal(null); + 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 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, 'existingItem'); + + const vault = new DisabledVault(storageKey); + const retrievedItem = vault.retrieve(); + expect(retrievedItem).to.equal(null); + 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, 'existing'); + + 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(); + expect(vault.contents).to.equal(null); + expect(window.localStorage.getItem(storageKey)).to.equal(null); + }); + }); + }); }); \ No newline at end of file