@splitsoftware/splitio-commons 1.16.1-rc.1 → 1.16.1-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +4 -0
- package/cjs/logger/constants.js +5 -4
- package/cjs/logger/messages/info.js +2 -1
- package/cjs/logger/messages/warn.js +1 -1
- package/cjs/readiness/readinessManager.js +7 -12
- package/cjs/services/splitApi.js +5 -9
- package/cjs/storages/AbstractSegmentsCacheSync.js +41 -12
- package/cjs/storages/AbstractSplitsCacheAsync.js +2 -2
- package/cjs/storages/AbstractSplitsCacheSync.js +7 -5
- package/cjs/storages/KeyBuilder.js +0 -3
- package/cjs/storages/KeyBuilderCS.js +6 -0
- package/cjs/storages/dataLoader.js +1 -1
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +29 -52
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +4 -16
- package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +9 -40
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +6 -15
- package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +4 -11
- package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
- package/cjs/sync/polling/pollingManagerCS.js +33 -51
- package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +14 -20
- package/cjs/sync/streaming/AuthClient/index.js +1 -1
- package/cjs/sync/streaming/SSEHandler/index.js +3 -6
- package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +15 -9
- package/cjs/sync/streaming/constants.js +3 -4
- package/cjs/sync/streaming/parseUtils.js +14 -9
- package/cjs/sync/streaming/pushManager.js +29 -55
- package/cjs/sync/submitters/telemetrySubmitter.js +0 -2
- package/cjs/sync/syncManagerOnline.js +14 -24
- package/cjs/utils/constants/index.js +4 -5
- package/cjs/utils/settingsValidation/index.js +1 -5
- package/esm/logger/constants.js +2 -1
- package/esm/logger/messages/info.js +2 -1
- package/esm/logger/messages/warn.js +1 -1
- package/esm/readiness/readinessManager.js +7 -12
- package/esm/services/splitApi.js +6 -10
- package/esm/storages/AbstractSegmentsCacheSync.js +41 -12
- package/esm/storages/AbstractSplitsCacheAsync.js +2 -2
- package/esm/storages/AbstractSplitsCacheSync.js +5 -3
- package/esm/storages/KeyBuilder.js +0 -3
- package/esm/storages/KeyBuilderCS.js +6 -0
- package/esm/storages/dataLoader.js +1 -1
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +29 -52
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -17
- package/esm/storages/inMemory/MySegmentsCacheInMemory.js +9 -40
- package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -16
- package/esm/sync/polling/fetchers/mySegmentsFetcher.js +4 -11
- package/esm/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
- package/esm/sync/polling/pollingManagerCS.js +34 -52
- package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +12 -18
- package/esm/sync/streaming/AuthClient/index.js +1 -1
- package/esm/sync/streaming/SSEHandler/index.js +4 -7
- package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +15 -9
- package/esm/sync/streaming/constants.js +2 -3
- package/esm/sync/streaming/parseUtils.js +12 -8
- package/esm/sync/streaming/pushManager.js +32 -57
- package/esm/sync/submitters/telemetrySubmitter.js +0 -2
- package/esm/sync/syncManagerOnline.js +15 -25
- package/esm/utils/constants/index.js +2 -3
- package/esm/utils/settingsValidation/index.js +1 -5
- package/package.json +1 -1
- package/src/dtos/types.ts +7 -8
- package/src/logger/constants.ts +2 -1
- package/src/logger/messages/info.ts +2 -1
- package/src/logger/messages/warn.ts +1 -1
- package/src/readiness/readinessManager.ts +7 -9
- package/src/readiness/types.ts +0 -1
- package/src/services/splitApi.ts +7 -12
- package/src/services/splitHttpClient.ts +1 -1
- package/src/services/types.ts +2 -3
- package/src/storages/AbstractSegmentsCacheSync.ts +53 -12
- package/src/storages/AbstractSplitsCacheAsync.ts +2 -2
- package/src/storages/AbstractSplitsCacheSync.ts +7 -5
- package/src/storages/KeyBuilder.ts +0 -3
- package/src/storages/KeyBuilderCS.ts +9 -0
- package/src/storages/dataLoader.ts +1 -1
- package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +26 -56
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +5 -20
- package/src/storages/inMemory/MySegmentsCacheInMemory.ts +10 -44
- package/src/storages/inMemory/SplitsCacheInMemory.ts +7 -13
- package/src/storages/types.ts +10 -8
- package/src/sync/polling/fetchers/mySegmentsFetcher.ts +7 -14
- package/src/sync/polling/fetchers/segmentChangesFetcher.ts +1 -1
- package/src/sync/polling/fetchers/types.ts +2 -2
- package/src/sync/polling/pollingManagerCS.ts +29 -61
- package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +12 -13
- package/src/sync/polling/types.ts +8 -8
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +17 -18
- package/src/sync/streaming/AuthClient/index.ts +1 -1
- package/src/sync/streaming/SSEClient/index.ts +4 -6
- package/src/sync/streaming/SSEHandler/index.ts +5 -9
- package/src/sync/streaming/SSEHandler/types.ts +13 -25
- package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +17 -12
- package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +1 -1
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +1 -1
- package/src/sync/streaming/UpdateWorkers/types.ts +2 -2
- package/src/sync/streaming/constants.ts +2 -3
- package/src/sync/streaming/parseUtils.ts +19 -11
- package/src/sync/streaming/pushManager.ts +38 -68
- package/src/sync/streaming/types.ts +11 -13
- package/src/sync/submitters/telemetrySubmitter.ts +0 -2
- package/src/sync/submitters/types.ts +3 -6
- package/src/sync/syncManagerOnline.ts +11 -19
- package/src/types.ts +1 -26
- package/src/utils/constants/index.ts +2 -3
- package/src/utils/settingsValidation/index.ts +1 -5
- package/types/dtos/types.d.ts +7 -8
- package/types/logger/constants.d.ts +2 -1
- package/types/readiness/types.d.ts +0 -1
- package/types/services/decorateHeaders.d.ts +2 -0
- package/types/services/splitApi.d.ts +1 -1
- package/types/services/splitHttpClient.d.ts +1 -1
- package/types/services/types.d.ts +2 -3
- package/types/storages/AbstractSegmentsCacheSync.d.ts +9 -11
- package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
- package/types/storages/AbstractSplitsCacheSync.d.ts +4 -4
- package/types/storages/KeyBuilder.d.ts +0 -1
- package/types/storages/KeyBuilderCS.d.ts +2 -0
- package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +2 -12
- package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +1 -1
- package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +3 -9
- package/types/storages/inMemory/SplitsCacheInMemory.d.ts +1 -2
- package/types/storages/types.d.ts +8 -7
- package/types/sync/polling/fetchers/mySegmentsFetcher.d.ts +2 -2
- package/types/sync/polling/fetchers/types.d.ts +2 -2
- package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +4 -3
- package/types/sync/polling/types.d.ts +8 -12
- package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +3 -2
- package/types/sync/streaming/SSEHandler/types.d.ts +13 -22
- package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +2 -3
- package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
- package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
- package/types/sync/streaming/UpdateWorkers/types.d.ts +2 -2
- package/types/sync/streaming/constants.d.ts +2 -3
- package/types/sync/streaming/parseUtils.d.ts +4 -5
- package/types/sync/streaming/pushManager.d.ts +0 -2
- package/types/sync/streaming/pushManagerCS_Spec1_3.d.ts +9 -0
- package/types/sync/streaming/pushManager_Spec1_3.d.ts +9 -0
- package/types/sync/streaming/types.d.ts +9 -10
- package/types/sync/submitters/types.d.ts +3 -6
- package/types/types.d.ts +0 -25
- package/types/utils/constants/index.d.ts +2 -3
- package/types/utils/settingsValidation/index.d.ts +0 -2
|
@@ -12,7 +12,7 @@ const messageNoFetch = 'Global fetch API is not available.';
|
|
|
12
12
|
* @param settings SDK settings, used to access authorizationKey, logger instance and metadata (SDK version, ip and hostname) to set additional headers
|
|
13
13
|
* @param platform object containing environment-specific dependencies
|
|
14
14
|
*/
|
|
15
|
-
export function splitHttpClientFactory(settings: ISettings, { getOptions, getFetch }: IPlatform): ISplitHttpClient {
|
|
15
|
+
export function splitHttpClientFactory(settings: ISettings, { getOptions, getFetch }: Pick<IPlatform, 'getOptions' | 'getFetch'>): ISplitHttpClient {
|
|
16
16
|
|
|
17
17
|
const { log, core: { authorizationKey }, version, runtime: { ip, hostname } } = settings;
|
|
18
18
|
const options = getOptions && getOptions(settings);
|
package/src/services/types.ts
CHANGED
|
@@ -39,7 +39,7 @@ export type IFetchSplitChanges = (since: number, noCache?: boolean, till?: numbe
|
|
|
39
39
|
|
|
40
40
|
export type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean, till?: number) => Promise<IResponse>
|
|
41
41
|
|
|
42
|
-
export type
|
|
42
|
+
export type IFetchMemberships = (userMatchingKey: string, noCache?: boolean) => Promise<IResponse>
|
|
43
43
|
|
|
44
44
|
export type IPostEventsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
45
45
|
|
|
@@ -61,8 +61,7 @@ export interface ISplitApi {
|
|
|
61
61
|
fetchAuth: IFetchAuth
|
|
62
62
|
fetchSplitChanges: IFetchSplitChanges
|
|
63
63
|
fetchSegmentChanges: IFetchSegmentChanges
|
|
64
|
-
|
|
65
|
-
fetchMyLargeSegments: IFetchMySegments
|
|
64
|
+
fetchMemberships: IFetchMemberships
|
|
66
65
|
postEventsBulk: IPostEventsBulk
|
|
67
66
|
postUniqueKeysBulkCs: IPostUniqueKeysBulkCs
|
|
68
67
|
postUniqueKeysBulkSs: IPostUniqueKeysBulkSs
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
2
|
/* eslint-disable no-unused-vars */
|
|
3
|
+
import { IMySegmentsResponse } from '../dtos/types';
|
|
4
|
+
import { MySegmentsData } from '../sync/polling/types';
|
|
3
5
|
import { ISegmentsCacheSync } from './types';
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -28,7 +30,9 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
|
|
|
28
30
|
/**
|
|
29
31
|
* clear the cache.
|
|
30
32
|
*/
|
|
31
|
-
|
|
33
|
+
clear() {
|
|
34
|
+
this.resetSegments({});
|
|
35
|
+
}
|
|
32
36
|
|
|
33
37
|
/**
|
|
34
38
|
* For server-side synchronizer: add the given list of segments to the cache, with an empty list of keys. The segments that already exist are not modified.
|
|
@@ -49,20 +53,57 @@ export abstract class AbstractSegmentsCacheSync implements ISegmentsCacheSync {
|
|
|
49
53
|
abstract getKeysCount(): number
|
|
50
54
|
|
|
51
55
|
/**
|
|
52
|
-
* For server-side synchronizer:
|
|
53
|
-
* For client-side synchronizer:
|
|
54
|
-
*/
|
|
55
|
-
setChangeNumber(name: string, changeNumber: number): boolean { return true; }
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* For server-side synchronizer: get the change number of `name` segment.
|
|
59
|
-
* For client-side synchronizer: the method is not used.
|
|
56
|
+
* For server-side synchronizer: change number of `name` segment.
|
|
57
|
+
* For client-side synchronizer: change number of mySegments.
|
|
60
58
|
*/
|
|
61
|
-
|
|
59
|
+
abstract setChangeNumber(name?: string, changeNumber?: number): boolean | void
|
|
60
|
+
abstract getChangeNumber(name: string): number
|
|
62
61
|
|
|
63
62
|
/**
|
|
64
63
|
* For server-side synchronizer: the method is not used.
|
|
65
|
-
* For client-side synchronizer:
|
|
64
|
+
* For client-side synchronizer: it resets or updates the cache.
|
|
66
65
|
*/
|
|
67
|
-
resetSegments(
|
|
66
|
+
resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean {
|
|
67
|
+
this.setChangeNumber(undefined, segmentsData.cn);
|
|
68
|
+
|
|
69
|
+
const { added, removed } = segmentsData as MySegmentsData;
|
|
70
|
+
|
|
71
|
+
if (added && removed) {
|
|
72
|
+
let isDiff = false;
|
|
73
|
+
|
|
74
|
+
added.forEach(segment => {
|
|
75
|
+
isDiff = this.addToSegment(segment) || isDiff;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
removed.forEach(segment => {
|
|
79
|
+
isDiff = this.removeFromSegment(segment) || isDiff;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return isDiff;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const names = ((segmentsData as IMySegmentsResponse).k || []).map(s => s.n).sort();
|
|
86
|
+
const storedSegmentKeys = this.getRegisteredSegments().sort();
|
|
87
|
+
|
|
88
|
+
// Extreme fast => everything is empty
|
|
89
|
+
if (!names.length && !storedSegmentKeys.length) return false;
|
|
90
|
+
|
|
91
|
+
let index = 0;
|
|
92
|
+
|
|
93
|
+
while (index < names.length && index < storedSegmentKeys.length && names[index] === storedSegmentKeys[index]) index++;
|
|
94
|
+
|
|
95
|
+
// Quick path => no changes
|
|
96
|
+
if (index === names.length && index === storedSegmentKeys.length) return false;
|
|
97
|
+
|
|
98
|
+
// Slowest path => add and/or remove segments
|
|
99
|
+
for (let removeIndex = index; removeIndex < storedSegmentKeys.length; removeIndex++) {
|
|
100
|
+
this.removeFromSegment(storedSegmentKeys[removeIndex]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (let addIndex = index; addIndex < names.length; addIndex++) {
|
|
104
|
+
this.addToSegment(names[addIndex]);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
68
109
|
}
|
|
@@ -22,9 +22,9 @@ export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
|
|
|
22
22
|
abstract trafficTypeExists(trafficType: string): Promise<boolean>
|
|
23
23
|
abstract clear(): Promise<boolean | void>
|
|
24
24
|
|
|
25
|
-
// @TODO revisit segment-related methods ('
|
|
25
|
+
// @TODO revisit segment-related methods ('usesSegments', 'getRegisteredSegments', 'registerSegments')
|
|
26
26
|
// noop, just keeping the interface. This is used by standalone client-side API only, and so only implemented by InMemory and InLocalStorage.
|
|
27
|
-
|
|
27
|
+
usesSegments(): Promise<boolean> {
|
|
28
28
|
return Promise.resolve(true);
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -2,6 +2,7 @@ import { ISplitsCacheSync } from './types';
|
|
|
2
2
|
import { ISplit } from '../dtos/types';
|
|
3
3
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
4
4
|
import { ISet } from '../utils/lang/sets';
|
|
5
|
+
import { IN_SEGMENT, IN_LARGE_SEGMENT } from '../utils/constants';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* This class provides a skeletal implementation of the ISplitsCacheSync interface
|
|
@@ -31,7 +32,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
|
|
|
31
32
|
return splits;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
abstract setChangeNumber(changeNumber: number): boolean
|
|
35
|
+
abstract setChangeNumber(changeNumber: number): boolean | void
|
|
35
36
|
|
|
36
37
|
abstract getChangeNumber(): number
|
|
37
38
|
|
|
@@ -43,7 +44,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
|
|
|
43
44
|
|
|
44
45
|
abstract trafficTypeExists(trafficType: string): boolean
|
|
45
46
|
|
|
46
|
-
abstract
|
|
47
|
+
abstract usesSegments(): boolean
|
|
47
48
|
|
|
48
49
|
abstract clear(): void
|
|
49
50
|
|
|
@@ -85,15 +86,16 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
|
|
|
85
86
|
|
|
86
87
|
/**
|
|
87
88
|
* Given a parsed split, it returns a boolean flagging if its conditions use segments matchers (rules & whitelists).
|
|
88
|
-
* This util is intended to simplify the implementation of `splitsCache::
|
|
89
|
+
* This util is intended to simplify the implementation of `splitsCache::usesSegments` method
|
|
89
90
|
*/
|
|
90
|
-
export function
|
|
91
|
+
export function usesSegments(split: ISplit) {
|
|
91
92
|
const conditions = split.conditions || [];
|
|
92
93
|
for (let i = 0; i < conditions.length; i++) {
|
|
93
94
|
const matchers = conditions[i].matcherGroup.matchers;
|
|
94
95
|
|
|
95
96
|
for (let j = 0; j < matchers.length; j++) {
|
|
96
|
-
|
|
97
|
+
const matcher = matchers[j].matcherType;
|
|
98
|
+
if (matcher === IN_SEGMENT || matcher === IN_LARGE_SEGMENT) return true;
|
|
97
99
|
}
|
|
98
100
|
}
|
|
99
101
|
|
|
@@ -51,9 +51,6 @@ export class KeyBuilder {
|
|
|
51
51
|
buildSplitsWithSegmentCountKey() {
|
|
52
52
|
return `${this.prefix}.splits.usingSegments`;
|
|
53
53
|
}
|
|
54
|
-
buildSplitsWithLargeSegmentCountKey() {
|
|
55
|
-
return `${this.prefix}.splits.usingLargeSegments`;
|
|
56
|
-
}
|
|
57
54
|
|
|
58
55
|
buildSegmentNameKey(segmentName: string) {
|
|
59
56
|
return `${this.prefix}.segment.${segmentName}`;
|
|
@@ -5,6 +5,7 @@ export interface MySegmentsKeyBuilder {
|
|
|
5
5
|
buildSegmentNameKey(segmentName: string): string;
|
|
6
6
|
extractSegmentName(builtSegmentKeyName: string): string | undefined;
|
|
7
7
|
extractOldSegmentKey(builtSegmentKeyName: string): string | undefined;
|
|
8
|
+
buildTillKey(): string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
|
|
@@ -47,6 +48,10 @@ export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
|
|
|
47
48
|
isSplitsCacheKey(key: string) {
|
|
48
49
|
return this.regexSplitsCacheKey.test(key);
|
|
49
50
|
}
|
|
51
|
+
|
|
52
|
+
buildTillKey() {
|
|
53
|
+
return `${this.prefix}.${this.matchingKey}.segments.till`;
|
|
54
|
+
}
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
export function myLargeSegmentsKeyBuilder(prefix: string, matchingKey: string): MySegmentsKeyBuilder {
|
|
@@ -63,6 +68,10 @@ export function myLargeSegmentsKeyBuilder(prefix: string, matchingKey: string):
|
|
|
63
68
|
|
|
64
69
|
extractOldSegmentKey() {
|
|
65
70
|
return undefined;
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
buildTillKey() {
|
|
74
|
+
return `${prefix}.${matchingKey}.largeSegments.till`;
|
|
66
75
|
}
|
|
67
76
|
};
|
|
68
77
|
}
|
|
@@ -50,6 +50,6 @@ export function dataLoaderFactory(preloadedData: SplitIO.PreloadedData): DataLoa
|
|
|
50
50
|
return Array.isArray(userIds) && userIds.indexOf(userId) > -1;
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
|
-
storage.segments.resetSegments(mySegmentsData);
|
|
53
|
+
storage.segments.resetSegments({ k: mySegmentsData.map(s => ({ n: s })) });
|
|
54
54
|
};
|
|
55
55
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ILogger } from '../../logger/types';
|
|
2
|
+
import { isNaNNumber } from '../../utils/lang';
|
|
2
3
|
import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
|
|
3
4
|
import type { MySegmentsKeyBuilder } from '../KeyBuilderCS';
|
|
4
5
|
import { LOG_PREFIX, DEFINED } from './constants';
|
|
@@ -15,22 +16,11 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
|
|
|
15
16
|
// There is not need to flush segments cache like splits cache, since resetSegments receives the up-to-date list of active segments
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
/**
|
|
19
|
-
* Removes list of segments from localStorage
|
|
20
|
-
* @NOTE this method is not being used at the moment.
|
|
21
|
-
*/
|
|
22
|
-
clear() {
|
|
23
|
-
this.log.info(LOG_PREFIX + 'Flushing MySegments data from localStorage');
|
|
24
|
-
|
|
25
|
-
// We cannot simply call `localStorage.clear()` since that implies removing user items from the storage
|
|
26
|
-
// We could optimize next sentence, since it implies iterating over all localStorage items
|
|
27
|
-
this.resetSegments([]);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
19
|
addToSegment(name: string): boolean {
|
|
31
20
|
const segmentKey = this.keys.buildSegmentNameKey(name);
|
|
32
21
|
|
|
33
22
|
try {
|
|
23
|
+
if (localStorage.getItem(segmentKey) === DEFINED) return false;
|
|
34
24
|
localStorage.setItem(segmentKey, DEFINED);
|
|
35
25
|
return true;
|
|
36
26
|
} catch (e) {
|
|
@@ -43,6 +33,7 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
|
|
|
43
33
|
const segmentKey = this.keys.buildSegmentNameKey(name);
|
|
44
34
|
|
|
45
35
|
try {
|
|
36
|
+
if (localStorage.getItem(segmentKey) !== DEFINED) return false;
|
|
46
37
|
localStorage.removeItem(segmentKey);
|
|
47
38
|
return true;
|
|
48
39
|
} catch (e) {
|
|
@@ -55,31 +46,22 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
|
|
|
55
46
|
return localStorage.getItem(this.keys.buildSegmentNameKey(name)) === DEFINED;
|
|
56
47
|
}
|
|
57
48
|
|
|
58
|
-
|
|
59
|
-
* Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
|
|
60
|
-
*
|
|
61
|
-
* @param {string[]} segmentNames list of segment names
|
|
62
|
-
* @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
|
|
63
|
-
*/
|
|
64
|
-
resetSegments(names: string[]): boolean {
|
|
65
|
-
let isDiff = false;
|
|
66
|
-
let index;
|
|
67
|
-
|
|
49
|
+
getRegisteredSegments(): string[] {
|
|
68
50
|
// Scan current values from localStorage
|
|
69
|
-
|
|
51
|
+
return Object.keys(localStorage).reduce((accum, key) => {
|
|
70
52
|
let segmentName = this.keys.extractSegmentName(key);
|
|
71
53
|
|
|
72
54
|
if (segmentName) {
|
|
73
55
|
accum.push(segmentName);
|
|
74
56
|
} else {
|
|
75
|
-
// @TODO @BREAKING: This is only to clean up "old" keys. Remove this whole else code block
|
|
57
|
+
// @TODO @BREAKING: This is only to clean up "old" keys. Remove this whole else code block
|
|
76
58
|
segmentName = this.keys.extractOldSegmentKey(key);
|
|
77
59
|
|
|
78
60
|
if (segmentName) { // this was an old segment key, let's clean up.
|
|
79
61
|
const newSegmentKey = this.keys.buildSegmentNameKey(segmentName);
|
|
80
62
|
try {
|
|
81
63
|
// If the new format key is not there, create it.
|
|
82
|
-
if (!localStorage.getItem(newSegmentKey)
|
|
64
|
+
if (!localStorage.getItem(newSegmentKey)) {
|
|
83
65
|
localStorage.setItem(newSegmentKey, DEFINED);
|
|
84
66
|
// we are migrating a segment, let's track it.
|
|
85
67
|
accum.push(segmentName);
|
|
@@ -93,44 +75,32 @@ export class MySegmentsCacheInLocal extends AbstractSegmentsCacheSync {
|
|
|
93
75
|
|
|
94
76
|
return accum;
|
|
95
77
|
}, [] as string[]);
|
|
78
|
+
}
|
|
96
79
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
80
|
+
getKeysCount() {
|
|
81
|
+
return 1;
|
|
82
|
+
}
|
|
100
83
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
84
|
+
setChangeNumber(name?: string, changeNumber?: number) {
|
|
85
|
+
try {
|
|
86
|
+
if (changeNumber) localStorage.setItem(this.keys.buildTillKey(), changeNumber + '');
|
|
87
|
+
else localStorage.removeItem(this.keys.buildTillKey());
|
|
88
|
+
} catch (e) {
|
|
89
|
+
this.log.error(e);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
104
92
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// Slowest path => we need to find at least 1 difference because
|
|
109
|
-
for (index = 0; index < names.length && storedSegmentNames.indexOf(names[index]) !== -1; index++) {
|
|
110
|
-
// TODO: why empty statement?
|
|
111
|
-
}
|
|
93
|
+
getChangeNumber() {
|
|
94
|
+
const n = -1;
|
|
95
|
+
let value: string | number | null = localStorage.getItem(this.keys.buildTillKey());
|
|
112
96
|
|
|
113
|
-
|
|
114
|
-
|
|
97
|
+
if (value !== null) {
|
|
98
|
+
value = parseInt(value, 10);
|
|
115
99
|
|
|
116
|
-
|
|
117
|
-
names.forEach(name => this.addToSegment(name));
|
|
118
|
-
}
|
|
100
|
+
return isNaNNumber(value) ? n : value;
|
|
119
101
|
}
|
|
120
102
|
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
getRegisteredSegments(): string[] {
|
|
125
|
-
return Object.keys(localStorage).reduce<string[]>((accum, key) => {
|
|
126
|
-
const segmentName = this.keys.extractSegmentName(key);
|
|
127
|
-
if (segmentName) accum.push(segmentName);
|
|
128
|
-
return accum;
|
|
129
|
-
}, []);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
getKeysCount() {
|
|
133
|
-
return 1;
|
|
103
|
+
return n;
|
|
134
104
|
}
|
|
135
105
|
|
|
136
106
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ISplit } from '../../dtos/types';
|
|
2
|
-
import { AbstractSplitsCacheSync,
|
|
2
|
+
import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
|
|
3
3
|
import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
|
|
4
4
|
import { KeyBuilderCS } from '../KeyBuilderCS';
|
|
5
5
|
import { ILogger } from '../../logger/types';
|
|
@@ -7,7 +7,6 @@ import { LOG_PREFIX } from './constants';
|
|
|
7
7
|
import { ISet, _Set, setToArray } from '../../utils/lang/sets';
|
|
8
8
|
import { ISettings } from '../../types';
|
|
9
9
|
import { getStorageHash } from '../KeyBuilder';
|
|
10
|
-
import { IN_LARGE_SEGMENT, IN_SEGMENT } from '../../utils/constants';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
|
|
@@ -51,15 +50,10 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
51
50
|
const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
52
51
|
this._decrementCount(ttKey);
|
|
53
52
|
|
|
54
|
-
if (
|
|
53
|
+
if (usesSegments(split)) {
|
|
55
54
|
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
56
55
|
this._decrementCount(segmentsCountKey);
|
|
57
56
|
}
|
|
58
|
-
|
|
59
|
-
if (usesMatcher(split, IN_LARGE_SEGMENT)) {
|
|
60
|
-
const segmentsCountKey = this.keys.buildSplitsWithLargeSegmentCountKey();
|
|
61
|
-
this._decrementCount(segmentsCountKey);
|
|
62
|
-
}
|
|
63
57
|
}
|
|
64
58
|
} catch (e) {
|
|
65
59
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -73,17 +67,11 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
73
67
|
// @ts-expect-error
|
|
74
68
|
localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
|
|
75
69
|
|
|
76
|
-
if (
|
|
70
|
+
if (usesSegments(split)) {
|
|
77
71
|
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
78
72
|
// @ts-expect-error
|
|
79
73
|
localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
|
|
80
74
|
}
|
|
81
|
-
|
|
82
|
-
if (usesMatcher(split, IN_LARGE_SEGMENT)) {
|
|
83
|
-
const segmentsCountKey = this.keys.buildSplitsWithLargeSegmentCountKey();
|
|
84
|
-
// @ts-expect-error
|
|
85
|
-
localStorage.setItem(segmentsCountKey, toNumber(localStorage.getItem(segmentsCountKey)) + 1);
|
|
86
|
-
}
|
|
87
75
|
}
|
|
88
76
|
} catch (e) {
|
|
89
77
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -215,14 +203,11 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
215
203
|
return isFiniteNumber(ttCount) && ttCount > 0;
|
|
216
204
|
}
|
|
217
205
|
|
|
218
|
-
|
|
206
|
+
usesSegments() {
|
|
219
207
|
// If cache hasn't been synchronized with the cloud, assume we need them.
|
|
220
208
|
if (!this.hasSync) return true;
|
|
221
209
|
|
|
222
|
-
const storedCount = localStorage.getItem(
|
|
223
|
-
this.keys.buildSplitsWithSegmentCountKey() :
|
|
224
|
-
this.keys.buildSplitsWithLargeSegmentCountKey()
|
|
225
|
-
);
|
|
210
|
+
const storedCount = localStorage.getItem(this.keys.buildSplitsWithSegmentCountKey());
|
|
226
211
|
const splitsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
|
|
227
212
|
|
|
228
213
|
if (isFiniteNumber(splitsWithSegmentsCount)) {
|
|
@@ -7,18 +7,19 @@ import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync';
|
|
|
7
7
|
export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
|
|
8
8
|
|
|
9
9
|
private segmentCache: Record<string, boolean> = {};
|
|
10
|
-
|
|
11
|
-
clear() {
|
|
12
|
-
this.segmentCache = {};
|
|
13
|
-
}
|
|
10
|
+
private cn?: number;
|
|
14
11
|
|
|
15
12
|
addToSegment(name: string): boolean {
|
|
13
|
+
if (this.segmentCache[name]) return false;
|
|
14
|
+
|
|
16
15
|
this.segmentCache[name] = true;
|
|
17
16
|
|
|
18
17
|
return true;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
removeFromSegment(name: string): boolean {
|
|
21
|
+
if (!this.segmentCache[name]) return false;
|
|
22
|
+
|
|
22
23
|
delete this.segmentCache[name];
|
|
23
24
|
|
|
24
25
|
return true;
|
|
@@ -28,48 +29,13 @@ export class MySegmentsCacheInMemory extends AbstractSegmentsCacheSync {
|
|
|
28
29
|
return this.segmentCache[name] === true;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
/**
|
|
32
|
-
* Reset (update) the cached list of segments with the given list, removing and adding segments if necessary.
|
|
33
|
-
* @NOTE based on the way we use segments in the browser, this way is the best option
|
|
34
|
-
*
|
|
35
|
-
* @param {string[]} names list of segment names
|
|
36
|
-
* @returns boolean indicating if the cache was updated (i.e., given list was different from the cached one)
|
|
37
|
-
*/
|
|
38
|
-
resetSegments(names: string[]): boolean {
|
|
39
|
-
let isDiff = false;
|
|
40
|
-
let index;
|
|
41
|
-
|
|
42
|
-
const storedSegmentKeys = Object.keys(this.segmentCache);
|
|
43
32
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// Quick path
|
|
49
|
-
if (storedSegmentKeys.length !== names.length) {
|
|
50
|
-
isDiff = true;
|
|
51
|
-
|
|
52
|
-
this.segmentCache = {};
|
|
53
|
-
names.forEach(s => {
|
|
54
|
-
this.addToSegment(s);
|
|
55
|
-
});
|
|
56
|
-
} else {
|
|
57
|
-
// Slowest path => we need to find at least 1 difference because
|
|
58
|
-
for (index = 0; index < names.length && this.isInSegment(names[index]); index++) {
|
|
59
|
-
// TODO: why empty statement?
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (index < names.length) {
|
|
63
|
-
isDiff = true;
|
|
64
|
-
|
|
65
|
-
this.segmentCache = {};
|
|
66
|
-
names.forEach(s => {
|
|
67
|
-
this.addToSegment(s);
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
33
|
+
setChangeNumber(name?: string, changeNumber?: number) {
|
|
34
|
+
this.cn = changeNumber;
|
|
35
|
+
}
|
|
71
36
|
|
|
72
|
-
|
|
37
|
+
getChangeNumber() {
|
|
38
|
+
return this.cn || -1;
|
|
73
39
|
}
|
|
74
40
|
|
|
75
41
|
getRegisteredSegments() {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
|
|
2
|
-
import { AbstractSplitsCacheSync,
|
|
2
|
+
import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
|
|
3
3
|
import { isFiniteNumber } from '../../utils/lang';
|
|
4
4
|
import { ISet, _Set } from '../../utils/lang/sets';
|
|
5
|
-
import { IN_LARGE_SEGMENT, IN_SEGMENT } from '../../utils/constants';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Default ISplitsCacheSync implementation that stores split definitions in memory.
|
|
@@ -15,7 +14,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
15
14
|
private ttCache: Record<string, number> = {};
|
|
16
15
|
private changeNumber: number = -1;
|
|
17
16
|
private segmentsCount: number = 0;
|
|
18
|
-
private largeSegmentsCount: number = 0;
|
|
19
17
|
private flagSetsCache: Record<string, ISet<string>> = {};
|
|
20
18
|
|
|
21
19
|
constructor(splitFiltersValidation?: ISplitFiltersValidation) {
|
|
@@ -28,7 +26,6 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
28
26
|
this.ttCache = {};
|
|
29
27
|
this.changeNumber = -1;
|
|
30
28
|
this.segmentsCount = 0;
|
|
31
|
-
this.largeSegmentsCount = 0;
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
addSplit(name: string, split: ISplit): boolean {
|
|
@@ -41,9 +38,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
41
38
|
|
|
42
39
|
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
|
|
43
40
|
|
|
44
|
-
//
|
|
45
|
-
if (
|
|
46
|
-
if (usesMatcher(previousSplit, IN_LARGE_SEGMENT)) this.largeSegmentsCount--;
|
|
41
|
+
// Subtract from segments count for the previous version of this Split
|
|
42
|
+
if (usesSegments(previousSplit)) this.segmentsCount--;
|
|
47
43
|
}
|
|
48
44
|
|
|
49
45
|
if (split) {
|
|
@@ -55,8 +51,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
55
51
|
this.addToFlagSets(split);
|
|
56
52
|
|
|
57
53
|
// Add to segments count for the new version of the Split
|
|
58
|
-
if (
|
|
59
|
-
if (usesMatcher(split, IN_LARGE_SEGMENT)) this.largeSegmentsCount++;
|
|
54
|
+
if (usesSegments(split)) this.segmentsCount++;
|
|
60
55
|
|
|
61
56
|
return true;
|
|
62
57
|
} else {
|
|
@@ -76,8 +71,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
76
71
|
this.removeFromFlagSets(split.name, split.sets);
|
|
77
72
|
|
|
78
73
|
// Update the segments count.
|
|
79
|
-
if (
|
|
80
|
-
if (usesMatcher(split, IN_LARGE_SEGMENT)) this.largeSegmentsCount--;
|
|
74
|
+
if (usesSegments(split)) this.segmentsCount--;
|
|
81
75
|
|
|
82
76
|
return true;
|
|
83
77
|
} else {
|
|
@@ -106,8 +100,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
106
100
|
return isFiniteNumber(this.ttCache[trafficType]) && this.ttCache[trafficType] > 0;
|
|
107
101
|
}
|
|
108
102
|
|
|
109
|
-
|
|
110
|
-
return this.getChangeNumber() === -1 ||
|
|
103
|
+
usesSegments(): boolean {
|
|
104
|
+
return this.getChangeNumber() === -1 || this.segmentsCount > 0;
|
|
111
105
|
}
|
|
112
106
|
|
|
113
107
|
getNamesByFlagSets(flagSets: string[]): ISet<string>[] {
|
package/src/storages/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { MaybeThenable, ISplit } from '../dtos/types';
|
|
1
|
+
import { MaybeThenable, ISplit, IMySegmentsResponse } from '../dtos/types';
|
|
2
|
+
import { MySegmentsData } from '../sync/polling/types';
|
|
2
3
|
import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs, TelemetryUsageStatsPayload, UpdatesFromSSEEnum } from '../sync/submitters/types';
|
|
3
4
|
import { SplitIO, ImpressionDTO, ISettings } from '../types';
|
|
4
5
|
import { ISet } from '../utils/lang/sets';
|
|
@@ -204,8 +205,8 @@ export interface ISplitsCacheBase {
|
|
|
204
205
|
getSplitNames(): MaybeThenable<string[]>,
|
|
205
206
|
// should never reject or throw an exception. Instead return true by default, asssuming the TT might exist.
|
|
206
207
|
trafficTypeExists(trafficType: string): MaybeThenable<boolean>,
|
|
207
|
-
// only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === 1) or contains a FF using
|
|
208
|
-
|
|
208
|
+
// only for Client-Side. Returns true if the storage is not synchronized yet (getChangeNumber() === -1) or contains a FF using segments or large segments
|
|
209
|
+
usesSegments(): MaybeThenable<boolean>,
|
|
209
210
|
clear(): MaybeThenable<boolean | void>,
|
|
210
211
|
// should never reject or throw an exception. Instead return false by default, to avoid emitting SDK_READY_FROM_CACHE.
|
|
211
212
|
checkCache(): MaybeThenable<boolean>,
|
|
@@ -218,12 +219,12 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
218
219
|
removeSplits(names: string[]): boolean[],
|
|
219
220
|
getSplit(name: string): ISplit | null,
|
|
220
221
|
getSplits(names: string[]): Record<string, ISplit | null>,
|
|
221
|
-
setChangeNumber(changeNumber: number): boolean,
|
|
222
|
+
setChangeNumber(changeNumber: number): boolean | void,
|
|
222
223
|
getChangeNumber(): number,
|
|
223
224
|
getAll(): ISplit[],
|
|
224
225
|
getSplitNames(): string[],
|
|
225
226
|
trafficTypeExists(trafficType: string): boolean,
|
|
226
|
-
|
|
227
|
+
usesSegments(): boolean,
|
|
227
228
|
clear(): void,
|
|
228
229
|
checkCache(): boolean,
|
|
229
230
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
|
|
@@ -240,7 +241,7 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
|
240
241
|
getAll(): Promise<ISplit[]>,
|
|
241
242
|
getSplitNames(): Promise<string[]>,
|
|
242
243
|
trafficTypeExists(trafficType: string): Promise<boolean>,
|
|
243
|
-
|
|
244
|
+
usesSegments(): Promise<boolean>,
|
|
244
245
|
clear(): Promise<boolean | void>,
|
|
245
246
|
checkCache(): Promise<boolean>,
|
|
246
247
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
|
|
@@ -268,9 +269,9 @@ export interface ISegmentsCacheSync extends ISegmentsCacheBase {
|
|
|
268
269
|
registerSegments(names: string[]): boolean
|
|
269
270
|
getRegisteredSegments(): string[]
|
|
270
271
|
getKeysCount(): number // only used for telemetry
|
|
271
|
-
setChangeNumber(name: string, changeNumber: number): boolean
|
|
272
|
+
setChangeNumber(name: string, changeNumber: number): boolean | void
|
|
272
273
|
getChangeNumber(name: string): number
|
|
273
|
-
resetSegments(
|
|
274
|
+
resetSegments(segmentsData: MySegmentsData | IMySegmentsResponse): boolean // only for Sync Client-Side
|
|
274
275
|
clear(): void
|
|
275
276
|
}
|
|
276
277
|
|
|
@@ -478,6 +479,7 @@ export interface IStorageSync extends IStorageBase<
|
|
|
478
479
|
ITelemetryCacheSync,
|
|
479
480
|
IUniqueKeysCacheSync
|
|
480
481
|
> {
|
|
482
|
+
// Defined in client-side
|
|
481
483
|
largeSegments?: ISegmentsCacheSync,
|
|
482
484
|
}
|
|
483
485
|
|