@splitsoftware/splitio-commons 1.11.0 → 1.12.0-rc.0
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 +15 -9
- package/cjs/evaluator/index.js +19 -3
- package/cjs/logger/constants.js +6 -4
- package/cjs/logger/messages/warn.js +5 -3
- package/cjs/sdkClient/client.js +19 -16
- package/cjs/sdkClient/clientInputValidation.js +16 -16
- package/cjs/sdkFactory/index.js +1 -1
- package/cjs/sdkManager/index.js +14 -13
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +3 -10
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +2 -10
- package/cjs/storages/inRedis/RedisAdapter.js +32 -13
- package/cjs/storages/inRedis/SegmentsCacheInRedis.js +2 -2
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +39 -22
- package/cjs/storages/inRedis/index.js +1 -1
- package/cjs/storages/pluggable/SplitsCachePluggable.js +28 -11
- package/cjs/storages/pluggable/index.js +1 -1
- package/cjs/utils/constants/index.js +16 -2
- package/cjs/utils/inputValidation/index.js +5 -5
- package/cjs/utils/inputValidation/{splitExistance.js → splitExistence.js} +3 -3
- package/cjs/utils/inputValidation/{trafficTypeExistance.js → trafficTypeExistence.js} +6 -6
- package/cjs/utils/lang/sets.js +11 -1
- package/cjs/utils/settingsValidation/index.js +1 -1
- package/cjs/utils/settingsValidation/splitFilters.js +25 -17
- package/esm/evaluator/index.js +20 -4
- package/esm/logger/constants.js +4 -2
- package/esm/logger/messages/warn.js +5 -3
- package/esm/sdkClient/client.js +20 -17
- package/esm/sdkClient/clientInputValidation.js +18 -18
- package/esm/sdkFactory/index.js +1 -1
- package/esm/sdkManager/index.js +11 -10
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +4 -11
- package/esm/storages/inMemory/SplitsCacheInMemory.js +3 -11
- package/esm/storages/inRedis/RedisAdapter.js +32 -13
- package/esm/storages/inRedis/SegmentsCacheInRedis.js +2 -2
- package/esm/storages/inRedis/SplitsCacheInRedis.js +40 -23
- package/esm/storages/inRedis/index.js +1 -1
- package/esm/storages/pluggable/SplitsCachePluggable.js +29 -12
- package/esm/storages/pluggable/index.js +1 -1
- package/esm/utils/constants/index.js +14 -0
- package/esm/utils/inputValidation/index.js +2 -2
- package/esm/utils/inputValidation/{splitExistance.js → splitExistence.js} +1 -1
- package/esm/utils/inputValidation/{trafficTypeExistance.js → trafficTypeExistence.js} +4 -4
- package/esm/utils/lang/sets.js +9 -0
- package/esm/utils/settingsValidation/index.js +1 -1
- package/esm/utils/settingsValidation/splitFilters.js +17 -9
- package/package.json +1 -1
- package/src/evaluator/index.ts +24 -4
- package/src/logger/constants.ts +4 -2
- package/src/logger/messages/warn.ts +9 -7
- package/src/sdkClient/client.ts +18 -18
- package/src/sdkClient/clientInputValidation.ts +18 -18
- package/src/sdkFactory/index.ts +1 -1
- package/src/sdkFactory/types.ts +3 -7
- package/src/sdkManager/index.ts +14 -14
- package/src/storages/AbstractSplitsCacheAsync.ts +1 -1
- package/src/storages/AbstractSplitsCacheSync.ts +1 -1
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +8 -15
- package/src/storages/inMemory/SplitsCacheInMemory.ts +6 -14
- package/src/storages/inRedis/EventsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/ImpressionsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/RedisAdapter.ts +38 -16
- package/src/storages/inRedis/SegmentsCacheInRedis.ts +5 -5
- package/src/storages/inRedis/SplitsCacheInRedis.ts +49 -28
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +2 -2
- package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +3 -3
- package/src/storages/inRedis/index.ts +1 -1
- package/src/storages/pluggable/SplitsCachePluggable.ts +35 -13
- package/src/storages/pluggable/index.ts +1 -1
- package/src/storages/types.ts +5 -5
- package/src/trackers/impressionObserver/utils.ts +1 -1
- package/src/types.ts +0 -2
- package/src/utils/constants/index.ts +16 -0
- package/src/utils/inputValidation/index.ts +2 -2
- package/src/utils/inputValidation/{splitExistance.ts → splitExistence.ts} +1 -1
- package/src/utils/inputValidation/{trafficTypeExistance.ts → trafficTypeExistence.ts} +4 -4
- package/src/utils/lang/sets.ts +9 -1
- package/src/utils/redis/RedisMock.ts +1 -3
- package/src/utils/settingsValidation/index.ts +1 -1
- package/src/utils/settingsValidation/splitFilters.ts +19 -11
- package/types/evaluator/index.d.ts +1 -1
- package/types/logger/constants.d.ts +4 -2
- package/types/sdkClient/identity.d.ts +6 -0
- package/types/sdkFactory/types.d.ts +3 -3
- package/types/sdkManager/index.d.ts +2 -3
- package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
- package/types/storages/AbstractSplitsCacheSync.d.ts +1 -1
- package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +1 -1
- package/types/storages/inMemory/SplitsCacheInMemory.d.ts +1 -1
- package/types/storages/inRedis/EventsCacheInRedis.d.ts +2 -2
- package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +3 -2
- package/types/storages/inRedis/ImpressionsCacheInRedis.d.ts +2 -2
- package/types/storages/inRedis/RedisAdapter.d.ts +1 -1
- package/types/storages/inRedis/SegmentsCacheInRedis.d.ts +3 -3
- package/types/storages/inRedis/SplitsCacheInRedis.d.ts +10 -14
- package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +2 -2
- package/types/storages/inRedis/{uniqueKeysCacheInRedis.d.ts → UniqueKeysCacheInRedis.d.ts} +3 -2
- package/types/storages/pluggable/SplitsCachePluggable.d.ts +10 -9
- package/types/storages/types.d.ts +5 -5
- package/types/trackers/impressionObserver/utils.d.ts +1 -1
- package/types/types.d.ts +0 -2
- package/types/utils/constants/index.d.ts +12 -0
- package/types/utils/inputValidation/index.d.ts +2 -2
- package/types/utils/inputValidation/sdkKey.d.ts +7 -0
- package/types/utils/inputValidation/splitExistence.d.ts +7 -0
- package/types/utils/inputValidation/trafficTypeExistence.d.ts +9 -0
- package/types/utils/lang/sets.d.ts +1 -0
- package/types/utils/settingsValidation/splitFilters.d.ts +3 -2
- package/types/myLogger.d.ts +0 -5
- package/types/sdkClient/types.d.ts +0 -18
- package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
- package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
- package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
- package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
- package/types/storages/metadataBuilder.d.ts +0 -3
- package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
- package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
- package/types/sync/offline/updaters/splitChangesUpdater.d.ts +0 -0
- package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
- package/types/sync/submitters/impressionCountsSubmitterInRedis.d.ts +0 -5
- package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
- package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
- package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
- package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
- package/types/sync/submitters/uniqueKeysSubmitterInRedis.d.ts +0 -5
- package/types/sync/syncTaskComposite.d.ts +0 -5
- package/types/trackers/filter/bloomFilter.d.ts +0 -10
- package/types/trackers/filter/dictionaryFilter.d.ts +0 -8
- package/types/trackers/filter/types.d.ts +0 -5
- package/types/utils/timeTracker/index.d.ts +0 -70
- /package/types/storages/inMemory/{uniqueKeysCacheInMemory.d.ts → UniqueKeysCacheInMemory.d.ts} +0 -0
- /package/types/storages/inMemory/{uniqueKeysCacheInMemoryCS.d.ts → UniqueKeysCacheInMemoryCS.d.ts} +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
|
|
2
2
|
import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
3
|
-
import { Redis } from 'ioredis';
|
|
4
3
|
import { ILogger } from '../../logger/types';
|
|
5
4
|
import { LOG_PREFIX } from './constants';
|
|
6
|
-
import { ISplit } from '../../dtos/types';
|
|
5
|
+
import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
|
|
7
6
|
import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
|
|
8
|
-
import { ISet } from '../../utils/lang/sets';
|
|
7
|
+
import { ISet, _Set, returnDifference } from '../../utils/lang/sets';
|
|
8
|
+
import type { RedisAdapter } from './RedisAdapter';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Discard errors for an answer of multiple operations.
|
|
@@ -24,15 +24,17 @@ function processPipelineAnswer(results: Array<[Error | null, string]>): string[]
|
|
|
24
24
|
export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
25
25
|
|
|
26
26
|
private readonly log: ILogger;
|
|
27
|
-
private readonly redis:
|
|
27
|
+
private readonly redis: RedisAdapter;
|
|
28
28
|
private readonly keys: KeyBuilderSS;
|
|
29
29
|
private redisError?: string;
|
|
30
|
+
private readonly flagSetsFilter: string[];
|
|
30
31
|
|
|
31
|
-
constructor(log: ILogger, keys: KeyBuilderSS, redis:
|
|
32
|
+
constructor(log: ILogger, keys: KeyBuilderSS, redis: RedisAdapter, splitFiltersValidation?: ISplitFiltersValidation) {
|
|
32
33
|
super();
|
|
33
34
|
this.log = log;
|
|
34
35
|
this.redis = redis;
|
|
35
36
|
this.keys = keys;
|
|
37
|
+
this.flagSetsFilter = splitFiltersValidation ? splitFiltersValidation.groupedFilters.bySet : [];
|
|
36
38
|
|
|
37
39
|
// There is no need to listen for redis 'error' event, because in that case ioredis calls will be rejected and handled by redis storage adapters.
|
|
38
40
|
// But it is done just to avoid getting the ioredis message `Unhandled error event`.
|
|
@@ -57,6 +59,24 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
57
59
|
return this.redis.incr(ttKey);
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
private _updateFlagSets(featureFlagName: string, flagSetsOfRemovedFlag?: string[], flagSetsOfAddedFlag?: string[]) {
|
|
63
|
+
const removeFromFlagSets = returnDifference(flagSetsOfRemovedFlag, flagSetsOfAddedFlag);
|
|
64
|
+
|
|
65
|
+
let addToFlagSets = returnDifference(flagSetsOfAddedFlag, flagSetsOfRemovedFlag);
|
|
66
|
+
if (this.flagSetsFilter.length > 0) {
|
|
67
|
+
addToFlagSets = addToFlagSets.filter(flagSet => {
|
|
68
|
+
return this.flagSetsFilter.some(filterFlagSet => filterFlagSet === flagSet);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const items = [featureFlagName];
|
|
73
|
+
|
|
74
|
+
return Promise.all([
|
|
75
|
+
...removeFromFlagSets.map(flagSetName => this.redis.srem(this.keys.buildFlagSetKey(flagSetName), items)),
|
|
76
|
+
...addToFlagSets.map(flagSetName => this.redis.sadd(this.keys.buildFlagSetKey(flagSetName), items))
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
79
|
+
|
|
60
80
|
/**
|
|
61
81
|
* Add a given split.
|
|
62
82
|
* The returned promise is resolved when the operation success
|
|
@@ -66,16 +86,16 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
66
86
|
const splitKey = this.keys.buildSplitKey(name);
|
|
67
87
|
return this.redis.get(splitKey).then(splitFromStorage => {
|
|
68
88
|
|
|
69
|
-
// handling parsing
|
|
70
|
-
let parsedPreviousSplit: ISplit,
|
|
89
|
+
// handling parsing error
|
|
90
|
+
let parsedPreviousSplit: ISplit, stringifiedNewSplit;
|
|
71
91
|
try {
|
|
72
92
|
parsedPreviousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : undefined;
|
|
73
|
-
|
|
93
|
+
stringifiedNewSplit = JSON.stringify(split);
|
|
74
94
|
} catch (e) {
|
|
75
95
|
throw new Error('Error parsing feature flag definition: ' + e);
|
|
76
96
|
}
|
|
77
97
|
|
|
78
|
-
return this.redis.set(splitKey,
|
|
98
|
+
return this.redis.set(splitKey, stringifiedNewSplit).then(() => {
|
|
79
99
|
// avoid unnecessary increment/decrement operations
|
|
80
100
|
if (parsedPreviousSplit && parsedPreviousSplit.trafficTypeName === split.trafficTypeName) return;
|
|
81
101
|
|
|
@@ -83,7 +103,7 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
83
103
|
return this._incrementCounts(split).then(() => {
|
|
84
104
|
if (parsedPreviousSplit) return this._decrementCounts(parsedPreviousSplit);
|
|
85
105
|
});
|
|
86
|
-
});
|
|
106
|
+
}).then(() => this._updateFlagSets(name, parsedPreviousSplit && parsedPreviousSplit.sets, split.sets));
|
|
87
107
|
}).then(() => true);
|
|
88
108
|
}
|
|
89
109
|
|
|
@@ -101,11 +121,12 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
101
121
|
* The returned promise is resolved when the operation success, with 1 or 0 indicating if the split existed or not.
|
|
102
122
|
* or rejected if it fails (e.g., redis operation fails).
|
|
103
123
|
*/
|
|
104
|
-
removeSplit(name: string)
|
|
124
|
+
removeSplit(name: string) {
|
|
105
125
|
return this.getSplit(name).then((split) => {
|
|
106
126
|
if (split) {
|
|
107
|
-
this._decrementCounts(split);
|
|
127
|
+
return this._decrementCounts(split).then(() => this._updateFlagSets(name, split.sets));
|
|
108
128
|
}
|
|
129
|
+
}).then(() => {
|
|
109
130
|
return this.redis.del(this.keys.buildSplitKey(name));
|
|
110
131
|
});
|
|
111
132
|
}
|
|
@@ -174,7 +195,7 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
174
195
|
.then((listOfKeys) => this.redis.pipeline(listOfKeys.map(k => ['get', k])).exec())
|
|
175
196
|
.then(processPipelineAnswer)
|
|
176
197
|
.then((splitDefinitions) => splitDefinitions.map((splitDefinition) => {
|
|
177
|
-
return JSON.parse(splitDefinition
|
|
198
|
+
return JSON.parse(splitDefinition);
|
|
178
199
|
}));
|
|
179
200
|
}
|
|
180
201
|
|
|
@@ -190,14 +211,18 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
190
211
|
}
|
|
191
212
|
|
|
192
213
|
/**
|
|
193
|
-
* Get list of
|
|
194
|
-
* The returned promise is resolved with the list of
|
|
195
|
-
* or rejected if
|
|
196
|
-
* @todo this is a no-op method to be implemented
|
|
214
|
+
* Get list of feature flag names related to a given list of flag set names.
|
|
215
|
+
* The returned promise is resolved with the list of feature flag names per flag set,
|
|
216
|
+
* or rejected if the pipelined redis operation fails (e.g., timeout).
|
|
197
217
|
*/
|
|
198
|
-
getNamesByFlagSets(): Promise<ISet<string
|
|
199
|
-
this.
|
|
200
|
-
|
|
218
|
+
getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>[]> {
|
|
219
|
+
return this.redis.pipeline(flagSets.map(flagSet => ['smembers', this.keys.buildFlagSetKey(flagSet)])).exec()
|
|
220
|
+
.then((results) => results.map(([e, value], index) => {
|
|
221
|
+
if (e === null) return value;
|
|
222
|
+
|
|
223
|
+
this.log.error(LOG_PREFIX + `Could not read result from get members of flag set ${flagSets[index]} due to an error: ${e}`);
|
|
224
|
+
}))
|
|
225
|
+
.then(namesByFlagSets => namesByFlagSets.map(namesByFlagSet => new _Set(namesByFlagSet)));
|
|
201
226
|
}
|
|
202
227
|
|
|
203
228
|
/**
|
|
@@ -214,26 +239,22 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
214
239
|
|
|
215
240
|
ttCount = parseInt(ttCount as string, 10);
|
|
216
241
|
if (!isFiniteNumber(ttCount) || ttCount < 0) {
|
|
217
|
-
this.log.info(LOG_PREFIX + `Could not validate traffic type
|
|
242
|
+
this.log.info(LOG_PREFIX + `Could not validate traffic type existence of ${trafficType} due to data corruption of some sorts.`);
|
|
218
243
|
return false;
|
|
219
244
|
}
|
|
220
245
|
|
|
221
246
|
return ttCount > 0;
|
|
222
247
|
})
|
|
223
248
|
.catch(e => {
|
|
224
|
-
this.log.error(LOG_PREFIX + `Could not validate traffic type
|
|
249
|
+
this.log.error(LOG_PREFIX + `Could not validate traffic type existence of ${trafficType} due to an error: ${e}.`);
|
|
225
250
|
// If there is an error, bypass the validation so the event can get tracked.
|
|
226
251
|
return true;
|
|
227
252
|
});
|
|
228
253
|
}
|
|
229
254
|
|
|
230
|
-
|
|
231
|
-
* Delete everything in the current database.
|
|
232
|
-
*
|
|
233
|
-
* @NOTE documentation says it never fails.
|
|
234
|
-
*/
|
|
255
|
+
// @TODO remove or implement. It is not being used.
|
|
235
256
|
clear() {
|
|
236
|
-
return
|
|
257
|
+
return Promise.resolve();
|
|
237
258
|
}
|
|
238
259
|
|
|
239
260
|
/**
|
|
@@ -3,13 +3,13 @@ import { Method, MultiConfigs, MultiMethodExceptions, MultiMethodLatencies } fro
|
|
|
3
3
|
import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
4
4
|
import { ITelemetryCacheAsync } from '../types';
|
|
5
5
|
import { findLatencyIndex } from '../findLatencyIndex';
|
|
6
|
-
import { Redis } from 'ioredis';
|
|
7
6
|
import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter';
|
|
8
7
|
import { CONSUMER_MODE, STORAGE_REDIS } from '../../utils/constants';
|
|
9
8
|
import { isNaNNumber, isString } from '../../utils/lang';
|
|
10
9
|
import { _Map } from '../../utils/lang/maps';
|
|
11
10
|
import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory';
|
|
12
11
|
import { parseLatencyField, parseExceptionField, parseMetadata } from '../utils';
|
|
12
|
+
import type { RedisAdapter } from './RedisAdapter';
|
|
13
13
|
|
|
14
14
|
export class TelemetryCacheInRedis implements ITelemetryCacheAsync {
|
|
15
15
|
|
|
@@ -19,7 +19,7 @@ export class TelemetryCacheInRedis implements ITelemetryCacheAsync {
|
|
|
19
19
|
* @param keys Key builder.
|
|
20
20
|
* @param redis Redis client.
|
|
21
21
|
*/
|
|
22
|
-
constructor(private readonly log: ILogger, private readonly keys: KeyBuilderSS, private readonly redis:
|
|
22
|
+
constructor(private readonly log: ILogger, private readonly keys: KeyBuilderSS, private readonly redis: RedisAdapter) { }
|
|
23
23
|
|
|
24
24
|
recordLatency(method: Method, latencyMs: number) {
|
|
25
25
|
const [key, field] = this.keys.buildLatencyKey(method, findLatencyIndex(latencyMs)).split('::');
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { IUniqueKeysCacheBase } from '../types';
|
|
2
|
-
import { Redis } from 'ioredis';
|
|
3
2
|
import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
|
|
4
3
|
import { setToArray } from '../../utils/lang/sets';
|
|
5
4
|
import { DEFAULT_CACHE_SIZE, REFRESH_RATE, TTL_REFRESH } from './constants';
|
|
6
5
|
import { LOG_PREFIX } from './constants';
|
|
7
6
|
import { ILogger } from '../../logger/types';
|
|
8
7
|
import { UniqueKeysItemSs } from '../../sync/submitters/types';
|
|
8
|
+
import type { RedisAdapter } from './RedisAdapter';
|
|
9
9
|
|
|
10
10
|
export class UniqueKeysCacheInRedis extends UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
|
|
11
11
|
|
|
12
12
|
private readonly log: ILogger;
|
|
13
13
|
private readonly key: string;
|
|
14
|
-
private readonly redis:
|
|
14
|
+
private readonly redis: RedisAdapter;
|
|
15
15
|
private readonly refreshRate: number;
|
|
16
16
|
private intervalId: any;
|
|
17
17
|
|
|
18
|
-
constructor(log: ILogger, key: string, redis:
|
|
18
|
+
constructor(log: ILogger, key: string, redis: RedisAdapter, uniqueKeysQueueSize = DEFAULT_CACHE_SIZE, refreshRate = REFRESH_RATE) {
|
|
19
19
|
super(uniqueKeysQueueSize);
|
|
20
20
|
this.log = log;
|
|
21
21
|
this.key = key;
|
|
@@ -45,7 +45,7 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
return {
|
|
48
|
-
splits: new SplitsCacheInRedis(log, keys, redisClient),
|
|
48
|
+
splits: new SplitsCacheInRedis(log, keys, redisClient, settings.sync.__splitFiltersValidation),
|
|
49
49
|
segments: new SegmentsCacheInRedis(log, keys, redisClient),
|
|
50
50
|
impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
|
|
51
51
|
impressionCounts: impressionCountsCache,
|
|
@@ -2,10 +2,10 @@ import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
|
|
|
2
2
|
import { KeyBuilder } from '../KeyBuilder';
|
|
3
3
|
import { IPluggableStorageWrapper } from '../types';
|
|
4
4
|
import { ILogger } from '../../logger/types';
|
|
5
|
-
import { ISplit } from '../../dtos/types';
|
|
5
|
+
import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
|
|
6
6
|
import { LOG_PREFIX } from './constants';
|
|
7
7
|
import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
|
|
8
|
-
import { ISet } from '../../utils/lang/sets';
|
|
8
|
+
import { ISet, _Set, returnDifference } from '../../utils/lang/sets';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* ISplitsCacheAsync implementation for pluggable storages.
|
|
@@ -15,6 +15,7 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
15
15
|
private readonly log: ILogger;
|
|
16
16
|
private readonly keys: KeyBuilder;
|
|
17
17
|
private readonly wrapper: IPluggableStorageWrapper;
|
|
18
|
+
private readonly flagSetsFilter: string[];
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Create a SplitsCache that uses a storage wrapper.
|
|
@@ -22,11 +23,12 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
22
23
|
* @param keys Key builder.
|
|
23
24
|
* @param wrapper Adapted wrapper storage.
|
|
24
25
|
*/
|
|
25
|
-
constructor(log: ILogger, keys: KeyBuilder, wrapper: IPluggableStorageWrapper) {
|
|
26
|
+
constructor(log: ILogger, keys: KeyBuilder, wrapper: IPluggableStorageWrapper, splitFiltersValidation?: ISplitFiltersValidation) {
|
|
26
27
|
super();
|
|
27
28
|
this.log = log;
|
|
28
29
|
this.keys = keys;
|
|
29
30
|
this.wrapper = wrapper;
|
|
31
|
+
this.flagSetsFilter = splitFiltersValidation ? splitFiltersValidation.groupedFilters.bySet : [];
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
private _decrementCounts(split: ISplit) {
|
|
@@ -41,6 +43,24 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
41
43
|
return this.wrapper.incr(ttKey);
|
|
42
44
|
}
|
|
43
45
|
|
|
46
|
+
private _updateFlagSets(featureFlagName: string, flagSetsOfRemovedFlag?: string[], flagSetsOfAddedFlag?: string[]) {
|
|
47
|
+
const removeFromFlagSets = returnDifference(flagSetsOfRemovedFlag, flagSetsOfAddedFlag);
|
|
48
|
+
|
|
49
|
+
let addToFlagSets = returnDifference(flagSetsOfAddedFlag, flagSetsOfRemovedFlag);
|
|
50
|
+
if (this.flagSetsFilter.length > 0) {
|
|
51
|
+
addToFlagSets = addToFlagSets.filter(flagSet => {
|
|
52
|
+
return this.flagSetsFilter.some(filterFlagSet => filterFlagSet === flagSet);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const items = [featureFlagName];
|
|
57
|
+
|
|
58
|
+
return Promise.all([
|
|
59
|
+
...removeFromFlagSets.map(flagSetName => this.wrapper.removeItems(this.keys.buildFlagSetKey(flagSetName), items)),
|
|
60
|
+
...addToFlagSets.map(flagSetName => this.wrapper.addItems(this.keys.buildFlagSetKey(flagSetName), items))
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
/**
|
|
45
65
|
* Add a given split.
|
|
46
66
|
* The returned promise is resolved when the operation success
|
|
@@ -67,7 +87,7 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
67
87
|
return this._incrementCounts(split).then(() => {
|
|
68
88
|
if (parsedPreviousSplit) return this._decrementCounts(parsedPreviousSplit);
|
|
69
89
|
});
|
|
70
|
-
});
|
|
90
|
+
}).then(() => this._updateFlagSets(name, parsedPreviousSplit && parsedPreviousSplit.sets, split.sets));
|
|
71
91
|
}).then(() => true);
|
|
72
92
|
}
|
|
73
93
|
|
|
@@ -88,8 +108,9 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
88
108
|
removeSplit(name: string) {
|
|
89
109
|
return this.getSplit(name).then((split) => {
|
|
90
110
|
if (split) {
|
|
91
|
-
this._decrementCounts(split);
|
|
111
|
+
return this._decrementCounts(split).then(() => this._updateFlagSets(name, split.sets));
|
|
92
112
|
}
|
|
113
|
+
}).then(() => {
|
|
93
114
|
return this.wrapper.del(this.keys.buildSplitKey(name));
|
|
94
115
|
});
|
|
95
116
|
}
|
|
@@ -156,14 +177,15 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
156
177
|
}
|
|
157
178
|
|
|
158
179
|
/**
|
|
159
|
-
* Get list of
|
|
160
|
-
* The returned promise is resolved with the list of
|
|
161
|
-
*
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
180
|
+
* Get list of feature flag names related to a given list of flag set names.
|
|
181
|
+
* The returned promise is resolved with the list of feature flag names per flag set.
|
|
182
|
+
* It never rejects (If there is a wrapper error for some flag set, an empty set is returned for it).
|
|
183
|
+
*/
|
|
184
|
+
getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>[]> {
|
|
185
|
+
return Promise.all(flagSets.map(flagSet => {
|
|
186
|
+
const flagSetKey = this.keys.buildFlagSetKey(flagSet);
|
|
187
|
+
return this.wrapper.getItems(flagSetKey).catch(() => []);
|
|
188
|
+
})).then(namesByFlagSets => namesByFlagSets.map(namesByFlagSet => new _Set(namesByFlagSet)));
|
|
167
189
|
}
|
|
168
190
|
|
|
169
191
|
/**
|
|
@@ -105,7 +105,7 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
return {
|
|
108
|
-
splits: new SplitsCachePluggable(log, keys, wrapper),
|
|
108
|
+
splits: new SplitsCachePluggable(log, keys, wrapper, settings.sync.__splitFiltersValidation),
|
|
109
109
|
segments: new SegmentsCachePluggable(log, keys, wrapper),
|
|
110
110
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
111
111
|
impressionCounts: impressionCountsCache,
|
package/src/storages/types.ts
CHANGED
|
@@ -44,10 +44,10 @@ export interface IPluggableStorageWrapper {
|
|
|
44
44
|
*
|
|
45
45
|
* @function del
|
|
46
46
|
* @param {string} key Item to delete
|
|
47
|
-
* @returns {Promise<
|
|
47
|
+
* @returns {Promise<boolean>} A promise that resolves if the operation success, whether the key existed and was removed (resolves with true) or it didn't exist (resolves with false).
|
|
48
48
|
* The promise rejects if the operation fails, for example, if there is a connection error.
|
|
49
49
|
*/
|
|
50
|
-
del: (key: string) => Promise<boolean
|
|
50
|
+
del: (key: string) => Promise<boolean>
|
|
51
51
|
/**
|
|
52
52
|
* Returns all keys matching the given prefix.
|
|
53
53
|
*
|
|
@@ -210,7 +210,7 @@ export interface ISplitsCacheBase {
|
|
|
210
210
|
// should never reject or throw an exception. Instead return false by default, to avoid emitting SDK_READY_FROM_CACHE.
|
|
211
211
|
checkCache(): MaybeThenable<boolean>,
|
|
212
212
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): MaybeThenable<boolean>,
|
|
213
|
-
getNamesByFlagSets(flagSets: string[]): MaybeThenable<ISet<string
|
|
213
|
+
getNamesByFlagSets(flagSets: string[]): MaybeThenable<ISet<string>[]>
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
@@ -227,7 +227,7 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
227
227
|
clear(): void,
|
|
228
228
|
checkCache(): boolean,
|
|
229
229
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean,
|
|
230
|
-
getNamesByFlagSets(flagSets: string[]): ISet<string>
|
|
230
|
+
getNamesByFlagSets(flagSets: string[]): ISet<string>[]
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
@@ -244,7 +244,7 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
|
244
244
|
clear(): Promise<boolean | void>,
|
|
245
245
|
checkCache(): Promise<boolean>,
|
|
246
246
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise<boolean>,
|
|
247
|
-
getNamesByFlagSets(flagSets: string[]): Promise<ISet<string
|
|
247
|
+
getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>[]>
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
/** Segments cache */
|
|
@@ -4,6 +4,6 @@ import { ISettings } from '../../types';
|
|
|
4
4
|
/**
|
|
5
5
|
* Storage is async if mode is consumer or partial consumer
|
|
6
6
|
*/
|
|
7
|
-
export function isStorageSync(settings: ISettings) {
|
|
7
|
+
export function isStorageSync(settings: Pick<ISettings, 'mode'>) {
|
|
8
8
|
return [CONSUMER_MODE, CONSUMER_PARTIAL_MODE].indexOf(settings.mode) === -1 ? true : false;
|
|
9
9
|
}
|
package/src/types.ts
CHANGED
|
@@ -197,8 +197,6 @@ interface ISharedSettings {
|
|
|
197
197
|
* List of feature flag filters. These filters are used to fetch a subset of the feature flag definitions in your environment, in order to reduce the delay of the SDK to be ready.
|
|
198
198
|
* This configuration is only meaningful when the SDK is working in "standalone" mode.
|
|
199
199
|
*
|
|
200
|
-
* At the moment, only one type of feature flag filter is supported: by name.
|
|
201
|
-
*
|
|
202
200
|
* Example:
|
|
203
201
|
* `splitFilter: [
|
|
204
202
|
* { type: 'byName', values: ['my_feature_flag_1', 'my_feature_flag_2'] }, // will fetch feature flags named 'my_feature_flag_1' and 'my_feature_flag_2'
|
|
@@ -39,6 +39,22 @@ export const CONSENT_GRANTED = 'GRANTED'; // The user has granted consent for tr
|
|
|
39
39
|
export const CONSENT_DECLINED = 'DECLINED'; // The user has declined consent for tracking events and impressions
|
|
40
40
|
export const CONSENT_UNKNOWN = 'UNKNOWN'; // The user has neither granted nor declined consent for tracking events and impressions
|
|
41
41
|
|
|
42
|
+
// Client method names
|
|
43
|
+
export const GET_TREATMENT = 'getTreatment';
|
|
44
|
+
export const GET_TREATMENTS = 'getTreatments';
|
|
45
|
+
export const GET_TREATMENT_WITH_CONFIG = 'getTreatmentWithConfig';
|
|
46
|
+
export const GET_TREATMENTS_WITH_CONFIG = 'getTreatmentsWithConfig';
|
|
47
|
+
export const GET_TREATMENTS_BY_FLAG_SET = 'getTreatmentsByFlagSet';
|
|
48
|
+
export const GET_TREATMENTS_BY_FLAG_SETS = 'getTreatmentsByFlagSets';
|
|
49
|
+
export const GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET = 'getTreatmentsWithConfigByFlagSet';
|
|
50
|
+
export const GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS = 'getTreatmentsWithConfigByFlagSets';
|
|
51
|
+
export const TRACK_FN_LABEL = 'track';
|
|
52
|
+
|
|
53
|
+
// Manager method names
|
|
54
|
+
export const SPLIT_FN_LABEL = 'split';
|
|
55
|
+
export const SPLITS_FN_LABEL = 'splits';
|
|
56
|
+
export const NAMES_FN_LABEL = 'names';
|
|
57
|
+
|
|
42
58
|
// Telemetry
|
|
43
59
|
export const QUEUED = 0;
|
|
44
60
|
export const DROPPED = 1;
|
|
@@ -8,6 +8,6 @@ export { validateSplit } from './split';
|
|
|
8
8
|
export { validateSplits } from './splits';
|
|
9
9
|
export { validateTrafficType } from './trafficType';
|
|
10
10
|
export { validateIfNotDestroyed, validateIfOperational } from './isOperational';
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
11
|
+
export { validateSplitExistence } from './splitExistence';
|
|
12
|
+
export { validateTrafficTypeExistence } from './trafficTypeExistence';
|
|
13
13
|
export { validatePreloadedData } from './preloadedData';
|
|
@@ -7,7 +7,7 @@ import { WARN_NOT_EXISTENT_SPLIT } from '../../logger/constants';
|
|
|
7
7
|
* This is defined here and in this format mostly because of the logger and the fact that it's considered a validation at product level.
|
|
8
8
|
* But it's not going to run on the input validation layer. In any case, the most compeling reason to use it as we do is to avoid going to Redis and get a split twice.
|
|
9
9
|
*/
|
|
10
|
-
export function
|
|
10
|
+
export function validateSplitExistence(log: ILogger, readinessManager: IReadinessManager, splitName: string, labelOrSplitObj: any, method: string): boolean {
|
|
11
11
|
if (readinessManager.isReady()) { // Only if it's ready we validate this, otherwise it may just be that the SDK is not ready yet.
|
|
12
12
|
if (labelOrSplitObj === SPLIT_NOT_FOUND || labelOrSplitObj == null) {
|
|
13
13
|
log.warn(WARN_NOT_EXISTENT_SPLIT, [method, splitName]);
|
|
@@ -7,14 +7,14 @@ import { MaybeThenable } from '../../dtos/types';
|
|
|
7
7
|
import { ILogger } from '../../logger/types';
|
|
8
8
|
import { WARN_NOT_EXISTENT_TT } from '../../logger/constants';
|
|
9
9
|
|
|
10
|
-
function
|
|
10
|
+
function logTTExistenceWarning(log: ILogger, maybeTT: string, method: string) {
|
|
11
11
|
log.warn(WARN_NOT_EXISTENT_TT, [method, maybeTT]);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Separated from the previous method since on some cases it'll be async.
|
|
16
16
|
*/
|
|
17
|
-
export function
|
|
17
|
+
export function validateTrafficTypeExistence(log: ILogger, readinessManager: IReadinessManager, splitsCache: ISplitsCacheBase, mode: SDKMode, maybeTT: string, method: string): MaybeThenable<boolean> {
|
|
18
18
|
|
|
19
19
|
// If not ready or in localhost mode, we won't run the validation
|
|
20
20
|
if (!readinessManager.isReady() || mode === LOCALHOST_MODE) return true;
|
|
@@ -23,11 +23,11 @@ export function validateTrafficTypeExistance(log: ILogger, readinessManager: IRe
|
|
|
23
23
|
|
|
24
24
|
if (thenable(res)) {
|
|
25
25
|
return res.then(function (isValid) {
|
|
26
|
-
if (!isValid)
|
|
26
|
+
if (!isValid) logTTExistenceWarning(log, maybeTT, method);
|
|
27
27
|
return isValid; // propagate result
|
|
28
28
|
});
|
|
29
29
|
} else {
|
|
30
|
-
if (!res)
|
|
30
|
+
if (!res) logTTExistenceWarning(log, maybeTT, method);
|
|
31
31
|
return res;
|
|
32
32
|
}
|
|
33
33
|
}
|
package/src/utils/lang/sets.ts
CHANGED
|
@@ -114,8 +114,16 @@ export const _Set = __getSetConstructor();
|
|
|
114
114
|
|
|
115
115
|
export function returnSetsUnion<T>(set: ISet<T>, set2: ISet<T>): ISet<T> {
|
|
116
116
|
const result = new _Set(setToArray(set));
|
|
117
|
-
set2.forEach(
|
|
117
|
+
set2.forEach(value => {
|
|
118
118
|
result.add(value);
|
|
119
119
|
});
|
|
120
120
|
return result;
|
|
121
121
|
}
|
|
122
|
+
|
|
123
|
+
export function returnDifference<T>(list: T[] = [], list2: T[] = []): T[] {
|
|
124
|
+
const result = new _Set(list);
|
|
125
|
+
list2.forEach(item => {
|
|
126
|
+
result.delete(item);
|
|
127
|
+
});
|
|
128
|
+
return setToArray(result);
|
|
129
|
+
}
|
|
@@ -202,7 +202,7 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
// validate the `splitFilters` settings and parse splits query
|
|
205
|
-
const splitFiltersValidation = validateSplitFilters(log, withDefaults.sync.splitFilters);
|
|
205
|
+
const splitFiltersValidation = validateSplitFilters(log, withDefaults.sync.splitFilters, withDefaults.mode);
|
|
206
206
|
withDefaults.sync.splitFilters = splitFiltersValidation.validFilters;
|
|
207
207
|
withDefaults.sync.__splitFiltersValidation = splitFiltersValidation;
|
|
208
208
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../constants';
|
|
1
2
|
import { validateSplits } from '../inputValidation/splits';
|
|
2
3
|
import { ISplitFiltersValidation } from '../../dtos/types';
|
|
3
4
|
import { SplitIO } from '../../types';
|
|
4
5
|
import { ILogger } from '../../logger/types';
|
|
5
|
-
import { WARN_SPLITS_FILTER_EMPTY, WARN_SPLITS_FILTER_INVALID, SETTINGS_SPLITS_FILTER, LOG_PREFIX_SETTINGS, ERROR_SETS_FILTER_EXCLUSIVE,
|
|
6
|
+
import { WARN_SPLITS_FILTER_IGNORED, WARN_SPLITS_FILTER_EMPTY, WARN_SPLITS_FILTER_INVALID, SETTINGS_SPLITS_FILTER, LOG_PREFIX_SETTINGS, ERROR_SETS_FILTER_EXCLUSIVE, WARN_LOWERCASE_FLAGSET, WARN_INVALID_FLAGSET, WARN_FLAGSET_NOT_CONFIGURED } from '../../logger/constants';
|
|
6
7
|
import { objectAssign } from '../lang/objectAssign';
|
|
7
8
|
import { find, uniq } from '../lang';
|
|
8
9
|
|
|
@@ -53,7 +54,7 @@ function validateSplitFilter(log: ILogger, type: SplitIO.SplitFilterType, values
|
|
|
53
54
|
if (result) {
|
|
54
55
|
|
|
55
56
|
if (type === 'bySet') {
|
|
56
|
-
result = sanitizeFlagSets(log, result);
|
|
57
|
+
result = sanitizeFlagSets(log, result, LOG_PREFIX_SETTINGS);
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
// check max length
|
|
@@ -87,7 +88,7 @@ function queryStringBuilder(groupedFilters: Record<SplitIO.SplitFilterType, stri
|
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
/**
|
|
90
|
-
* Sanitizes set names list taking
|
|
91
|
+
* Sanitizes set names list taking into account:
|
|
91
92
|
* - It should be lowercase
|
|
92
93
|
* - Must adhere the following regular expression /^[a-z0-9][_a-z0-9]{0,49}$/ that means
|
|
93
94
|
* - must start with a letter or number
|
|
@@ -97,20 +98,21 @@ function queryStringBuilder(groupedFilters: Record<SplitIO.SplitFilterType, stri
|
|
|
97
98
|
*
|
|
98
99
|
* @param {ILogger} log
|
|
99
100
|
* @param {string[]} flagSets
|
|
101
|
+
* @param {string} method
|
|
100
102
|
* @returns sanitized list of set names
|
|
101
103
|
*/
|
|
102
|
-
function sanitizeFlagSets(log: ILogger, flagSets: string[]) {
|
|
104
|
+
function sanitizeFlagSets(log: ILogger, flagSets: string[], method: string) {
|
|
103
105
|
let sanitizedSets = flagSets
|
|
104
106
|
.map(flagSet => {
|
|
105
|
-
if (CAPITAL_LETTERS_REGEX.test(flagSet)){
|
|
106
|
-
log.warn(
|
|
107
|
+
if (CAPITAL_LETTERS_REGEX.test(flagSet)) {
|
|
108
|
+
log.warn(WARN_LOWERCASE_FLAGSET, [method, flagSet]);
|
|
107
109
|
flagSet = flagSet.toLowerCase();
|
|
108
110
|
}
|
|
109
111
|
return flagSet;
|
|
110
112
|
})
|
|
111
113
|
.filter(flagSet => {
|
|
112
|
-
if (!VALID_FLAGSET_REGEX.test(flagSet)){
|
|
113
|
-
log.warn(
|
|
114
|
+
if (!VALID_FLAGSET_REGEX.test(flagSet)) {
|
|
115
|
+
log.warn(WARN_INVALID_FLAGSET, [method, flagSet, VALID_FLAGSET_REGEX, flagSet]);
|
|
114
116
|
return false;
|
|
115
117
|
}
|
|
116
118
|
if (typeof flagSet !== 'string') return false;
|
|
@@ -128,6 +130,7 @@ function configuredFilter(validFilters: SplitIO.SplitFilter[], filterType: Split
|
|
|
128
130
|
*
|
|
129
131
|
* @param {ILogger} log logger
|
|
130
132
|
* @param {any} maybeSplitFilters split filters configuration param provided by the user
|
|
133
|
+
* @param {string} mode settings mode
|
|
131
134
|
* @returns it returns an object with the following properties:
|
|
132
135
|
* - `validFilters`: the validated `splitFilters` configuration object defined by the user.
|
|
133
136
|
* - `queryString`: the parsed split filter query. it is null if all filters are invalid or all values in filters are invalid.
|
|
@@ -135,7 +138,7 @@ function configuredFilter(validFilters: SplitIO.SplitFilter[], filterType: Split
|
|
|
135
138
|
*
|
|
136
139
|
* @throws Error if the some of the grouped list of values per filter exceeds the max allowed length
|
|
137
140
|
*/
|
|
138
|
-
export function validateSplitFilters(log: ILogger, maybeSplitFilters: any): ISplitFiltersValidation {
|
|
141
|
+
export function validateSplitFilters(log: ILogger, maybeSplitFilters: any, mode: string): ISplitFiltersValidation {
|
|
139
142
|
// Validation result schema
|
|
140
143
|
const res = {
|
|
141
144
|
validFilters: [],
|
|
@@ -145,6 +148,11 @@ export function validateSplitFilters(log: ILogger, maybeSplitFilters: any): ISpl
|
|
|
145
148
|
|
|
146
149
|
// do nothing if `splitFilters` param is not a non-empty array or mode is not STANDALONE
|
|
147
150
|
if (!maybeSplitFilters) return res;
|
|
151
|
+
// Warn depending on the mode
|
|
152
|
+
if (mode === CONSUMER_MODE || mode === CONSUMER_PARTIAL_MODE) {
|
|
153
|
+
log.warn(WARN_SPLITS_FILTER_IGNORED);
|
|
154
|
+
return res;
|
|
155
|
+
}
|
|
148
156
|
// Check collection type
|
|
149
157
|
if (!Array.isArray(maybeSplitFilters) || maybeSplitFilters.length === 0) {
|
|
150
158
|
log.warn(WARN_SPLITS_FILTER_EMPTY);
|
|
@@ -181,9 +189,9 @@ export function validateSplitFilters(log: ILogger, maybeSplitFilters: any): ISpl
|
|
|
181
189
|
return res;
|
|
182
190
|
}
|
|
183
191
|
|
|
184
|
-
export function
|
|
192
|
+
export function validateFlagSets(log: ILogger, method: string, flagSets: string[], flagSetsInConfig: string[]): string[] {
|
|
185
193
|
const sets = validateSplits(log, flagSets, method, 'flag sets', 'flag set');
|
|
186
|
-
let toReturn = sets ? sanitizeFlagSets(log, sets) : [];
|
|
194
|
+
let toReturn = sets ? sanitizeFlagSets(log, sets, method) : [];
|
|
187
195
|
if (flagSetsInConfig.length > 0) {
|
|
188
196
|
toReturn = toReturn.filter(flagSet => {
|
|
189
197
|
if (flagSetsInConfig.indexOf(flagSet) > -1) {
|
|
@@ -5,4 +5,4 @@ import { SplitIO } from '../types';
|
|
|
5
5
|
import { ILogger } from '../logger/types';
|
|
6
6
|
export declare function evaluateFeature(log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync): MaybeThenable<IEvaluationResult>;
|
|
7
7
|
export declare function evaluateFeatures(log: ILogger, key: SplitIO.SplitKey, splitNames: string[], attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync): MaybeThenable<Record<string, IEvaluationResult>>;
|
|
8
|
-
export declare function evaluateFeaturesByFlagSets(log: ILogger, key: SplitIO.SplitKey, flagSets: string[], attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync): MaybeThenable<Record<string, IEvaluationResult>>;
|
|
8
|
+
export declare function evaluateFeaturesByFlagSets(log: ILogger, key: SplitIO.SplitKey, flagSets: string[], attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync, method: string): MaybeThenable<Record<string, IEvaluationResult>>;
|
|
@@ -89,14 +89,16 @@ export declare const WARN_NOT_EXISTENT_SPLIT = 215;
|
|
|
89
89
|
export declare const WARN_LOWERCASE_TRAFFIC_TYPE = 216;
|
|
90
90
|
export declare const WARN_NOT_EXISTENT_TT = 217;
|
|
91
91
|
export declare const WARN_INTEGRATION_INVALID = 218;
|
|
92
|
+
export declare const WARN_SPLITS_FILTER_IGNORED = 219;
|
|
92
93
|
export declare const WARN_SPLITS_FILTER_INVALID = 220;
|
|
93
94
|
export declare const WARN_SPLITS_FILTER_EMPTY = 221;
|
|
94
95
|
export declare const WARN_SDK_KEY = 222;
|
|
95
96
|
export declare const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 223;
|
|
96
97
|
export declare const STREAMING_PARSING_SPLIT_UPDATE = 224;
|
|
97
|
-
export declare const
|
|
98
|
-
export declare const
|
|
98
|
+
export declare const WARN_INVALID_FLAGSET = 225;
|
|
99
|
+
export declare const WARN_LOWERCASE_FLAGSET = 226;
|
|
99
100
|
export declare const WARN_FLAGSET_NOT_CONFIGURED = 227;
|
|
101
|
+
export declare const WARN_FLAGSET_WITHOUT_FLAGS = 228;
|
|
100
102
|
export declare const ERROR_ENGINE_COMBINER_IFELSEIF = 300;
|
|
101
103
|
export declare const ERROR_LOGLEVEL_INVALID = 301;
|
|
102
104
|
export declare const ERROR_CLIENT_LISTENER = 302;
|