@splitsoftware/splitio-commons 3.0.0-rc.0 → 3.0.0-rc.1
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 +8 -3
- package/cjs/evaluator/index.js +11 -11
- package/cjs/logger/constants.js +4 -4
- package/cjs/logger/messages/warn.js +0 -1
- package/cjs/readiness/constants.js +4 -4
- package/cjs/readiness/readinessManager.js +22 -22
- package/cjs/readiness/sdkReadinessManager.js +0 -16
- package/cjs/sdkClient/sdkLifecycle.js +2 -1
- package/cjs/sdkClient/trackMethod.js +2 -2
- package/cjs/sdkFactory/index.js +5 -5
- package/cjs/sdkManager/index.js +2 -2
- package/cjs/services/authProvider.js +67 -0
- package/cjs/services/secureSplitHttpClient.js +39 -0
- package/cjs/services/splitApi.js +7 -2
- package/cjs/storages/AbstractDefinitionsCacheAsync.js +51 -0
- package/cjs/storages/AbstractDefinitionsCacheSync.js +76 -0
- package/cjs/storages/KeyBuilder.js +6 -6
- package/cjs/storages/KeyBuilderCS.js +5 -5
- package/cjs/storages/KeyBuilderSS.js +2 -2
- package/cjs/storages/getRolloutPlan.js +3 -3
- package/cjs/storages/inLocalStorage/DefinitionsCacheInLocal.js +194 -0
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +9 -9
- package/cjs/storages/inLocalStorage/index.js +6 -6
- package/cjs/storages/inLocalStorage/validateCache.js +3 -3
- package/cjs/storages/inMemory/DefinitionsCacheInMemory.js +121 -0
- package/cjs/storages/inMemory/InMemoryStorage.js +4 -4
- package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -5
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +4 -4
- package/cjs/storages/inMemory/SegmentsCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +3 -3
- package/cjs/storages/inRedis/{SplitsCacheInRedis.js → DefinitionsCacheInRedis.js} +77 -77
- package/cjs/storages/inRedis/index.js +2 -2
- package/cjs/storages/pluggable/DefinitionsCachePluggable.js +212 -0
- package/cjs/storages/pluggable/index.js +2 -2
- package/cjs/storages/setRolloutPlan.js +5 -5
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +5 -5
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +1 -1
- package/cjs/sync/polling/pollingManagerCS.js +7 -7
- package/cjs/sync/polling/syncTasks/definitionsSyncTask.js +1 -1
- package/cjs/sync/polling/updaters/definitionChangesUpdater.js +8 -8
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +1 -1
- package/cjs/sync/streaming/SSEHandler/index.js +1 -1
- package/cjs/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.js → DefinitionsUpdateWorker.js} +15 -15
- package/cjs/sync/streaming/constants.js +3 -3
- package/cjs/sync/streaming/pushManager.js +13 -13
- package/cjs/sync/submitters/telemetrySubmitter.js +1 -2
- package/cjs/sync/syncManagerOnline.js +4 -4
- package/cjs/utils/Backoff.js +20 -0
- package/cjs/utils/lang/sets.js +4 -4
- package/cjs/utils/settingsValidation/url.js +1 -1
- package/esm/evaluator/index.js +11 -11
- package/esm/logger/constants.js +1 -1
- package/esm/logger/messages/warn.js +0 -1
- package/esm/readiness/constants.js +3 -3
- package/esm/readiness/readinessManager.js +23 -23
- package/esm/readiness/sdkReadinessManager.js +1 -17
- package/esm/sdkClient/sdkLifecycle.js +2 -1
- package/esm/sdkClient/trackMethod.js +2 -2
- package/esm/sdkFactory/index.js +6 -6
- package/esm/sdkManager/index.js +2 -2
- package/esm/services/authProvider.js +63 -0
- package/esm/services/secureSplitHttpClient.js +35 -0
- package/esm/services/splitApi.js +7 -2
- package/esm/storages/AbstractDefinitionsCacheAsync.js +48 -0
- package/esm/storages/AbstractDefinitionsCacheSync.js +71 -0
- package/esm/storages/KeyBuilder.js +6 -6
- package/esm/storages/KeyBuilderCS.js +5 -5
- package/esm/storages/KeyBuilderSS.js +2 -2
- package/esm/storages/getRolloutPlan.js +3 -3
- package/esm/storages/inLocalStorage/DefinitionsCacheInLocal.js +191 -0
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +6 -6
- package/esm/storages/inLocalStorage/index.js +6 -6
- package/esm/storages/inLocalStorage/validateCache.js +3 -3
- package/esm/storages/inMemory/DefinitionsCacheInMemory.js +118 -0
- package/esm/storages/inMemory/InMemoryStorage.js +4 -4
- package/esm/storages/inMemory/InMemoryStorageCS.js +5 -5
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +1 -1
- package/esm/storages/inMemory/SegmentsCacheInMemory.js +1 -1
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +3 -3
- package/esm/storages/inRedis/{SplitsCacheInRedis.js → DefinitionsCacheInRedis.js} +76 -76
- package/esm/storages/inRedis/index.js +2 -2
- package/esm/storages/pluggable/DefinitionsCachePluggable.js +209 -0
- package/esm/storages/pluggable/index.js +2 -2
- package/esm/storages/setRolloutPlan.js +5 -5
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +6 -6
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +1 -1
- package/esm/sync/polling/pollingManagerCS.js +4 -4
- package/esm/sync/polling/syncTasks/definitionsSyncTask.js +1 -1
- package/esm/sync/polling/updaters/definitionChangesUpdater.js +9 -9
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +1 -1
- package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +2 -2
- package/esm/sync/streaming/SSEHandler/index.js +2 -2
- package/esm/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.js → DefinitionsUpdateWorker.js} +14 -14
- package/esm/sync/streaming/constants.js +2 -2
- package/esm/sync/streaming/pushManager.js +14 -14
- package/esm/sync/submitters/telemetrySubmitter.js +1 -2
- package/esm/sync/syncManagerOnline.js +3 -3
- package/esm/utils/Backoff.js +20 -0
- package/esm/utils/lang/sets.js +4 -4
- package/esm/utils/settingsValidation/url.js +1 -1
- package/package.json +1 -1
- package/src/dtos/types.ts +2 -2
- package/src/evaluator/index.ts +11 -11
- package/src/logger/constants.ts +1 -1
- package/src/logger/messages/warn.ts +0 -1
- package/src/readiness/constants.ts +3 -3
- package/src/readiness/readinessManager.ts +22 -22
- package/src/readiness/sdkReadinessManager.ts +1 -16
- package/src/readiness/types.ts +10 -16
- package/src/sdkClient/sdkLifecycle.ts +2 -1
- package/src/sdkClient/trackMethod.ts +2 -2
- package/src/sdkFactory/index.ts +5 -5
- package/src/sdkManager/index.ts +6 -6
- package/src/services/authProvider.ts +86 -0
- package/src/services/secureSplitHttpClient.ts +44 -0
- package/src/services/splitApi.ts +9 -3
- package/src/services/types.ts +4 -0
- package/src/storages/AbstractDefinitionsCacheAsync.ts +63 -0
- package/src/storages/AbstractDefinitionsCacheSync.ts +95 -0
- package/src/storages/KeyBuilder.ts +6 -6
- package/src/storages/KeyBuilderCS.ts +6 -6
- package/src/storages/KeyBuilderSS.ts +2 -2
- package/src/storages/getRolloutPlan.ts +3 -3
- package/src/storages/inLocalStorage/DefinitionsCacheInLocal.ts +234 -0
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +6 -6
- package/src/storages/inLocalStorage/index.ts +6 -6
- package/src/storages/inLocalStorage/validateCache.ts +4 -4
- package/src/storages/inMemory/DefinitionsCacheInMemory.ts +130 -0
- package/src/storages/inMemory/InMemoryStorage.ts +4 -4
- package/src/storages/inMemory/InMemoryStorageCS.ts +5 -5
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +1 -1
- package/src/storages/inMemory/SegmentsCacheInMemory.ts +1 -1
- package/src/storages/inMemory/TelemetryCacheInMemory.ts +3 -3
- package/src/storages/inRedis/{SplitsCacheInRedis.ts → DefinitionsCacheInRedis.ts} +67 -67
- package/src/storages/inRedis/index.ts +2 -2
- package/src/storages/pluggable/DefinitionsCachePluggable.ts +230 -0
- package/src/storages/pluggable/index.ts +2 -2
- package/src/storages/setRolloutPlan.ts +7 -7
- package/src/storages/types.ts +19 -19
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +8 -8
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +3 -3
- package/src/sync/polling/pollingManagerCS.ts +4 -4
- package/src/sync/polling/syncTasks/definitionsSyncTask.ts +1 -1
- package/src/sync/polling/types.ts +1 -1
- package/src/sync/polling/updaters/definitionChangesUpdater.ts +11 -11
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +1 -1
- package/src/sync/streaming/AuthClient/index.ts +2 -2
- package/src/sync/streaming/AuthClient/types.ts +14 -11
- package/src/sync/streaming/SSEClient/index.ts +2 -2
- package/src/sync/streaming/SSEClient/types.ts +2 -2
- package/src/sync/streaming/SSEHandler/NotificationKeeper.ts +2 -2
- package/src/sync/streaming/SSEHandler/index.ts +2 -2
- package/src/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.ts → DefinitionsUpdateWorker.ts} +16 -16
- package/src/sync/streaming/constants.ts +2 -2
- package/src/sync/streaming/pushManager.ts +16 -16
- package/src/sync/streaming/types.ts +2 -2
- package/src/sync/submitters/telemetrySubmitter.ts +1 -2
- package/src/sync/syncManagerOnline.ts +3 -3
- package/src/utils/Backoff.ts +21 -0
- package/src/utils/inputValidation/trafficTypeExistence.ts +2 -2
- package/src/utils/jwt/types.ts +1 -1
- package/src/utils/lang/sets.ts +4 -2
- package/src/utils/settingsValidation/url.ts +1 -1
- package/types/splitio.d.ts +23 -5
- package/cjs/storages/AbstractSplitsCacheAsync.js +0 -51
- package/cjs/storages/AbstractSplitsCacheSync.js +0 -76
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +0 -194
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +0 -121
- package/cjs/storages/pluggable/SplitsCachePluggable.js +0 -212
- package/esm/storages/AbstractSplitsCacheAsync.js +0 -48
- package/esm/storages/AbstractSplitsCacheSync.js +0 -71
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +0 -191
- package/esm/storages/inMemory/SplitsCacheInMemory.js +0 -118
- package/esm/storages/pluggable/SplitsCachePluggable.js +0 -209
- package/src/storages/AbstractSplitsCacheAsync.ts +0 -63
- package/src/storages/AbstractSplitsCacheSync.ts +0 -95
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +0 -234
- package/src/storages/inMemory/SplitsCacheInMemory.ts +0 -130
- package/src/storages/pluggable/SplitsCachePluggable.ts +0 -230
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { IDefinitionsCacheSync, IStorageSync } from './types';
|
|
2
|
+
import { IRBSegment, IDefinition } from '../dtos/types';
|
|
3
|
+
import { objectAssign } from '../utils/lang/objectAssign';
|
|
4
|
+
import { IN_SEGMENT, IN_LARGE_SEGMENT } from '../utils/constants';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This class provides a skeletal implementation of the IDefinitionsCacheSync interface
|
|
8
|
+
* to minimize the effort required to implement this interface.
|
|
9
|
+
*/
|
|
10
|
+
export abstract class AbstractDefinitionsCacheSync implements IDefinitionsCacheSync {
|
|
11
|
+
|
|
12
|
+
protected abstract add(definition: IDefinition): boolean
|
|
13
|
+
protected abstract remove(name: string): boolean
|
|
14
|
+
protected abstract setChangeNumber(changeNumber: number): boolean | void
|
|
15
|
+
|
|
16
|
+
update(toAdd: IDefinition[], toRemove: string[], changeNumber: number): boolean {
|
|
17
|
+
let updated = toAdd.map(addedFF => this.add(addedFF)).some(result => result);
|
|
18
|
+
updated = toRemove.map(removedFF => this.remove(removedFF)).some(result => result) || updated;
|
|
19
|
+
this.setChangeNumber(changeNumber);
|
|
20
|
+
return updated;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
abstract get(name: string): IDefinition | null
|
|
24
|
+
|
|
25
|
+
getMany(names: string[]): Record<string, IDefinition | null> {
|
|
26
|
+
const definitions: Record<string, IDefinition | null> = {};
|
|
27
|
+
names.forEach(name => {
|
|
28
|
+
definitions[name] = this.get(name);
|
|
29
|
+
});
|
|
30
|
+
return definitions;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
abstract getChangeNumber(): number
|
|
34
|
+
|
|
35
|
+
getAll(): IDefinition[] {
|
|
36
|
+
return this.getNames().map(key => this.get(key) as IDefinition);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
abstract getNames(): string[]
|
|
40
|
+
|
|
41
|
+
abstract trafficTypeExists(trafficType: string): boolean
|
|
42
|
+
|
|
43
|
+
abstract usesSegments(): boolean
|
|
44
|
+
|
|
45
|
+
abstract clear(): void
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Kill `name` definition and set `defaultTreatment` and `changeNumber`.
|
|
49
|
+
* Used for SPLIT_KILL push notifications.
|
|
50
|
+
*
|
|
51
|
+
* @returns `true` if the operation successed updating the definition, or `false` if no definition is updated,
|
|
52
|
+
* for instance, if the `changeNumber` is old, or if the definition is not found (e.g., `/splitchanges` hasn't been fetched yet), or if the storage fails to apply the update.
|
|
53
|
+
*/
|
|
54
|
+
killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean {
|
|
55
|
+
const definition = this.get(name);
|
|
56
|
+
|
|
57
|
+
if (definition && (!definition.changeNumber || definition.changeNumber < changeNumber)) {
|
|
58
|
+
const newDefinition = objectAssign({}, definition);
|
|
59
|
+
newDefinition.killed = true;
|
|
60
|
+
newDefinition.defaultTreatment = defaultTreatment;
|
|
61
|
+
newDefinition.changeNumber = changeNumber;
|
|
62
|
+
|
|
63
|
+
return this.add(newDefinition);
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
abstract getNamesBySets(sets: string[]): Set<string>[]
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Given a parsed definition, it returns a boolean flagging if its conditions use segments matchers (rules & whitelists).
|
|
74
|
+
* This util is intended to simplify the implementation of `definitionsCache::usesSegments` method
|
|
75
|
+
*/
|
|
76
|
+
export function usesSegments(ruleEntity: IDefinition | IRBSegment) {
|
|
77
|
+
const conditions = ruleEntity.conditions || [];
|
|
78
|
+
for (let i = 0; i < conditions.length; i++) {
|
|
79
|
+
const matchers = conditions[i].matcherGroup.matchers;
|
|
80
|
+
|
|
81
|
+
for (let j = 0; j < matchers.length; j++) {
|
|
82
|
+
const matcher = matchers[j].matcherType;
|
|
83
|
+
if (matcher === IN_SEGMENT || matcher === IN_LARGE_SEGMENT) return true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const excluded = (ruleEntity as IRBSegment).excluded;
|
|
88
|
+
if (excluded && excluded.segments && excluded.segments.length > 0) return true;
|
|
89
|
+
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function usesSegmentsSync(storage: Pick<IStorageSync, 'definitions' | 'rbSegments'>) {
|
|
94
|
+
return storage.definitions.usesSegments() || storage.rbSegments.usesSegments();
|
|
95
|
+
}
|
|
@@ -21,19 +21,19 @@ export class KeyBuilder {
|
|
|
21
21
|
return `${this.prefix}.trafficType.${trafficType}`;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
return `${this.prefix}.flagSet.${
|
|
24
|
+
buildSetKey(set: string) {
|
|
25
|
+
return `${this.prefix}.flagSet.${set}`;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
return `${this.prefix}.split.${
|
|
28
|
+
buildDefinitionKey(definitionName: string) {
|
|
29
|
+
return `${this.prefix}.split.${definitionName}`;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
buildDefinitionsTillKey() {
|
|
33
33
|
return `${this.prefix}.splits.till`;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
buildDefinitionKeyPrefix() {
|
|
37
37
|
return `${this.prefix}.split.`;
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -9,13 +9,13 @@ export interface MySegmentsKeyBuilder {
|
|
|
9
9
|
|
|
10
10
|
export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
|
|
11
11
|
|
|
12
|
-
protected readonly
|
|
12
|
+
protected readonly regexDefinitionsCacheKey: RegExp;
|
|
13
13
|
protected readonly matchingKey: string;
|
|
14
14
|
|
|
15
15
|
constructor(prefix: string, matchingKey: string) {
|
|
16
16
|
super(prefix);
|
|
17
17
|
this.matchingKey = matchingKey;
|
|
18
|
-
this.
|
|
18
|
+
this.regexDefinitionsCacheKey = new RegExp(`^${prefix}\\.(splits?|trafficType|flagSet)\\.`);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -35,15 +35,15 @@ export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
|
|
|
35
35
|
return `${this.prefix}.splits.lastUpdated`;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
return this.
|
|
38
|
+
isDefinitionsCacheKey(key: string) {
|
|
39
|
+
return this.regexDefinitionsCacheKey.test(key);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
buildTillKey() {
|
|
43
43
|
return `${this.prefix}.${this.matchingKey}.segments.till`;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
isDefinitionKey(key: string) {
|
|
47
47
|
return startsWith(key, `${this.prefix}.split.`);
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -51,7 +51,7 @@ export class KeyBuilderCS extends KeyBuilder implements MySegmentsKeyBuilder {
|
|
|
51
51
|
return startsWith(key, `${this.prefix}.rbsegment.`);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
buildDefinitionsWithSegmentCountKey() {
|
|
55
55
|
return `${this.prefix}.splits.usingSegments`;
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -49,8 +49,8 @@ export class KeyBuilderSS extends KeyBuilder {
|
|
|
49
49
|
return `${this.prefix}.events`;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
return `${this.
|
|
52
|
+
searchPatternForDefinitionKeys() {
|
|
53
|
+
return `${this.buildDefinitionKeyPrefix()}*`;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
searchPatternForRBSegmentKeys() {
|
|
@@ -12,16 +12,16 @@ import { IMembershipsResponse, IMySegmentsResponse } from '../dtos/types';
|
|
|
12
12
|
export function getRolloutPlan(log: ILogger, storage: IStorageSync, options: SplitIO.RolloutPlanOptions = {}): RolloutPlan {
|
|
13
13
|
|
|
14
14
|
const { keys, exposeSegments } = options;
|
|
15
|
-
const {
|
|
15
|
+
const { definitions, segments, rbSegments } = storage;
|
|
16
16
|
|
|
17
17
|
log.debug(`storage: get feature flags${keys ? `, and memberships for keys: ${keys}` : ''}${exposeSegments ? ', and segments' : ''}`);
|
|
18
18
|
|
|
19
19
|
return {
|
|
20
20
|
splitChanges: {
|
|
21
21
|
ff: {
|
|
22
|
-
t:
|
|
22
|
+
t: definitions.getChangeNumber(),
|
|
23
23
|
s: -1,
|
|
24
|
-
d:
|
|
24
|
+
d: definitions.getAll(),
|
|
25
25
|
},
|
|
26
26
|
rbs: {
|
|
27
27
|
t: rbSegments.getChangeNumber(),
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { IDefinition } from '../../dtos/types';
|
|
2
|
+
import { AbstractDefinitionsCacheSync, usesSegments } from '../AbstractDefinitionsCacheSync';
|
|
3
|
+
import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
|
|
4
|
+
import { KeyBuilderCS } from '../KeyBuilderCS';
|
|
5
|
+
import { ILogger } from '../../logger/types';
|
|
6
|
+
import { LOG_PREFIX } from './constants';
|
|
7
|
+
import { ISettings } from '../../types';
|
|
8
|
+
import { setToArray } from '../../utils/lang/sets';
|
|
9
|
+
import { StorageAdapter } from '../types';
|
|
10
|
+
|
|
11
|
+
export class DefinitionsCacheInLocal extends AbstractDefinitionsCacheSync {
|
|
12
|
+
|
|
13
|
+
private readonly keys: KeyBuilderCS;
|
|
14
|
+
private readonly log: ILogger;
|
|
15
|
+
private readonly setsFilter: string[];
|
|
16
|
+
private hasSync?: boolean;
|
|
17
|
+
private readonly storage: StorageAdapter;
|
|
18
|
+
|
|
19
|
+
constructor(settings: ISettings, keys: KeyBuilderCS, storage: StorageAdapter) {
|
|
20
|
+
super();
|
|
21
|
+
this.keys = keys;
|
|
22
|
+
this.log = settings.log;
|
|
23
|
+
this.setsFilter = settings.sync.__splitFiltersValidation.groupedFilters.bySet;
|
|
24
|
+
this.storage = storage;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private _decrementCount(key: string) {
|
|
28
|
+
const count = toNumber(this.storage.getItem(key)) - 1;
|
|
29
|
+
if (count > 0) this.storage.setItem(key, count + '');
|
|
30
|
+
else this.storage.removeItem(key);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private _decrementCounts(definition: IDefinition) {
|
|
34
|
+
try {
|
|
35
|
+
const ttKey = this.keys.buildTrafficTypeKey(definition.trafficTypeName);
|
|
36
|
+
this._decrementCount(ttKey);
|
|
37
|
+
|
|
38
|
+
if (usesSegments(definition)) {
|
|
39
|
+
const segmentsCountKey = this.keys.buildDefinitionsWithSegmentCountKey();
|
|
40
|
+
this._decrementCount(segmentsCountKey);
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {
|
|
43
|
+
this.log.error(LOG_PREFIX + e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private _incrementCounts(definition: IDefinition) {
|
|
48
|
+
try {
|
|
49
|
+
const ttKey = this.keys.buildTrafficTypeKey(definition.trafficTypeName);
|
|
50
|
+
this.storage.setItem(ttKey, (toNumber(this.storage.getItem(ttKey)) + 1) + '');
|
|
51
|
+
|
|
52
|
+
if (usesSegments(definition)) {
|
|
53
|
+
const segmentsCountKey = this.keys.buildDefinitionsWithSegmentCountKey();
|
|
54
|
+
this.storage.setItem(segmentsCountKey, (toNumber(this.storage.getItem(segmentsCountKey)) + 1) + '');
|
|
55
|
+
}
|
|
56
|
+
} catch (e) {
|
|
57
|
+
this.log.error(LOG_PREFIX + e);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Removes all definitions related data from localStorage (splits, counters, changeNumber and lastUpdated).
|
|
64
|
+
* We cannot simply call `localStorage.clear()` since that implies removing user items from the storage.
|
|
65
|
+
*/
|
|
66
|
+
clear() {
|
|
67
|
+
// collect item keys
|
|
68
|
+
const len = this.storage.length;
|
|
69
|
+
const accum = [];
|
|
70
|
+
for (let cur = 0; cur < len; cur++) {
|
|
71
|
+
const key = this.storage.key(cur);
|
|
72
|
+
if (key != null && this.keys.isDefinitionsCacheKey(key)) accum.push(key);
|
|
73
|
+
}
|
|
74
|
+
// remove items
|
|
75
|
+
accum.forEach(key => {
|
|
76
|
+
this.storage.removeItem(key);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
this.hasSync = false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
add(definition: IDefinition) {
|
|
83
|
+
const name = definition.name;
|
|
84
|
+
const definitionKey = this.keys.buildDefinitionKey(name);
|
|
85
|
+
const definitionFromStorage = this.storage.getItem(definitionKey);
|
|
86
|
+
const previousDefinition = definitionFromStorage ? JSON.parse(definitionFromStorage) : null;
|
|
87
|
+
|
|
88
|
+
if (previousDefinition) {
|
|
89
|
+
this._decrementCounts(previousDefinition);
|
|
90
|
+
this.removeFromSets(previousDefinition.name, previousDefinition.sets);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.storage.setItem(definitionKey, JSON.stringify(definition));
|
|
94
|
+
|
|
95
|
+
this._incrementCounts(definition);
|
|
96
|
+
this.addToSets(definition);
|
|
97
|
+
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
remove(name: string): boolean {
|
|
102
|
+
const definition = this.get(name);
|
|
103
|
+
if (!definition) return false;
|
|
104
|
+
|
|
105
|
+
this.storage.removeItem(this.keys.buildDefinitionKey(name));
|
|
106
|
+
|
|
107
|
+
this._decrementCounts(definition);
|
|
108
|
+
this.removeFromSets(definition.name, definition.sets);
|
|
109
|
+
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get(name: string): IDefinition | null {
|
|
114
|
+
const item = this.storage.getItem(this.keys.buildDefinitionKey(name));
|
|
115
|
+
return item && JSON.parse(item);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
setChangeNumber(changeNumber: number): boolean {
|
|
119
|
+
try {
|
|
120
|
+
this.storage.setItem(this.keys.buildDefinitionsTillKey(), changeNumber + '');
|
|
121
|
+
// update "last updated" timestamp with current time
|
|
122
|
+
this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
123
|
+
this.hasSync = true;
|
|
124
|
+
return true;
|
|
125
|
+
} catch (e) {
|
|
126
|
+
this.log.error(LOG_PREFIX + e);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getChangeNumber(): number {
|
|
132
|
+
const n = -1;
|
|
133
|
+
let value: string | number | null = this.storage.getItem(this.keys.buildDefinitionsTillKey());
|
|
134
|
+
|
|
135
|
+
if (value !== null) {
|
|
136
|
+
value = parseInt(value, 10);
|
|
137
|
+
|
|
138
|
+
return isNaNNumber(value) ? n : value;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return n;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getNames(): string[] {
|
|
145
|
+
const len = this.storage.length;
|
|
146
|
+
const accum = [];
|
|
147
|
+
|
|
148
|
+
let cur = 0;
|
|
149
|
+
|
|
150
|
+
while (cur < len) {
|
|
151
|
+
const key = this.storage.key(cur);
|
|
152
|
+
|
|
153
|
+
if (key != null && this.keys.isDefinitionKey(key)) accum.push(this.keys.extractKey(key));
|
|
154
|
+
|
|
155
|
+
cur++;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return accum;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
trafficTypeExists(trafficType: string): boolean {
|
|
162
|
+
const ttCount = toNumber(this.storage.getItem(this.keys.buildTrafficTypeKey(trafficType)));
|
|
163
|
+
return isFiniteNumber(ttCount) && ttCount > 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
usesSegments() {
|
|
167
|
+
// If cache hasn't been synchronized with the cloud, assume we need them.
|
|
168
|
+
if (!this.hasSync) return true;
|
|
169
|
+
|
|
170
|
+
const storedCount = this.storage.getItem(this.keys.buildDefinitionsWithSegmentCountKey());
|
|
171
|
+
const definitionsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
|
|
172
|
+
|
|
173
|
+
return isFiniteNumber(definitionsWithSegmentsCount) ?
|
|
174
|
+
definitionsWithSegmentsCount > 0 :
|
|
175
|
+
true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
getNamesBySets(sets: string[]): Set<string>[] {
|
|
179
|
+
return sets.map(set => {
|
|
180
|
+
const setKey = this.keys.buildSetKey(set);
|
|
181
|
+
const setFromStorage = this.storage.getItem(setKey);
|
|
182
|
+
|
|
183
|
+
return new Set(setFromStorage ? JSON.parse(setFromStorage) : []);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private addToSets(definition: IDefinition) {
|
|
188
|
+
if (!definition.sets) return;
|
|
189
|
+
|
|
190
|
+
definition.sets.forEach(set => {
|
|
191
|
+
|
|
192
|
+
if (this.setsFilter.length > 0 && !this.setsFilter.some(filterSet => filterSet === set)) return;
|
|
193
|
+
|
|
194
|
+
const setKey = this.keys.buildSetKey(set);
|
|
195
|
+
|
|
196
|
+
const setFromStorage = this.storage.getItem(setKey);
|
|
197
|
+
|
|
198
|
+
const setCache = new Set(setFromStorage ? JSON.parse(setFromStorage) : []);
|
|
199
|
+
|
|
200
|
+
if (setCache.has(definition.name)) return;
|
|
201
|
+
|
|
202
|
+
setCache.add(definition.name);
|
|
203
|
+
|
|
204
|
+
this.storage.setItem(setKey, JSON.stringify(setToArray(setCache)));
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private removeFromSets(definitionName: string, sets?: string[] | null) {
|
|
209
|
+
if (!sets) return;
|
|
210
|
+
|
|
211
|
+
sets.forEach(set => {
|
|
212
|
+
this.removeNames(set, definitionName);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private removeNames(setName: string, definitionName: string) {
|
|
217
|
+
const setKey = this.keys.buildSetKey(setName);
|
|
218
|
+
|
|
219
|
+
const setFromStorage = this.storage.getItem(setKey);
|
|
220
|
+
|
|
221
|
+
if (!setFromStorage) return;
|
|
222
|
+
|
|
223
|
+
const setCache = new Set(JSON.parse(setFromStorage));
|
|
224
|
+
setCache.delete(definitionName);
|
|
225
|
+
|
|
226
|
+
if (setCache.size === 0) {
|
|
227
|
+
this.storage.removeItem(setKey);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
this.storage.setItem(setKey, JSON.stringify(setToArray(setCache)));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
}
|
|
@@ -3,7 +3,7 @@ import { ILogger } from '../../logger/types';
|
|
|
3
3
|
import { ISettings } from '../../types';
|
|
4
4
|
import { isFiniteNumber, isNaNNumber, toNumber } from '../../utils/lang';
|
|
5
5
|
import { setToArray } from '../../utils/lang/sets';
|
|
6
|
-
import { usesSegments } from '../
|
|
6
|
+
import { usesSegments } from '../AbstractDefinitionsCacheSync';
|
|
7
7
|
import { KeyBuilderCS } from '../KeyBuilderCS';
|
|
8
8
|
import { IRBSegmentsCacheSync, StorageAdapter } from '../types';
|
|
9
9
|
import { LOG_PREFIX } from './constants';
|
|
@@ -42,7 +42,7 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
private updateSegmentCount(diff: number) {
|
|
45
|
-
const segmentsCountKey = this.keys.
|
|
45
|
+
const segmentsCountKey = this.keys.buildDefinitionsWithSegmentCountKey();
|
|
46
46
|
const count = toNumber(this.storage.getItem(segmentsCountKey)) + diff;
|
|
47
47
|
if (count > 0) this.storage.setItem(segmentsCountKey, count + '');
|
|
48
48
|
else this.storage.removeItem(segmentsCountKey);
|
|
@@ -121,11 +121,11 @@ export class RBSegmentsCacheInLocal implements IRBSegmentsCacheSync {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
usesSegments(): boolean {
|
|
124
|
-
const storedCount = this.storage.getItem(this.keys.
|
|
125
|
-
const
|
|
124
|
+
const storedCount = this.storage.getItem(this.keys.buildDefinitionsWithSegmentCountKey());
|
|
125
|
+
const definitionsWithSegmentsCount = storedCount === null ? 0 : toNumber(storedCount);
|
|
126
126
|
|
|
127
|
-
return isFiniteNumber(
|
|
128
|
-
|
|
127
|
+
return isFiniteNumber(definitionsWithSegmentsCount) ?
|
|
128
|
+
definitionsWithSegmentsCount > 0 :
|
|
129
129
|
true;
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -5,7 +5,7 @@ import { IStorageFactoryParams, IStorageSync, IStorageSyncFactory, StorageAdapte
|
|
|
5
5
|
import { validatePrefix } from '../KeyBuilder';
|
|
6
6
|
import { KeyBuilderCS, myLargeSegmentsKeyBuilder } from '../KeyBuilderCS';
|
|
7
7
|
import { isLocalStorageAvailable, isValidStorageWrapper, isWebStorage } from '../../utils/env/isLocalStorageAvailable';
|
|
8
|
-
import {
|
|
8
|
+
import { DefinitionsCacheInLocal } from './DefinitionsCacheInLocal';
|
|
9
9
|
import { RBSegmentsCacheInLocal } from './RBSegmentsCacheInLocal';
|
|
10
10
|
import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
|
|
11
11
|
import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
|
|
@@ -50,26 +50,26 @@ export function InLocalStorage(options: SplitIO.InLocalStorageOptions = {}): ISt
|
|
|
50
50
|
const matchingKey = getMatching(settings.core.key);
|
|
51
51
|
const keys = new KeyBuilderCS(prefix, matchingKey);
|
|
52
52
|
|
|
53
|
-
const
|
|
53
|
+
const definitions = new DefinitionsCacheInLocal(settings, keys, storage);
|
|
54
54
|
const rbSegments = new RBSegmentsCacheInLocal(settings, keys, storage);
|
|
55
55
|
const segments = new MySegmentsCacheInLocal(log, keys, storage);
|
|
56
56
|
const largeSegments = new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey), storage);
|
|
57
57
|
let validateCachePromise: Promise<SplitIO.SdkReadyMetadata> | undefined;
|
|
58
58
|
|
|
59
59
|
return {
|
|
60
|
-
|
|
60
|
+
definitions,
|
|
61
61
|
rbSegments,
|
|
62
62
|
segments,
|
|
63
63
|
largeSegments,
|
|
64
64
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
65
65
|
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
66
66
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
67
|
-
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(
|
|
67
|
+
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(definitions, segments) : undefined,
|
|
68
68
|
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
69
69
|
|
|
70
70
|
validateCache() {
|
|
71
71
|
if (!validateCachePromise) {
|
|
72
|
-
validateCachePromise = validateCache(options, storage, settings, keys,
|
|
72
|
+
validateCachePromise = validateCache(options, storage, settings, keys, definitions, rbSegments, segments, largeSegments);
|
|
73
73
|
}
|
|
74
74
|
return validateCachePromise;
|
|
75
75
|
},
|
|
@@ -86,7 +86,7 @@ export function InLocalStorage(options: SplitIO.InLocalStorageOptions = {}): ISt
|
|
|
86
86
|
shared(matchingKey: string) {
|
|
87
87
|
|
|
88
88
|
return {
|
|
89
|
-
|
|
89
|
+
definitions: this.definitions,
|
|
90
90
|
rbSegments: this.rbSegments,
|
|
91
91
|
segments: new MySegmentsCacheInLocal(log, new KeyBuilderCS(prefix, matchingKey), storage),
|
|
92
92
|
largeSegments: new MySegmentsCacheInLocal(log, myLargeSegmentsKeyBuilder(prefix, matchingKey), storage),
|
|
@@ -2,7 +2,7 @@ import { ISettings } from '../../types';
|
|
|
2
2
|
import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
|
|
3
3
|
import { getStorageHash } from '../KeyBuilder';
|
|
4
4
|
import { LOG_PREFIX } from './constants';
|
|
5
|
-
import type {
|
|
5
|
+
import type { DefinitionsCacheInLocal } from './DefinitionsCacheInLocal';
|
|
6
6
|
import type { RBSegmentsCacheInLocal } from './RBSegmentsCacheInLocal';
|
|
7
7
|
import type { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
|
|
8
8
|
import { KeyBuilderCS } from '../KeyBuilderCS';
|
|
@@ -68,11 +68,11 @@ function validateExpiration(options: SplitIO.InLocalStorageOptions, storage: Sto
|
|
|
68
68
|
*
|
|
69
69
|
* @returns Metadata object with `initialCacheLoad` (true if is fresh install, false if is ready from cache) and `lastUpdateTimestamp` (timestamp of last cache update or undefined)
|
|
70
70
|
*/
|
|
71
|
-
export function validateCache(options: SplitIO.InLocalStorageOptions, storage: StorageAdapter, settings: ISettings, keys: KeyBuilderCS,
|
|
71
|
+
export function validateCache(options: SplitIO.InLocalStorageOptions, storage: StorageAdapter, settings: ISettings, keys: KeyBuilderCS, definitions: DefinitionsCacheInLocal, rbSegments: RBSegmentsCacheInLocal, segments: MySegmentsCacheInLocal, largeSegments: MySegmentsCacheInLocal): Promise<SplitIO.SdkReadyMetadata> {
|
|
72
72
|
|
|
73
73
|
return Promise.resolve(storage.load && storage.load()).then(() => {
|
|
74
74
|
const currentTimestamp = Date.now();
|
|
75
|
-
const isThereCache =
|
|
75
|
+
const isThereCache = definitions.getChangeNumber() > -1;
|
|
76
76
|
|
|
77
77
|
// Get lastUpdateTimestamp from storage
|
|
78
78
|
const lastUpdatedTimestampStr = storage.getItem(keys.buildLastUpdatedKey());
|
|
@@ -80,7 +80,7 @@ export function validateCache(options: SplitIO.InLocalStorageOptions, storage: S
|
|
|
80
80
|
const lastUpdateTimestamp = (!isNaNNumber(lastUpdatedTimestamp) && lastUpdatedTimestamp !== undefined) ? lastUpdatedTimestamp : undefined;
|
|
81
81
|
|
|
82
82
|
if (validateExpiration(options, storage, settings, keys, currentTimestamp, isThereCache)) {
|
|
83
|
-
|
|
83
|
+
definitions.clear();
|
|
84
84
|
rbSegments.clear();
|
|
85
85
|
segments.clear();
|
|
86
86
|
largeSegments.clear();
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { IDefinition, ISplitFiltersValidation } from '../../dtos/types';
|
|
2
|
+
import { AbstractDefinitionsCacheSync, usesSegments } from '../AbstractDefinitionsCacheSync';
|
|
3
|
+
import { isFiniteNumber } from '../../utils/lang';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Default IDefinitionsCacheSync implementation that stores split definitions in memory.
|
|
7
|
+
*/
|
|
8
|
+
export class DefinitionsCacheInMemory extends AbstractDefinitionsCacheSync {
|
|
9
|
+
|
|
10
|
+
private setsFilter: string[];
|
|
11
|
+
private definitionsCache: Record<string, IDefinition> = {};
|
|
12
|
+
private ttCache: Record<string, number> = {};
|
|
13
|
+
private changeNumber: number = -1;
|
|
14
|
+
private segmentsCount: number = 0;
|
|
15
|
+
private setsCache: Record<string, Set<string>> = {};
|
|
16
|
+
|
|
17
|
+
constructor(splitFiltersValidation?: ISplitFiltersValidation) {
|
|
18
|
+
super();
|
|
19
|
+
this.setsFilter = splitFiltersValidation ? splitFiltersValidation.groupedFilters.bySet : [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
clear() {
|
|
23
|
+
this.definitionsCache = {};
|
|
24
|
+
this.ttCache = {};
|
|
25
|
+
this.changeNumber = -1;
|
|
26
|
+
this.segmentsCount = 0;
|
|
27
|
+
this.setsCache = {};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
add(definition: IDefinition): boolean {
|
|
31
|
+
const name = definition.name;
|
|
32
|
+
const previousDefinition = this.get(name);
|
|
33
|
+
if (previousDefinition) { // We had this Split already
|
|
34
|
+
|
|
35
|
+
const previousTtName = previousDefinition.trafficTypeName;
|
|
36
|
+
this.ttCache[previousTtName]--;
|
|
37
|
+
if (!this.ttCache[previousTtName]) delete this.ttCache[previousTtName];
|
|
38
|
+
|
|
39
|
+
this.removeFromFlagSets(previousDefinition.name, previousDefinition.sets);
|
|
40
|
+
|
|
41
|
+
// Subtract from segments count for the previous version of this Split
|
|
42
|
+
if (usesSegments(previousDefinition)) this.segmentsCount--;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Store the Split.
|
|
46
|
+
this.definitionsCache[name] = definition;
|
|
47
|
+
// Update TT cache
|
|
48
|
+
const ttName = definition.trafficTypeName;
|
|
49
|
+
this.ttCache[ttName] = (this.ttCache[ttName] || 0) + 1;
|
|
50
|
+
this.addToFlagSets(definition);
|
|
51
|
+
|
|
52
|
+
// Add to segments count for the new version of the Split
|
|
53
|
+
if (usesSegments(definition)) this.segmentsCount++;
|
|
54
|
+
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
remove(name: string): boolean {
|
|
59
|
+
const definition = this.get(name);
|
|
60
|
+
if (!definition) return false;
|
|
61
|
+
|
|
62
|
+
// Delete the Split
|
|
63
|
+
delete this.definitionsCache[name];
|
|
64
|
+
|
|
65
|
+
const ttName = definition.trafficTypeName;
|
|
66
|
+
this.ttCache[ttName]--; // Update tt cache
|
|
67
|
+
if (!this.ttCache[ttName]) delete this.ttCache[ttName];
|
|
68
|
+
this.removeFromFlagSets(definition.name, definition.sets);
|
|
69
|
+
|
|
70
|
+
// Update the segments count.
|
|
71
|
+
if (usesSegments(definition)) this.segmentsCount--;
|
|
72
|
+
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get(name: string): IDefinition | null {
|
|
77
|
+
return this.definitionsCache[name] || null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
setChangeNumber(changeNumber: number): boolean {
|
|
81
|
+
this.changeNumber = changeNumber;
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getChangeNumber(): number {
|
|
86
|
+
return this.changeNumber;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getNames(): string[] {
|
|
90
|
+
return Object.keys(this.definitionsCache);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
trafficTypeExists(trafficType: string): boolean {
|
|
94
|
+
return isFiniteNumber(this.ttCache[trafficType]) && this.ttCache[trafficType] > 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
usesSegments(): boolean {
|
|
98
|
+
return this.getChangeNumber() === -1 || this.segmentsCount > 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
getNamesBySets(sets: string[]): Set<string>[] {
|
|
102
|
+
return sets.map(set => this.setsCache[set] || new Set());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private addToFlagSets(featureFlag: IDefinition) {
|
|
106
|
+
if (!featureFlag.sets) return;
|
|
107
|
+
featureFlag.sets.forEach(featureFlagSet => {
|
|
108
|
+
|
|
109
|
+
if (this.setsFilter.length > 0 && !this.setsFilter.some(filterFlagSet => filterFlagSet === featureFlagSet)) return;
|
|
110
|
+
|
|
111
|
+
if (!this.setsCache[featureFlagSet]) this.setsCache[featureFlagSet] = new Set([]);
|
|
112
|
+
|
|
113
|
+
this.setsCache[featureFlagSet].add(featureFlag.name);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private removeFromFlagSets(featureFlagName: string, sets?: string[] | null) {
|
|
118
|
+
if (!sets) return;
|
|
119
|
+
sets.forEach(set => {
|
|
120
|
+
this.removeNames(set, featureFlagName);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private removeNames(setName: string, featureFlagName: string) {
|
|
125
|
+
if (!this.setsCache[setName]) return;
|
|
126
|
+
this.setsCache[setName].delete(featureFlagName);
|
|
127
|
+
if (this.setsCache[setName].size === 0) delete this.setsCache[setName];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
}
|