@splitsoftware/splitio-commons 1.12.1-rc.0 → 1.12.1-rc.2
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 +3 -2
- package/cjs/sdkClient/client.js +4 -4
- package/cjs/sdkManager/index.js +2 -2
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +1 -3
- package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +1 -5
- package/cjs/storages/inRedis/RedisAdapter.js +12 -10
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +38 -17
- package/cjs/storages/pluggable/SplitsCachePluggable.js +5 -5
- 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/redis/RedisMock.js +1 -7
- package/esm/sdkClient/client.js +4 -4
- package/esm/sdkManager/index.js +3 -3
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +1 -3
- package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +1 -5
- package/esm/storages/inRedis/RedisAdapter.js +12 -10
- package/esm/storages/inRedis/SplitsCacheInRedis.js +39 -18
- package/esm/storages/pluggable/SplitsCachePluggable.js +5 -5
- 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/redis/RedisMock.js +1 -7
- package/package.json +2 -2
- package/src/sdkClient/client.ts +4 -4
- package/src/sdkManager/index.ts +3 -3
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +4 -5
- package/src/storages/inRedis/EventsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +4 -8
- package/src/storages/inRedis/ImpressionsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/RedisAdapter.ts +16 -12
- package/src/storages/inRedis/SegmentsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/SplitsCacheInRedis.ts +48 -23
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +2 -2
- package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +3 -3
- package/src/storages/pluggable/SplitsCachePluggable.ts +5 -5
- 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/redis/RedisMock.ts +1 -11
- 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 +3 -2
- package/types/storages/inRedis/SegmentsCacheInRedis.d.ts +2 -2
- package/types/storages/inRedis/SplitsCacheInRedis.d.ts +9 -8
- package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +2 -2
- package/types/storages/inRedis/UniqueKeysCacheInRedis.d.ts +3 -2
- package/types/storages/pluggable/SplitsCachePluggable.d.ts +4 -4
- package/types/utils/inputValidation/index.d.ts +2 -2
- package/types/utils/inputValidation/splitExistence.d.ts +7 -0
- package/types/utils/inputValidation/trafficTypeExistence.d.ts +9 -0
- package/types/utils/redis/RedisMock.d.ts +0 -1
|
@@ -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, returnListDifference } 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 = returnListDifference(flagSetsOfRemovedFlag, flagSetsOfAddedFlag);
|
|
64
|
+
|
|
65
|
+
let addToFlagSets = returnListDifference(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
|
}
|
|
@@ -171,10 +192,10 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
171
192
|
*/
|
|
172
193
|
getAll(): Promise<ISplit[]> {
|
|
173
194
|
return this.redis.keys(this.keys.searchPatternForSplitKeys())
|
|
174
|
-
.then((listOfKeys) => this.redis.
|
|
195
|
+
.then((listOfKeys) => this.redis.pipelineExec(listOfKeys.map(k => ['get', k])))
|
|
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.
|
|
197
217
|
*/
|
|
198
|
-
getNamesByFlagSets(): Promise<ISet<string>[]> {
|
|
199
|
-
this.
|
|
200
|
-
|
|
218
|
+
getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>[]> {
|
|
219
|
+
return this.redis.pipelineExec(flagSets.map(flagSet => ['smembers', this.keys.buildFlagSetKey(flagSet)]))
|
|
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,14 +239,14 @@ 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
|
});
|
|
@@ -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;
|
|
@@ -177,14 +177,14 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
/**
|
|
180
|
-
* Get list of
|
|
181
|
-
* The returned promise is resolved with the list of
|
|
182
|
-
*
|
|
183
|
-
|
|
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
184
|
getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>[]> {
|
|
185
185
|
return Promise.all(flagSets.map(flagSet => {
|
|
186
186
|
const flagSetKey = this.keys.buildFlagSetKey(flagSet);
|
|
187
|
-
return this.wrapper.getItems(flagSetKey);
|
|
187
|
+
return this.wrapper.getItems(flagSetKey).catch(() => []);
|
|
188
188
|
})).then(namesByFlagSets => namesByFlagSets.map(namesByFlagSet => new _Set(namesByFlagSet)));
|
|
189
189
|
}
|
|
190
190
|
|
|
@@ -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
|
}
|
|
@@ -8,13 +8,10 @@ function asyncFunction(data: any): Promise<any> {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const IDENTITY_METHODS: string[] = [];
|
|
11
|
-
const ASYNC_METHODS = ['rpush', 'hincrby'];
|
|
12
|
-
const PIPELINE_METHODS = ['rpush', 'hincrby'];
|
|
11
|
+
const ASYNC_METHODS = ['rpush', 'hincrby', 'pipelineExec'];
|
|
13
12
|
|
|
14
13
|
export class RedisMock {
|
|
15
14
|
|
|
16
|
-
private pipelineMethods: any = { exec: jest.fn(asyncFunction) };
|
|
17
|
-
|
|
18
15
|
constructor() {
|
|
19
16
|
IDENTITY_METHODS.forEach(method => {
|
|
20
17
|
this[method] = jest.fn(identityFunction);
|
|
@@ -22,12 +19,5 @@ export class RedisMock {
|
|
|
22
19
|
ASYNC_METHODS.forEach(method => {
|
|
23
20
|
this[method] = jest.fn(asyncFunction);
|
|
24
21
|
});
|
|
25
|
-
PIPELINE_METHODS.forEach(method => {
|
|
26
|
-
this.pipelineMethods[method] = this[method];
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
this.pipeline = jest.fn(() => {return this.pipelineMethods;});
|
|
30
22
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
23
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { IEventsCacheAsync } from '../types';
|
|
2
2
|
import { IMetadata } from '../../dtos/types';
|
|
3
|
-
import { Redis } from 'ioredis';
|
|
4
3
|
import { SplitIO } from '../../types';
|
|
5
4
|
import { ILogger } from '../../logger/types';
|
|
6
5
|
import { StoredEventWithMetadata } from '../../sync/submitters/types';
|
|
6
|
+
import type { RedisAdapter } from './RedisAdapter';
|
|
7
7
|
export declare class EventsCacheInRedis implements IEventsCacheAsync {
|
|
8
8
|
private readonly log;
|
|
9
9
|
private readonly key;
|
|
10
10
|
private readonly redis;
|
|
11
11
|
private readonly metadata;
|
|
12
|
-
constructor(log: ILogger, key: string, redis:
|
|
12
|
+
constructor(log: ILogger, key: string, redis: RedisAdapter, metadata: IMetadata);
|
|
13
13
|
/**
|
|
14
14
|
* Add a new event object into the queue.
|
|
15
15
|
* Unlike `impressions::track`, result promise is never rejected.
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
/// <reference types="ioredis" />
|
|
2
2
|
import { ILogger } from '../../logger/types';
|
|
3
3
|
import { ImpressionCountsPayload } from '../../sync/submitters/types';
|
|
4
4
|
import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
|
|
5
|
+
import type { RedisAdapter } from './RedisAdapter';
|
|
5
6
|
export declare class ImpressionCountsCacheInRedis extends ImpressionCountsCacheInMemory {
|
|
6
7
|
private readonly log;
|
|
7
8
|
private readonly key;
|
|
8
9
|
private readonly redis;
|
|
9
10
|
private readonly refreshRate;
|
|
10
11
|
private intervalId;
|
|
11
|
-
constructor(log: ILogger, key: string, redis:
|
|
12
|
+
constructor(log: ILogger, key: string, redis: RedisAdapter, impressionCountsCacheSize?: number, refreshRate?: number);
|
|
12
13
|
private postImpressionCountsInRedis;
|
|
13
14
|
start(): void;
|
|
14
15
|
stop(): Promise<boolean | import("ioredis").BooleanResponse | undefined>;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { IImpressionsCacheAsync } from '../types';
|
|
2
2
|
import { IMetadata } from '../../dtos/types';
|
|
3
3
|
import { ImpressionDTO } from '../../types';
|
|
4
|
-
import { Redis } from 'ioredis';
|
|
5
4
|
import { StoredImpressionWithMetadata } from '../../sync/submitters/types';
|
|
6
5
|
import { ILogger } from '../../logger/types';
|
|
6
|
+
import type { RedisAdapter } from './RedisAdapter';
|
|
7
7
|
export declare class ImpressionsCacheInRedis implements IImpressionsCacheAsync {
|
|
8
8
|
private readonly log;
|
|
9
9
|
private readonly key;
|
|
10
10
|
private readonly redis;
|
|
11
11
|
private readonly metadata;
|
|
12
|
-
constructor(log: ILogger, key: string, redis:
|
|
12
|
+
constructor(log: ILogger, key: string, redis: RedisAdapter, metadata: IMetadata);
|
|
13
13
|
track(impressions: ImpressionDTO[]): Promise<void>;
|
|
14
14
|
count(): Promise<number>;
|
|
15
15
|
drop(count?: number): Promise<any>;
|
|
@@ -8,7 +8,8 @@ export declare class RedisAdapter extends ioredis {
|
|
|
8
8
|
private _options;
|
|
9
9
|
private _notReadyCommandsQueue?;
|
|
10
10
|
private _runningCommands;
|
|
11
|
-
constructor(log: ILogger, storageSettings
|
|
11
|
+
constructor(log: ILogger, storageSettings?: Record<string, any>);
|
|
12
|
+
pipelineExec(commands?: (string | number)[][]): Promise<Array<[Error | null, any]>>;
|
|
12
13
|
_listenToEvents(): void;
|
|
13
14
|
_setTimeoutWrappers(): void;
|
|
14
15
|
_setDisconnectWrapper(): void;
|
|
@@ -20,5 +21,5 @@ export declare class RedisAdapter extends ioredis {
|
|
|
20
21
|
/**
|
|
21
22
|
* Parses the options into what we care about.
|
|
22
23
|
*/
|
|
23
|
-
static _defineOptions({ connectionTimeout, operationTimeout, url, host, port, db, pass, tls }
|
|
24
|
+
static _defineOptions({ connectionTimeout, operationTimeout, url, host, port, db, pass, tls }?: Record<string, any>): object;
|
|
24
25
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Redis } from 'ioredis';
|
|
2
1
|
import { ILogger } from '../../logger/types';
|
|
3
2
|
import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
4
3
|
import { ISegmentsCacheAsync } from '../types';
|
|
4
|
+
import type { RedisAdapter } from './RedisAdapter';
|
|
5
5
|
export declare class SegmentsCacheInRedis implements ISegmentsCacheAsync {
|
|
6
6
|
private readonly log;
|
|
7
7
|
private readonly redis;
|
|
8
8
|
private readonly keys;
|
|
9
|
-
constructor(log: ILogger, keys: KeyBuilderSS, redis:
|
|
9
|
+
constructor(log: ILogger, keys: KeyBuilderSS, redis: RedisAdapter);
|
|
10
10
|
addToSegment(name: string, segmentKeys: string[]): Promise<boolean>;
|
|
11
11
|
removeFromSegment(name: string, segmentKeys: string[]): Promise<boolean>;
|
|
12
12
|
isInSegment(name: string, key: string): Promise<boolean>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
2
|
-
import { Redis } from 'ioredis';
|
|
3
2
|
import { ILogger } from '../../logger/types';
|
|
4
|
-
import { ISplit } from '../../dtos/types';
|
|
3
|
+
import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
|
|
5
4
|
import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
|
|
6
5
|
import { ISet } from '../../utils/lang/sets';
|
|
6
|
+
import type { RedisAdapter } from './RedisAdapter';
|
|
7
7
|
/**
|
|
8
8
|
* ISplitsCacheAsync implementation that stores split definitions in Redis.
|
|
9
9
|
* Supported by Node.
|
|
@@ -13,9 +13,11 @@ export declare class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
13
13
|
private readonly redis;
|
|
14
14
|
private readonly keys;
|
|
15
15
|
private redisError?;
|
|
16
|
-
|
|
16
|
+
private readonly flagSetsFilter;
|
|
17
|
+
constructor(log: ILogger, keys: KeyBuilderSS, redis: RedisAdapter, splitFiltersValidation?: ISplitFiltersValidation);
|
|
17
18
|
private _decrementCounts;
|
|
18
19
|
private _incrementCounts;
|
|
20
|
+
private _updateFlagSets;
|
|
19
21
|
/**
|
|
20
22
|
* Add a given split.
|
|
21
23
|
* The returned promise is resolved when the operation success
|
|
@@ -73,12 +75,11 @@ export declare class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
73
75
|
*/
|
|
74
76
|
getSplitNames(): Promise<string[]>;
|
|
75
77
|
/**
|
|
76
|
-
* Get list of
|
|
77
|
-
* The returned promise is resolved with the list of
|
|
78
|
-
* or rejected if
|
|
79
|
-
* @todo this is a no-op method to be implemented
|
|
78
|
+
* Get list of feature flag names related to a given list of flag set names.
|
|
79
|
+
* The returned promise is resolved with the list of feature flag names per flag set,
|
|
80
|
+
* or rejected if the pipelined redis operation fails.
|
|
80
81
|
*/
|
|
81
|
-
getNamesByFlagSets(): Promise<ISet<string>[]>;
|
|
82
|
+
getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>[]>;
|
|
82
83
|
/**
|
|
83
84
|
* Check traffic type existence.
|
|
84
85
|
* The returned promise is resolved with a boolean indicating whether the TT exist or not.
|
|
@@ -2,7 +2,7 @@ import { ILogger } from '../../logger/types';
|
|
|
2
2
|
import { Method, MultiConfigs, MultiMethodExceptions, MultiMethodLatencies } from '../../sync/submitters/types';
|
|
3
3
|
import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
4
4
|
import { ITelemetryCacheAsync } from '../types';
|
|
5
|
-
import {
|
|
5
|
+
import type { RedisAdapter } from './RedisAdapter';
|
|
6
6
|
export declare class TelemetryCacheInRedis implements ITelemetryCacheAsync {
|
|
7
7
|
private readonly log;
|
|
8
8
|
private readonly keys;
|
|
@@ -13,7 +13,7 @@ export declare class TelemetryCacheInRedis implements ITelemetryCacheAsync {
|
|
|
13
13
|
* @param keys Key builder.
|
|
14
14
|
* @param redis Redis client.
|
|
15
15
|
*/
|
|
16
|
-
constructor(log: ILogger, keys: KeyBuilderSS, redis:
|
|
16
|
+
constructor(log: ILogger, keys: KeyBuilderSS, redis: RedisAdapter);
|
|
17
17
|
recordLatency(method: Method, latencyMs: number): Promise<number | void>;
|
|
18
18
|
recordException(method: Method): Promise<number | void>;
|
|
19
19
|
recordConfig(): Promise<number | void>;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
/// <reference types="ioredis" />
|
|
1
2
|
import { IUniqueKeysCacheBase } from '../types';
|
|
2
|
-
import { Redis } from 'ioredis';
|
|
3
3
|
import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory';
|
|
4
4
|
import { ILogger } from '../../logger/types';
|
|
5
5
|
import { UniqueKeysItemSs } from '../../sync/submitters/types';
|
|
6
|
+
import type { RedisAdapter } from './RedisAdapter';
|
|
6
7
|
export declare class UniqueKeysCacheInRedis extends UniqueKeysCacheInMemory implements IUniqueKeysCacheBase {
|
|
7
8
|
private readonly log;
|
|
8
9
|
private readonly key;
|
|
9
10
|
private readonly redis;
|
|
10
11
|
private readonly refreshRate;
|
|
11
12
|
private intervalId;
|
|
12
|
-
constructor(log: ILogger, key: string, redis:
|
|
13
|
+
constructor(log: ILogger, key: string, redis: RedisAdapter, uniqueKeysQueueSize?: number, refreshRate?: number);
|
|
13
14
|
private postUniqueKeysInRedis;
|
|
14
15
|
start(): void;
|
|
15
16
|
stop(): Promise<boolean | import("ioredis").BooleanResponse | undefined>;
|
|
@@ -71,10 +71,10 @@ export declare class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
71
71
|
*/
|
|
72
72
|
getSplitNames(): Promise<string[]>;
|
|
73
73
|
/**
|
|
74
|
-
* Get list of
|
|
75
|
-
* The returned promise is resolved with the list of
|
|
76
|
-
*
|
|
77
|
-
|
|
74
|
+
* Get list of feature flag names related to a given list of flag set names.
|
|
75
|
+
* The returned promise is resolved with the list of feature flag names per flag set.
|
|
76
|
+
* It never rejects (If there is a wrapper error for some flag set, an empty set is returned for it).
|
|
77
|
+
*/
|
|
78
78
|
getNamesByFlagSets(flagSets: string[]): Promise<ISet<string>[]>;
|
|
79
79
|
/**
|
|
80
80
|
* Check traffic type existence.
|
|
@@ -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';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IReadinessManager } from '../../readiness/types';
|
|
2
|
+
import { ILogger } from '../../logger/types';
|
|
3
|
+
/**
|
|
4
|
+
* 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.
|
|
5
|
+
* 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.
|
|
6
|
+
*/
|
|
7
|
+
export declare function validateSplitExistence(log: ILogger, readinessManager: IReadinessManager, splitName: string, labelOrSplitObj: any, method: string): boolean;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ISplitsCacheBase } from '../../storages/types';
|
|
2
|
+
import { IReadinessManager } from '../../readiness/types';
|
|
3
|
+
import { SDKMode } from '../../types';
|
|
4
|
+
import { MaybeThenable } from '../../dtos/types';
|
|
5
|
+
import { ILogger } from '../../logger/types';
|
|
6
|
+
/**
|
|
7
|
+
* Separated from the previous method since on some cases it'll be async.
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateTrafficTypeExistence(log: ILogger, readinessManager: IReadinessManager, splitsCache: ISplitsCacheBase, mode: SDKMode, maybeTT: string, method: string): MaybeThenable<boolean>;
|