diff --git a/src/sdkRuntimeModels.ts b/src/sdkRuntimeModels.ts index eb1eb3d2e..6f129b47c 100644 --- a/src/sdkRuntimeModels.ts +++ b/src/sdkRuntimeModels.ts @@ -270,6 +270,9 @@ export interface SDKInitConfig extends Omit { dataPlan?: DataPlanConfig | KitBlockerDataPlan; // TODO: These should be eventually split into two different attributes logLevel?: LogLevelType; + + noFunctional?: boolean; + noTargeting?: boolean; kitConfigs?: IKitConfigs[]; kits?: Dictionary; diff --git a/src/store.ts b/src/store.ts index e9467d5dd..db3a0413c 100644 --- a/src/store.ts +++ b/src/store.ts @@ -152,6 +152,8 @@ export interface IFeatureFlags { export interface IStore { isEnabled: boolean; isInitialized: boolean; + noFunctional: boolean; + noTargeting: boolean; // Session Attributes are persistent attributes that are tied to the current session and // are uploaded then cleared when the session ends. @@ -209,6 +211,11 @@ export interface IStore { _getFromPersistence?(mpid: MPID, key: string): T; _setPersistence?(mpid: MPID, key: string, value: T): void; + getNoFunctional?(): boolean; + setNoFunctional?(noFunctional: boolean): void; + getNoTargeting?(): boolean; + setNoTargeting?(noTargeting: boolean): void; + getDeviceId?(): string; setDeviceId?(deviceId: string): void; getFirstSeenTime?(mpid: MPID): number; @@ -245,6 +252,8 @@ export default function Store( const defaultStore: Partial = { isEnabled: true, + noFunctional: false, + noTargeting: false, sessionAttributes: {}, localSessionAttributes: {}, currentSessionMPIDs: [], @@ -588,6 +597,16 @@ export default function Store( } }; + this.getNoFunctional = (): boolean => this.noFunctional; + this.setNoFunctional = (noFunctional: boolean): void => { + this.noFunctional = noFunctional; + }; + + this.getNoTargeting = (): boolean => this.noTargeting; + this.setNoTargeting = (noTargeting: boolean): void => { + this.noTargeting = noTargeting; + }; + this.getDeviceId = () => this.deviceId; this.setDeviceId = (deviceId: string) => { this.deviceId = deviceId; @@ -720,6 +739,15 @@ export default function Store( this.storageName = createMainStorageName(workspaceToken); this.prodStorageName = createProductStorageName(workspaceToken); + // Extract privacy flags directly from config into Store + if (config.hasOwnProperty('noFunctional')) { + this.setNoFunctional(config.noFunctional); + } + + if (config.hasOwnProperty('noTargeting')) { + this.setNoTargeting(config.noTargeting); + } + this.SDKConfig.requiredWebviewBridgeName = requiredWebviewBridgeName || workspaceToken; diff --git a/test/jest/store.flags.spec.ts b/test/jest/store.flags.spec.ts new file mode 100644 index 000000000..cc980a3ff --- /dev/null +++ b/test/jest/store.flags.spec.ts @@ -0,0 +1,85 @@ +import Store, { IStore } from '../../src/store'; +import { IMParticleWebSDKInstance } from '../../src/mp-instance'; +import { SDKInitConfig } from '../../src/sdkRuntimeModels'; + +describe('Store privacy flags', () => { + let store: IStore & Record; + let mockMPInstance: IMParticleWebSDKInstance & Record; + + beforeEach(() => { + mockMPInstance = { + _Helpers: { + createMainStorageName: () => 'mprtcl-v4', + createProductStorageName: () => 'mprtcl-prodv4', + Validators: { isFunction: () => true }, + extend: Object.assign, + }, + _NativeSdkHelpers: { + isWebviewEnabled: () => false, + }, + _Persistence: { + update: () => {}, + savePersistence: () => {}, + getPersistence: () => ({ gs: {} }), + }, + Logger: { + verbose: () => {}, + warning: () => {}, + error: () => {}, + }, + Identity: { + getCurrentUser: () => ({ getMPID: () => 'mpid' }), + }, + } as any; + + store = {} as any; + // Construct Store with our mock 'this' + (Store as any).call(store, {} as SDKInitConfig, mockMPInstance, 'apikey'); + }); + + it('should default noFunctional and noTargeting to false when not provided', () => { + const cfg: SDKInitConfig = { flags: {} } as any; + store.processConfig(cfg); + expect(store.getNoFunctional()).toBe(false); + expect(store.getNoTargeting()).toBe(false); + }); + + it('should set noFunctional as true and noTargeting as true from config', () => { + const cfg: SDKInitConfig = { + noFunctional: true, + noTargeting: true, + flags: {}, + } as any; + + store.processConfig(cfg); + + expect(store.getNoFunctional()).toBe(true); + expect(store.getNoTargeting()).toBe(true); + }); + + it('should set noFunctional as true and noTargeting as false from config', () => { + const cfg: SDKInitConfig = { + noFunctional: true, + noTargeting: false, + flags: {}, + } as any; + + store.processConfig(cfg); + + expect(store.getNoFunctional()).toBe(true); + expect(store.getNoTargeting()).toBe(false); + }); + + it('should set noFunctional as false and noTargeting as true from config', () => { + const cfg: SDKInitConfig = { + noFunctional: false, + noTargeting: true, + flags: {}, + } as any; + + store.processConfig(cfg); + + expect(store.getNoFunctional()).toBe(false); + expect(store.getNoTargeting()).toBe(true); + }); +}); \ No newline at end of file diff --git a/test/src/tests-store.ts b/test/src/tests-store.ts index c79518909..2a6cf8c36 100644 --- a/test/src/tests-store.ts +++ b/test/src/tests-store.ts @@ -1801,4 +1801,27 @@ describe('Store', () => { }); }); }); + + describe('#privacy flags', () => { + it('should set noFunctional and noTargeting to false when not provided', () => { + const inst = window.mParticle.getInstance(); + const store = (inst as any)._Store; + expect(store.getNoFunctional()).to.equal(false); + expect(store.getNoTargeting()).to.equal(false); + }); + + it('should set noFunctional and noTargeting from init config', () => { + window.mParticle._resetForTests(MPConfig); + window.mParticle.config = window.mParticle.config || {}; + window.mParticle.config.noFunctional = true; + window.mParticle.config.noTargeting = true; + + window.mParticle.init(apiKey, window.mParticle.config); + + const inst = window.mParticle.getInstance(); + const store = (inst as any)._Store; + expect(store.getNoFunctional()).to.equal(true); + expect(store.getNoTargeting()).to.equal(true); + }); + }); }); \ No newline at end of file