Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/batchUploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ export class BatchUploader {

// Cache Offline Storage Availability boolean
// so that we don't have to check it every time
this.offlineStorageEnabled =
this.isOfflineStorageAvailable() &&
!mpInstance._Store.getPrivacyFlag('OfflineEvents');
this.offlineStorageEnabled = this.isOfflineStorageAvailable();

if (this.offlineStorageEnabled) {
this.eventVault = new SessionStorageVault<SDKEvent[]>(
Expand Down
11 changes: 0 additions & 11 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,3 @@ export const HTTP_UNAUTHORIZED = 401 as const;
export const HTTP_FORBIDDEN = 403 as const;
export const HTTP_NOT_FOUND = 404 as const;
export const HTTP_SERVER_ERROR = 500 as const;

export type PrivacyControl = 'functional' | 'targeting';

export type StorageTypes = 'SDKState' | 'OfflineEvents' | 'IdentityCache' | 'TimeOnSite';

export const StoragePrivacyMap: Record<StorageTypes, PrivacyControl> = {
SDKState: 'functional',
OfflineEvents: 'functional',
IdentityCache: 'functional',
TimeOnSite: 'targeting',
};
10 changes: 4 additions & 6 deletions src/mp-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import KitBlocker from './kitBlocking';
import ConfigAPIClient, { IKitConfigs } from './configAPIClient';
import IdentityAPIClient from './identityApiClient';
import { isFunction, parseConfig, valueof, generateDeprecationMessage } from './utils';
import { DisabledVault, LocalStorageVault } from './vault';
import { LocalStorageVault } from './vault';
import { removeExpiredIdentityCacheDates } from './identity-utils';
import IntegrationCapture from './integrationCapture';
import { IPreInit, processReadyQueue } from './pre-init-utils';
Expand Down Expand Up @@ -1543,11 +1543,9 @@ function createKitBlocker(config, mpInstance) {
}

function createIdentityCache(mpInstance) {
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 });
return new LocalStorageVault(`${mpInstance._Store.storageName}-id-cache`, {
logger: mpInstance.Logger,
});
}

function runPreConfigFetchInitialization(mpInstance, apiKey, config) {
Expand Down
15 changes: 1 addition & 14 deletions src/persistence.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ export default function _Persistence(mpInstance) {
mpInstance._Store.isFirstRun = false;
}

if (mpInstance._Store.getPrivacyFlag('SDKState')) {
return;
}

// https://go.mparticle.com/work/SQDSDKS-6045
if (!mpInstance._Store.isLocalStorageAvailable) {
mpInstance._Store.SDKConfig.useCookieStorage = true;
Expand Down Expand Up @@ -117,10 +113,7 @@ export default function _Persistence(mpInstance) {
};

this.update = function() {
if (
!mpInstance._Store.webviewBridgeEnabled &&
!mpInstance._Store.getPrivacyFlag('SDKState')
) {
if (!mpInstance._Store.webviewBridgeEnabled) {
if (mpInstance._Store.SDKConfig.useCookieStorage) {
self.setCookie();
}
Expand Down Expand Up @@ -810,9 +803,6 @@ export default function _Persistence(mpInstance) {

// https://go.mparticle.com/work/SQDSDKS-6021
this.savePersistence = function(persistence) {
if (mpInstance._Store.getPrivacyFlag('SDKState')) {
return;
}
var encodedPersistence = self.encodePersistence(
JSON.stringify(persistence)
),
Expand Down Expand Up @@ -857,9 +847,6 @@ export default function _Persistence(mpInstance) {
};

this.getPersistence = function() {
if (mpInstance._Store.getPrivacyFlag('SDKState')) {
return null;
}
var persistence = this.useLocalStorage()
? this.getLocalStorage()
: this.getCookie();
Expand Down
3 changes: 0 additions & 3 deletions src/sdkRuntimeModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,6 @@ export interface SDKInitConfig
extends Omit<MPConfiguration, 'dataPlan' | 'logLevel'> {
dataPlan?: DataPlanConfig | KitBlockerDataPlan; // TODO: These should be eventually split into two different attributes
logLevel?: LogLevelType;

noFunctional?: boolean;
noTargeting?: boolean;

kitConfigs?: IKitConfigs[];
kits?: Dictionary<UnregisteredKit>;
Expand Down
47 changes: 2 additions & 45 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
UserIdentities,
} from '@mparticle/web-sdk';
import { IKitConfigs } from './configAPIClient';
import Constants, { PrivacyControl, StoragePrivacyMap, StorageTypes } from './constants';
import Constants from './constants';
import {
DataPlanResult,
KitBlockerOptions,
Expand Down Expand Up @@ -153,8 +153,6 @@ 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.
Expand Down Expand Up @@ -211,12 +209,6 @@ export interface IStore {
_getFromPersistence?<T>(mpid: MPID, key: string): T;
_setPersistence?<T>(mpid: MPID, key: string, value: T): void;

getNoFunctional?(): boolean;
setNoFunctional?(noFunctional: boolean): void;
getNoTargeting?(): boolean;
setNoTargeting?(noTargeting: boolean): void;
getPrivacyFlag?(storageType: StorageTypes): boolean;

getDeviceId?(): string;
setDeviceId?(deviceId: string): void;
getFirstSeenTime?(mpid: MPID): number;
Expand Down Expand Up @@ -252,8 +244,6 @@ export default function Store(

const defaultStore: Partial<IStore> = {
isEnabled: true,
noFunctional: false,
noTargeting: false,
sessionAttributes: {},
localSessionAttributes: {},
currentSessionMPIDs: [],
Expand Down Expand Up @@ -596,27 +586,6 @@ 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.getPrivacyFlag = (storageType: StorageTypes): boolean => {
const privacyControl: PrivacyControl = StoragePrivacyMap[storageType];
if (privacyControl === 'functional') {
return this.getNoFunctional();
}
if (privacyControl === 'targeting') {
return this.getNoTargeting();
}
return false;
};

this.getDeviceId = () => this.deviceId;
this.setDeviceId = (deviceId: string) => {
this.deviceId = deviceId;
Expand Down Expand Up @@ -737,21 +706,9 @@ export default function Store(
this.SDKConfig[baseUrlKeys] = baseUrls[baseUrlKeys];
}

const { noFunctional, noTargeting } = config?.launcherOptions ?? {};

if (noFunctional != null) {
this.setNoFunctional(noFunctional);
}

if (noTargeting != null) {
this.setNoTargeting(noTargeting);
}

if (workspaceToken) {
this.SDKConfig.workspaceToken = workspaceToken;
if (!this.getPrivacyFlag('TimeOnSite')) {
mpInstance._timeOnSiteTimer = new ForegroundTimer(workspaceToken);
}
mpInstance._timeOnSiteTimer = new ForegroundTimer(workspaceToken);
} else {
mpInstance.Logger.warning(
'You should have a workspaceToken on your config object for security purposes.'
Expand Down
16 changes: 0 additions & 16 deletions src/vault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,4 @@ export class SessionStorageVault<StorableItem> extends BaseVault<StorableItem> {
constructor(storageKey: string, options?: IVaultOptions) {
super(storageKey, window.sessionStorage, options);
}
}

// DisabledVault is used when persistence is disabled by privacy flags.
export class DisabledVault<StorableItem> extends BaseVault<StorableItem> {
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;
}
}
74 changes: 2 additions & 72 deletions test/jest/batchUploader.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { BatchUploader } from '../../src/batchUploader';
import { IMParticleWebSDKInstance } from '../../src/mp-instance';
import { IStore } from '../../src/store';
import { StoragePrivacyMap, StorageTypes } from '../../src/constants';
import { SDKEvent } from '../../src/sdkRuntimeModels';

describe('BatchUploader', () => {
let batchUploader: BatchUploader;
Expand All @@ -21,39 +18,18 @@ describe('BatchUploader', () => {
// Create a mock mParticle instance with mocked methods for instantiating a BatchUploader
mockMPInstance = {
_Store: {
storageName: 'mprtcl-v4_abcdef',
getNoFunctional: function(this: IStore) { return this.noFunctional; },
getNoTargeting: function(this: IStore) { return this.noTargeting; },
getPrivacyFlag: function(this: IStore, storageType: StorageTypes) {
const privacyControl = StoragePrivacyMap[storageType];
if (privacyControl === 'functional') {
return this.getNoFunctional();
}
if (privacyControl === 'targeting') {
return this.getNoTargeting();
}
return false;
},
deviceId: 'device-1',
SDKConfig: {
flags: {}
}
},
_Helpers: {
getFeatureFlag: jest.fn().mockReturnValue('100'),
getFeatureFlag: jest.fn().mockReturnValue(false),
createServiceUrl: jest.fn().mockReturnValue('https://mock-url.com'),
generateUniqueId: jest.fn().mockReturnValue('req-1'),
},
Identity: {
getCurrentUser: jest.fn().mockReturnValue({
getMPID: () => 'test-mpid',
getConsentState: jest.fn().mockReturnValue(null),
getMPID: () => 'test-mpid'
})
},
Logger: {
verbose: jest.fn(),
error: jest.fn(),
warning: jest.fn(),
}
} as unknown as IMParticleWebSDKInstance;

Expand Down Expand Up @@ -132,50 +108,4 @@ describe('BatchUploader', () => {
expect(secondCallTime).toBe(firstCallTime);
});
});

describe('noFunctional', () => {
beforeEach(() => {
localStorage.clear();
sessionStorage.clear();
});

it('should disable offline storage when noFunctional is true', () => {
mockMPInstance._Store.noFunctional = true;

const uploader = new BatchUploader(mockMPInstance, 1000);
expect(uploader['offlineStorageEnabled']).toBe(false);

uploader.queueEvent({ EventDataType: 4 } as SDKEvent);
expect(sessionStorage.getItem('mprtcl-v4_abcdef-events')).toBeNull();
expect(localStorage.getItem('mprtcl-v4_abcdef-batches')).toBeNull();
});

it('should enable offline storage when noFunctional is false by default', async () => {
const uploader = new BatchUploader(mockMPInstance, 1000);

expect(uploader['offlineStorageEnabled']).toBe(true);

uploader.queueEvent({ EventDataType: 4 } as SDKEvent);
expect(sessionStorage.getItem('mprtcl-v4_abcdef-events')).not.toBeNull();

jest.advanceTimersByTime(1000);
await Promise.resolve();

expect(localStorage.getItem('mprtcl-v4_abcdef-batches')).not.toBeNull();
});

it('should enable offline storage when noFunctional is false', async () => {
mockMPInstance._Store.noFunctional = false;

const uploader = new BatchUploader(mockMPInstance, 1000);

uploader.queueEvent({ EventDataType: 4 } as SDKEvent);
expect(sessionStorage.getItem('mprtcl-v4_abcdef-events')).not.toBeNull();

jest.advanceTimersByTime(1000);
await Promise.resolve();

expect(localStorage.getItem('mprtcl-v4_abcdef-batches')).not.toBeNull();
});
});
});
64 changes: 0 additions & 64 deletions test/jest/foregroundTimeTracker.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import ForegroundTimeTracker from '../../src/foregroundTimeTracker';
import Store, { IStore } from '../../src/store';
import { SDKInitConfig } from '../../src/sdkRuntimeModels';
import { IMParticleWebSDKInstance } from '../../src/mp-instance';

describe('ForegroundTimeTracker', () => {
let foregroundTimeTracker: ForegroundTimeTracker;
Expand Down Expand Up @@ -493,65 +490,4 @@ describe('ForegroundTimeTracker', () => {
expect(updatePersistenceSpy).toHaveBeenCalled();
});
});

describe('#privacy flags', () => {
const workspaceToken = 'abcdef';
const tosKey = `mprtcl-tos-${workspaceToken}`;
let mockMPInstance: IMParticleWebSDKInstance;
let store: IStore;

beforeEach(() => {
jest.useFakeTimers({ now: Date.now(), advanceTimers: true });
localStorage.clear();

mockMPInstance = {
_Helpers: {
createMainStorageName: () => `mprtcl-v4_${workspaceToken}`,
createProductStorageName: () => `mprtcl-prodv4_${workspaceToken}`,
Validators: { isFunction: () => true },
extend: Object.assign,
},
_NativeSdkHelpers: { isWebviewEnabled: () => false },
_Persistence: {
update: jest.fn(),
savePersistence: jest.fn(),
getPersistence: () => ({ gs: {} }),
},
Logger: { verbose: jest.fn(), warning: jest.fn(), error: jest.fn() },
Identity: { getCurrentUser: () => ({ getMPID: () => 'mpid' }) },
_timeOnSiteTimer: undefined as any,
} as unknown as IMParticleWebSDKInstance;

store = {} as IStore;
(Store as any).call(store, {} as SDKInitConfig, mockMPInstance, 'apikey');
mockMPInstance._Store = store;
});

afterEach(() => {
jest.useRealTimers();
localStorage.clear();
});

it('should track time on site when noTargeting is false by default', () => {
store.processConfig({ workspaceToken } as SDKInitConfig);
expect(mockMPInstance._timeOnSiteTimer).toBeDefined();
jest.advanceTimersByTime(1000);
mockMPInstance._timeOnSiteTimer.getTimeInForeground();
expect(localStorage.getItem(tosKey)).not.toBeNull();
});

it('should NOT track time on site when noTargeting is true', () => {
store.processConfig({ workspaceToken, launcherOptions: { noTargeting: true } } as SDKInitConfig);
expect(mockMPInstance._timeOnSiteTimer).toBeUndefined();
expect(localStorage.getItem(tosKey)).toBeNull();
});

it('should track time on site when noTargeting is false', () => {
store.processConfig({ workspaceToken, launcherOptions: { noTargeting: false } } as SDKInitConfig);
expect(mockMPInstance._timeOnSiteTimer).toBeDefined();
jest.advanceTimersByTime(1000);
mockMPInstance._timeOnSiteTimer.getTimeInForeground();
expect(localStorage.getItem(tosKey)).not.toBeNull();
});
});
});
Loading
Loading