Skip to content

Commit 2b2e795

Browse files
committed
Merge remote-tracking branch 'origin/development' into feat/sdke-132-disable-identity-cache-when-no-functional-is-true
2 parents f31395b + 97dd88a commit 2b2e795

11 files changed

Lines changed: 384 additions & 91 deletions

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# [2.46.0](https://github.com/mParticle/mparticle-web-sdk/compare/v2.45.0...v2.46.0) (2025-09-17)
2+
3+
4+
### Features
5+
6+
* added only rokt, all, none click ids when collectClickIdV2Enabl… ([#1062](https://github.com/mParticle/mparticle-web-sdk/issues/1062)) ([99982ac](https://github.com/mParticle/mparticle-web-sdk/commit/99982ac8d648a138d161caf507e0be256b55e488))
7+
18
# [2.45.0](https://github.com/mParticle/mparticle-web-sdk/compare/v2.44.0...v2.45.0) (2025-09-15)
29

310

dist/mparticle.common.js

Lines changed: 34 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/mparticle.esm.js

Lines changed: 34 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/mparticle.js

Lines changed: 105 additions & 34 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@mparticle/web-sdk",
3-
"version": "2.45.0",
3+
"version": "2.46.0",
44
"description": "mParticle core SDK for web applications",
55
"license": "Apache-2.0",
66
"keywords": [

src/batchUploader.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ export class BatchUploader {
7272

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

7779
if (this.offlineStorageEnabled) {
7880
this.eventVault = new SessionStorageVault<SDKEvent[]>(

src/constants.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,15 @@ export const HTTP_UNAUTHORIZED = 401 as const;
229229
export const HTTP_FORBIDDEN = 403 as const;
230230
export const HTTP_NOT_FOUND = 404 as const;
231231
export const HTTP_SERVER_ERROR = 500 as const;
232+
233+
export type PrivacyControl = 'functional' | 'targeting';
234+
235+
export type StorageTypes = 'SDKState' | 'Products' | 'OfflineEvents' | 'IdentityCache' | 'TimeOnSite';
236+
237+
export const StoragePrivacyMap: Record<StorageTypes, PrivacyControl> = {
238+
SDKState: 'functional',
239+
Products: 'targeting',
240+
OfflineEvents: 'functional',
241+
IdentityCache: 'functional',
242+
TimeOnSite: 'targeting',
243+
};

src/store.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
UserIdentities,
99
} from '@mparticle/web-sdk';
1010
import { IKitConfigs } from './configAPIClient';
11-
import Constants from './constants';
11+
import Constants, { PrivacyControl, StoragePrivacyMap, StorageTypes } from './constants';
1212
import {
1313
DataPlanResult,
1414
KitBlockerOptions,
@@ -216,6 +216,7 @@ export interface IStore {
216216
setNoFunctional?(noFunctional: boolean): void;
217217
getNoTargeting?(): boolean;
218218
setNoTargeting?(noTargeting: boolean): void;
219+
getPrivacyFlag?(storageType: StorageTypes): boolean;
219220

220221
getDeviceId?(): string;
221222
setDeviceId?(deviceId: string): void;
@@ -608,6 +609,17 @@ export default function Store(
608609
this.noTargeting = noTargeting;
609610
};
610611

612+
this.getPrivacyFlag = (storageType: StorageTypes): boolean => {
613+
const privacyControl: PrivacyControl = StoragePrivacyMap[storageType];
614+
if (privacyControl === 'functional') {
615+
return this.getNoFunctional();
616+
}
617+
if (privacyControl === 'targeting') {
618+
return this.getNoTargeting();
619+
}
620+
return false;
621+
};
622+
611623
this.getDeviceId = () => this.deviceId;
612624
this.setDeviceId = (deviceId: string) => {
613625
this.deviceId = deviceId;

test/jest/batchUploader.spec.ts

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,59 @@
11
import { BatchUploader } from '../../src/batchUploader';
22
import { IMParticleWebSDKInstance } from '../../src/mp-instance';
3+
import { IStore } from '../../src/store';
4+
import { StoragePrivacyMap, StorageTypes } from '../../src/constants';
5+
import { SDKEvent } from '../../src/sdkRuntimeModels';
36

47
describe('BatchUploader', () => {
58
let batchUploader: BatchUploader;
69
let mockMPInstance: IMParticleWebSDKInstance;
10+
let originalFetch: typeof global.fetch;
711

812
beforeEach(() => {
913
const now = Date.now();
1014
jest.useFakeTimers({
1115
now: now,
1216
advanceTimers: true // This improves the performance of nested timers, equivalent to Sinon's shouldAdvanceTime
1317
});
14-
18+
originalFetch = global.fetch;
19+
global.fetch = jest.fn().mockResolvedValue({ ok: true, status: 200 });
20+
1521
// Create a mock mParticle instance with mocked methods for instantiating a BatchUploader
1622
mockMPInstance = {
1723
_Store: {
24+
storageName: 'mprtcl-v4_abcdef',
25+
getNoFunctional: function(this: IStore) { return this.noFunctional; },
26+
getNoTargeting: function(this: IStore) { return this.noTargeting; },
27+
getPrivacyFlag: function(this: IStore, storageType: StorageTypes) {
28+
const privacyControl = StoragePrivacyMap[storageType];
29+
if (privacyControl === 'functional') {
30+
return this.getNoFunctional();
31+
}
32+
if (privacyControl === 'targeting') {
33+
return this.getNoTargeting();
34+
}
35+
return false;
36+
},
37+
deviceId: 'device-1',
1838
SDKConfig: {
1939
flags: {}
2040
}
2141
},
2242
_Helpers: {
23-
getFeatureFlag: jest.fn().mockReturnValue(false),
24-
createServiceUrl: jest.fn().mockReturnValue('https://mock-url.com')
43+
getFeatureFlag: jest.fn().mockReturnValue('100'),
44+
createServiceUrl: jest.fn().mockReturnValue('https://mock-url.com'),
45+
generateUniqueId: jest.fn().mockReturnValue('req-1'),
2546
},
2647
Identity: {
2748
getCurrentUser: jest.fn().mockReturnValue({
28-
getMPID: () => 'test-mpid'
49+
getMPID: () => 'test-mpid',
50+
getConsentState: jest.fn().mockReturnValue(null),
2951
})
52+
},
53+
Logger: {
54+
verbose: jest.fn(),
55+
error: jest.fn(),
56+
warning: jest.fn(),
3057
}
3158
} as unknown as IMParticleWebSDKInstance;
3259

@@ -35,6 +62,7 @@ describe('BatchUploader', () => {
3562

3663
afterEach(() => {
3764
jest.useRealTimers();
65+
global.fetch = originalFetch;
3866
});
3967

4068
describe('shouldDebounceAST', () => {
@@ -104,4 +132,50 @@ describe('BatchUploader', () => {
104132
expect(secondCallTime).toBe(firstCallTime);
105133
});
106134
});
135+
136+
describe('noFunctional', () => {
137+
beforeEach(() => {
138+
localStorage.clear();
139+
sessionStorage.clear();
140+
});
141+
142+
it('should disable offline storage when noFunctional is true', () => {
143+
mockMPInstance._Store.noFunctional = true;
144+
145+
const uploader = new BatchUploader(mockMPInstance, 1000);
146+
expect(uploader['offlineStorageEnabled']).toBe(false);
147+
148+
uploader.queueEvent({ EventDataType: 4 } as SDKEvent);
149+
expect(sessionStorage.getItem('mprtcl-v4_abcdef-events')).toBeNull();
150+
expect(localStorage.getItem('mprtcl-v4_abcdef-batches')).toBeNull();
151+
});
152+
153+
it('should enable offline storage when noFunctional is false by default', async () => {
154+
const uploader = new BatchUploader(mockMPInstance, 1000);
155+
156+
expect(uploader['offlineStorageEnabled']).toBe(true);
157+
158+
uploader.queueEvent({ EventDataType: 4 } as SDKEvent);
159+
expect(sessionStorage.getItem('mprtcl-v4_abcdef-events')).not.toBeNull();
160+
161+
jest.advanceTimersByTime(1000);
162+
await Promise.resolve();
163+
164+
expect(localStorage.getItem('mprtcl-v4_abcdef-batches')).not.toBeNull();
165+
});
166+
167+
it('should enable offline storage when noFunctional is false', async () => {
168+
mockMPInstance._Store.noFunctional = false;
169+
170+
const uploader = new BatchUploader(mockMPInstance, 1000);
171+
172+
uploader.queueEvent({ EventDataType: 4 } as SDKEvent);
173+
expect(sessionStorage.getItem('mprtcl-v4_abcdef-events')).not.toBeNull();
174+
175+
jest.advanceTimersByTime(1000);
176+
await Promise.resolve();
177+
178+
expect(localStorage.getItem('mprtcl-v4_abcdef-batches')).not.toBeNull();
179+
});
180+
});
107181
});

0 commit comments

Comments
 (0)