@splitsoftware/splitio-commons 3.0.0-rc.0 → 3.0.0-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 +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 +15 -6
- 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/pollingManagerSS.js +3 -3
- package/cjs/sync/polling/syncTasks/definitionsSyncTask.js +1 -1
- package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +2 -3
- 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 +10 -8
- package/cjs/utils/Backoff.js +20 -0
- package/cjs/utils/lang/sets.js +4 -4
- package/cjs/utils/settingsValidation/index.js +2 -0
- package/cjs/utils/settingsValidation/url.js +6 -2
- 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 +15 -6
- 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/pollingManagerSS.js +3 -3
- package/esm/sync/polling/syncTasks/definitionsSyncTask.js +1 -1
- package/esm/sync/polling/syncTasks/segmentsSyncTask.js +2 -3
- 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 +9 -7
- package/esm/utils/Backoff.js +20 -0
- package/esm/utils/lang/sets.js +4 -4
- package/esm/utils/settingsValidation/index.js +2 -0
- package/esm/utils/settingsValidation/url.js +6 -2
- 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 +18 -7
- package/src/services/types.ts +6 -1
- 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/pollingManagerSS.ts +5 -4
- package/src/sync/polling/syncTasks/definitionsSyncTask.ts +1 -1
- package/src/sync/polling/syncTasks/segmentsSyncTask.ts +3 -4
- 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 +10 -8
- 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/index.ts +2 -0
- package/src/utils/settingsValidation/url.ts +6 -2
- package/types/splitio.d.ts +27 -8
- 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DefinitionsCacheInMemory } from './DefinitionsCacheInMemory';
|
|
2
2
|
import { SegmentsCacheInMemory } from './SegmentsCacheInMemory';
|
|
3
3
|
import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
|
|
4
4
|
import { EventsCacheInMemory } from './EventsCacheInMemory';
|
|
@@ -17,18 +17,18 @@ import { RBSegmentsCacheInMemory } from './RBSegmentsCacheInMemory';
|
|
|
17
17
|
export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageSync {
|
|
18
18
|
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { __splitFiltersValidation } } } = params;
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const definitions = new DefinitionsCacheInMemory(__splitFiltersValidation);
|
|
21
21
|
const rbSegments = new RBSegmentsCacheInMemory();
|
|
22
22
|
const segments = new SegmentsCacheInMemory();
|
|
23
23
|
|
|
24
24
|
const storage = {
|
|
25
|
-
|
|
25
|
+
definitions,
|
|
26
26
|
rbSegments,
|
|
27
27
|
segments,
|
|
28
28
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
29
29
|
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
30
30
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
31
|
-
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(
|
|
31
|
+
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(definitions, segments) : undefined,
|
|
32
32
|
uniqueKeys: new UniqueKeysCacheInMemory(),
|
|
33
33
|
|
|
34
34
|
destroy() { }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DefinitionsCacheInMemory } from './DefinitionsCacheInMemory';
|
|
2
2
|
import { MySegmentsCacheInMemory } from './MySegmentsCacheInMemory';
|
|
3
3
|
import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
|
|
4
4
|
import { EventsCacheInMemory } from './EventsCacheInMemory';
|
|
@@ -17,20 +17,20 @@ import { RBSegmentsCacheInMemory } from './RBSegmentsCacheInMemory';
|
|
|
17
17
|
export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSync {
|
|
18
18
|
const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize }, sync: { __splitFiltersValidation } } } = params;
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const definitions = new DefinitionsCacheInMemory(__splitFiltersValidation);
|
|
21
21
|
const rbSegments = new RBSegmentsCacheInMemory();
|
|
22
22
|
const segments = new MySegmentsCacheInMemory();
|
|
23
23
|
const largeSegments = new MySegmentsCacheInMemory();
|
|
24
24
|
|
|
25
25
|
const storage = {
|
|
26
|
-
|
|
26
|
+
definitions,
|
|
27
27
|
rbSegments,
|
|
28
28
|
segments,
|
|
29
29
|
largeSegments,
|
|
30
30
|
impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
|
|
31
31
|
impressionCounts: new ImpressionCountsCacheInMemory(),
|
|
32
32
|
events: new EventsCacheInMemory(eventsQueueSize),
|
|
33
|
-
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(
|
|
33
|
+
telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(definitions, segments) : undefined,
|
|
34
34
|
uniqueKeys: new UniqueKeysCacheInMemoryCS(),
|
|
35
35
|
|
|
36
36
|
destroy() { },
|
|
@@ -38,7 +38,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
|
|
|
38
38
|
// When using shared instantiation with MEMORY we reuse everything but segments (they are unique per key)
|
|
39
39
|
shared() {
|
|
40
40
|
return {
|
|
41
|
-
|
|
41
|
+
definitions: this.definitions,
|
|
42
42
|
rbSegments: this.rbSegments,
|
|
43
43
|
segments: new MySegmentsCacheInMemory(),
|
|
44
44
|
largeSegments: new MySegmentsCacheInMemory(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IRBSegment } from '../../dtos/types';
|
|
2
2
|
import { setToArray } from '../../utils/lang/sets';
|
|
3
|
-
import { usesSegments } from '../
|
|
3
|
+
import { usesSegments } from '../AbstractDefinitionsCacheSync';
|
|
4
4
|
import { IRBSegmentsCacheSync } from '../types';
|
|
5
5
|
|
|
6
6
|
export class RBSegmentsCacheInMemory implements IRBSegmentsCacheSync {
|
|
@@ -2,7 +2,7 @@ import { isIntegerNumber } from '../../utils/lang';
|
|
|
2
2
|
import { ISegmentsCacheSync } from '../types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Default
|
|
5
|
+
* Default IDefinitionsCacheSync implementation for server-side that stores segments definitions in memory.
|
|
6
6
|
*/
|
|
7
7
|
export class SegmentsCacheInMemory implements ISegmentsCacheSync {
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@ import { ImpressionDataType, EventDataType, LastSync, HttpErrors, HttpLatencies,
|
|
|
2
2
|
import { DEDUPED, DROPPED, LOCALHOST_MODE, QUEUED } from '../../utils/constants';
|
|
3
3
|
import { checkIfServerSide } from '../../utils/key';
|
|
4
4
|
import { findLatencyIndex } from '../findLatencyIndex';
|
|
5
|
-
import { ISegmentsCacheSync,
|
|
5
|
+
import { ISegmentsCacheSync, IDefinitionsCacheSync, IStorageFactoryParams, ITelemetryCacheSync } from '../types';
|
|
6
6
|
|
|
7
7
|
const MAX_STREAMING_EVENTS = 20;
|
|
8
8
|
const MAX_TAGS = 10;
|
|
@@ -28,7 +28,7 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
|
|
|
28
28
|
|
|
29
29
|
public name = 'telemetry stats';
|
|
30
30
|
|
|
31
|
-
constructor(private
|
|
31
|
+
constructor(private definitions?: IDefinitionsCacheSync, private segments?: ISegmentsCacheSync, private largeSegments?: ISegmentsCacheSync) { }
|
|
32
32
|
|
|
33
33
|
// isEmpty flag
|
|
34
34
|
private e = true;
|
|
@@ -51,7 +51,7 @@ export class TelemetryCacheInMemory implements ITelemetryCacheSync {
|
|
|
51
51
|
iQ: this.getImpressionStats(QUEUED),
|
|
52
52
|
iDe: this.getImpressionStats(DEDUPED),
|
|
53
53
|
iDr: this.getImpressionStats(DROPPED),
|
|
54
|
-
spC: this.
|
|
54
|
+
spC: this.definitions && this.definitions.getNames().length,
|
|
55
55
|
seC: this.segments && this.segments.getRegisteredSegments().length,
|
|
56
56
|
skC: this.segments && this.segments.getKeysCount(),
|
|
57
57
|
lsC: this.largeSegments && this.largeSegments.getRegisteredSegments().length,
|
|
@@ -3,7 +3,7 @@ import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
|
3
3
|
import { ILogger } from '../../logger/types';
|
|
4
4
|
import { LOG_PREFIX } from './constants';
|
|
5
5
|
import { IDefinition, ISplitFiltersValidation } from '../../dtos/types';
|
|
6
|
-
import {
|
|
6
|
+
import { AbstractDefinitionsCacheAsync } from '../AbstractDefinitionsCacheAsync';
|
|
7
7
|
import { returnDifference } from '../../utils/lang/sets';
|
|
8
8
|
import type { RedisAdapter } from './RedisAdapter';
|
|
9
9
|
|
|
@@ -18,23 +18,23 @@ function processPipelineAnswer(results: Array<[Error | null, unknown]> | null):
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* IDefinitionsCacheAsync implementation that stores definitions in Redis.
|
|
22
22
|
* Supported by Node.js
|
|
23
23
|
*/
|
|
24
|
-
export class
|
|
24
|
+
export class DefinitionsCacheInRedis extends AbstractDefinitionsCacheAsync {
|
|
25
25
|
|
|
26
26
|
private readonly log: ILogger;
|
|
27
27
|
private readonly redis: RedisAdapter;
|
|
28
28
|
private readonly keys: KeyBuilderSS;
|
|
29
29
|
private redisError?: Error;
|
|
30
|
-
private readonly
|
|
30
|
+
private readonly setsFilter: string[];
|
|
31
31
|
|
|
32
32
|
constructor(log: ILogger, keys: KeyBuilderSS, redis: RedisAdapter, splitFiltersValidation?: ISplitFiltersValidation) {
|
|
33
33
|
super();
|
|
34
34
|
this.log = log;
|
|
35
35
|
this.redis = redis;
|
|
36
36
|
this.keys = keys;
|
|
37
|
-
this.
|
|
37
|
+
this.setsFilter = splitFiltersValidation ? splitFiltersValidation.groupedFilters.bySet : [];
|
|
38
38
|
|
|
39
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.
|
|
40
40
|
// But it is done just to avoid getting the ioredis message `Unhandled error event`.
|
|
@@ -47,95 +47,95 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
private _decrementCounts(
|
|
51
|
-
const ttKey = this.keys.buildTrafficTypeKey(
|
|
50
|
+
private _decrementCounts(definition: IDefinition) {
|
|
51
|
+
const ttKey = this.keys.buildTrafficTypeKey(definition.trafficTypeName);
|
|
52
52
|
return this.redis.decr(ttKey).then((count: number) => {
|
|
53
53
|
if (count === 0) return this.redis.del(ttKey);
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
private _incrementCounts(
|
|
58
|
-
const ttKey = this.keys.buildTrafficTypeKey(
|
|
57
|
+
private _incrementCounts(definition: IDefinition) {
|
|
58
|
+
const ttKey = this.keys.buildTrafficTypeKey(definition.trafficTypeName);
|
|
59
59
|
return this.redis.incr(ttKey);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
private
|
|
63
|
-
const
|
|
62
|
+
private _updateSets(definitionName: string, setsOfRemovedDefinition?: string[] | null, setsOfAddedDefinition?: string[] | null) {
|
|
63
|
+
const removeFromSets = returnDifference(setsOfRemovedDefinition, setsOfAddedDefinition);
|
|
64
64
|
|
|
65
|
-
let
|
|
66
|
-
if (this.
|
|
67
|
-
|
|
68
|
-
return this.
|
|
65
|
+
let addToSets = returnDifference(setsOfAddedDefinition, setsOfRemovedDefinition);
|
|
66
|
+
if (this.setsFilter.length > 0) {
|
|
67
|
+
addToSets = addToSets.filter(set => {
|
|
68
|
+
return this.setsFilter.some(filterSet => filterSet === set);
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
const items = [
|
|
72
|
+
const items = [definitionName];
|
|
73
73
|
|
|
74
74
|
return Promise.all([
|
|
75
|
-
...
|
|
76
|
-
...
|
|
75
|
+
...removeFromSets.map(setName => this.redis.srem(this.keys.buildSetKey(setName), items)),
|
|
76
|
+
...addToSets.map(setName => this.redis.sadd(this.keys.buildSetKey(setName), items))
|
|
77
77
|
]);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* Add a given
|
|
81
|
+
* Add a given definition.
|
|
82
82
|
* The returned promise is resolved when the operation success
|
|
83
83
|
* or rejected if it fails (e.g., redis operation fails)
|
|
84
84
|
*/
|
|
85
|
-
|
|
86
|
-
const name =
|
|
87
|
-
const
|
|
88
|
-
return this.redis.get(
|
|
85
|
+
add(definition: IDefinition): Promise<boolean> {
|
|
86
|
+
const name = definition.name;
|
|
87
|
+
const definitionKey = this.keys.buildDefinitionKey(name);
|
|
88
|
+
return this.redis.get(definitionKey).then((definitionFromStorage: string | null) => {
|
|
89
89
|
|
|
90
90
|
// handling parsing error
|
|
91
|
-
let
|
|
91
|
+
let parsedPreviousDefinition: IDefinition, stringifiedNewDefinition;
|
|
92
92
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
parsedPreviousDefinition = definitionFromStorage ? JSON.parse(definitionFromStorage) : undefined;
|
|
94
|
+
stringifiedNewDefinition = JSON.stringify(definition);
|
|
95
95
|
} catch (e) {
|
|
96
96
|
throw new Error('Error parsing feature flag definition: ' + e);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
return this.redis.set(
|
|
99
|
+
return this.redis.set(definitionKey, stringifiedNewDefinition).then(() => {
|
|
100
100
|
// avoid unnecessary increment/decrement operations
|
|
101
|
-
if (
|
|
101
|
+
if (parsedPreviousDefinition && parsedPreviousDefinition.trafficTypeName === definition.trafficTypeName) return;
|
|
102
102
|
|
|
103
103
|
// update traffic type counts
|
|
104
|
-
return this._incrementCounts(
|
|
105
|
-
if (
|
|
104
|
+
return this._incrementCounts(definition).then(() => {
|
|
105
|
+
if (parsedPreviousDefinition) return this._decrementCounts(parsedPreviousDefinition);
|
|
106
106
|
});
|
|
107
|
-
}).then(() => this.
|
|
107
|
+
}).then(() => this._updateSets(name, parsedPreviousDefinition && parsedPreviousDefinition.sets, definition.sets));
|
|
108
108
|
}).then(() => true);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
* Remove a given
|
|
113
|
-
* The returned promise is resolved when the operation success, with true or false indicating if the
|
|
112
|
+
* Remove a given definition.
|
|
113
|
+
* The returned promise is resolved when the operation success, with true or false indicating if the definition existed (and was removed) or not.
|
|
114
114
|
* or rejected if it fails (e.g., redis operation fails).
|
|
115
115
|
*/
|
|
116
|
-
|
|
117
|
-
return this.
|
|
118
|
-
if (
|
|
119
|
-
return this._decrementCounts(
|
|
116
|
+
remove(name: string) {
|
|
117
|
+
return this.get(name).then((definition) => {
|
|
118
|
+
if (definition) {
|
|
119
|
+
return this._decrementCounts(definition).then(() => this._updateSets(name, definition.sets));
|
|
120
120
|
}
|
|
121
121
|
}).then(() => {
|
|
122
|
-
return this.redis.del(this.keys.
|
|
122
|
+
return this.redis.del(this.keys.buildDefinitionKey(name)).then((status: number) => status === 1);
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/**
|
|
127
|
-
* Get
|
|
127
|
+
* Get definition or null if it's not defined.
|
|
128
128
|
* Returned promise is rejected if redis operation fails.
|
|
129
129
|
*/
|
|
130
|
-
|
|
130
|
+
get(name: string): Promise<IDefinition | null> {
|
|
131
131
|
if (this.redisError) {
|
|
132
132
|
this.log.error(LOG_PREFIX + this.redisError);
|
|
133
133
|
|
|
134
134
|
return Promise.reject(this.redisError);
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
return this.redis.get(this.keys.
|
|
138
|
-
.then((
|
|
137
|
+
return this.redis.get(this.keys.buildDefinitionKey(name))
|
|
138
|
+
.then((maybeDefinition: string | null) => maybeDefinition && JSON.parse(maybeDefinition));
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
/**
|
|
@@ -144,7 +144,7 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
144
144
|
* or rejected if it fails.
|
|
145
145
|
*/
|
|
146
146
|
setChangeNumber(changeNumber: number): Promise<boolean> {
|
|
147
|
-
return this.redis.set(this.keys.
|
|
147
|
+
return this.redis.set(this.keys.buildDefinitionsTillKey(), changeNumber + '').then(
|
|
148
148
|
(status: string | null) => status === 'OK'
|
|
149
149
|
);
|
|
150
150
|
}
|
|
@@ -155,7 +155,7 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
155
155
|
* The promise will never be rejected.
|
|
156
156
|
*/
|
|
157
157
|
getChangeNumber(): Promise<number> {
|
|
158
|
-
return this.redis.get(this.keys.
|
|
158
|
+
return this.redis.get(this.keys.buildDefinitionsTillKey()).then((value: string | null) => {
|
|
159
159
|
const i = parseInt(value as string, 10);
|
|
160
160
|
|
|
161
161
|
return isNaNNumber(i) ? -1 : i;
|
|
@@ -166,44 +166,44 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
|
-
* Get list of all
|
|
170
|
-
* The returned promise is resolved with the list of
|
|
169
|
+
* Get list of all definitions.
|
|
170
|
+
* The returned promise is resolved with the list of definitions,
|
|
171
171
|
* or rejected if redis operation fails.
|
|
172
172
|
*/
|
|
173
173
|
// @TODO we need to benchmark which is the maximun number of commands we could pipeline without kill redis performance.
|
|
174
174
|
getAll(): Promise<IDefinition[]> {
|
|
175
|
-
return this.redis.keys(this.keys.
|
|
175
|
+
return this.redis.keys(this.keys.searchPatternForDefinitionKeys())
|
|
176
176
|
.then((listOfKeys: string[]) => this.redis.pipeline(listOfKeys.map((k: string) => ['get', k])).exec())
|
|
177
177
|
.then(processPipelineAnswer)
|
|
178
|
-
.then((
|
|
179
|
-
return JSON.parse(
|
|
178
|
+
.then((definitions: string[]) => definitions.map((definition: string) => {
|
|
179
|
+
return JSON.parse(definition);
|
|
180
180
|
}));
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
/**
|
|
184
|
-
* Get list of
|
|
185
|
-
* The returned promise is resolved with the list of
|
|
184
|
+
* Get list of definition names.
|
|
185
|
+
* The returned promise is resolved with the list of names,
|
|
186
186
|
* or rejected if redis operation fails.
|
|
187
187
|
*/
|
|
188
|
-
|
|
189
|
-
return this.redis.keys(this.keys.
|
|
188
|
+
getNames(): Promise<string[]> {
|
|
189
|
+
return this.redis.keys(this.keys.searchPatternForDefinitionKeys()).then(
|
|
190
190
|
(listOfKeys: string[]) => listOfKeys.map(this.keys.extractKey)
|
|
191
191
|
);
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
/**
|
|
195
|
-
* Get list of
|
|
196
|
-
* The returned promise is resolved with the list of
|
|
195
|
+
* Get list of definition names related to a given list of set names.
|
|
196
|
+
* The returned promise is resolved with the list of names per set,
|
|
197
197
|
* or rejected if the pipelined redis operation fails (e.g., timeout).
|
|
198
198
|
*/
|
|
199
|
-
|
|
200
|
-
return this.redis.pipeline(
|
|
199
|
+
getNamesBySets(sets: string[]): Promise<Set<string>[]> {
|
|
200
|
+
return this.redis.pipeline(sets.map(set => ['smembers', this.keys.buildSetKey(set)])).exec()
|
|
201
201
|
.then((results: [Error | null, unknown][] | null) => results ? results.map(([e, value]: [Error | null, unknown], index: number) => {
|
|
202
202
|
if (e === null) return value as string;
|
|
203
203
|
|
|
204
|
-
this.log.error(LOG_PREFIX + `Could not read result from get members of
|
|
204
|
+
this.log.error(LOG_PREFIX + `Could not read result from get members of set ${sets[index]} due to an error: ${e}`);
|
|
205
205
|
}) : [])
|
|
206
|
-
.then((
|
|
206
|
+
.then((namesBySets: (string | undefined)[]) => namesBySets.map((namesBySet: string | undefined) => new Set(namesBySet)));
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
/**
|
|
@@ -239,25 +239,25 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
/**
|
|
242
|
-
* Fetches multiple
|
|
242
|
+
* Fetches multiple definitions.
|
|
243
243
|
* Returned promise is rejected if redis operation fails.
|
|
244
244
|
*/
|
|
245
|
-
|
|
245
|
+
getMany(names: string[]): Promise<Record<string, IDefinition | null>> {
|
|
246
246
|
if (this.redisError) {
|
|
247
247
|
this.log.error(LOG_PREFIX + this.redisError);
|
|
248
248
|
|
|
249
249
|
return Promise.reject(this.redisError);
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
const keys = names.map(name => this.keys.
|
|
252
|
+
const keys = names.map(name => this.keys.buildDefinitionKey(name));
|
|
253
253
|
return this.redis.mget(...keys)
|
|
254
|
-
.then((
|
|
255
|
-
const
|
|
254
|
+
.then((stringifiedDefinitions: (string | null)[]) => {
|
|
255
|
+
const definitions: Record<string, IDefinition | null> = {};
|
|
256
256
|
names.forEach((name, idx) => {
|
|
257
|
-
const
|
|
258
|
-
|
|
257
|
+
const definition = stringifiedDefinitions[idx];
|
|
258
|
+
definitions[name] = definition && JSON.parse(definition);
|
|
259
259
|
});
|
|
260
|
-
return Promise.resolve(
|
|
260
|
+
return Promise.resolve(definitions);
|
|
261
261
|
})
|
|
262
262
|
.catch((e: unknown) => {
|
|
263
263
|
this.log.error(LOG_PREFIX + `Could not grab feature flags due to an error: ${e}.`);
|
|
@@ -2,7 +2,7 @@ import type { RedisAdapter } from './RedisAdapter';
|
|
|
2
2
|
import { IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams } from '../types';
|
|
3
3
|
import { validatePrefix } from '../KeyBuilder';
|
|
4
4
|
import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
5
|
-
import {
|
|
5
|
+
import { DefinitionsCacheInRedis } from './DefinitionsCacheInRedis';
|
|
6
6
|
import { SegmentsCacheInRedis } from './SegmentsCacheInRedis';
|
|
7
7
|
import { ImpressionsCacheInRedis } from './ImpressionsCacheInRedis';
|
|
8
8
|
import { EventsCacheInRedis } from './EventsCacheInRedis';
|
|
@@ -59,7 +59,7 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
return {
|
|
62
|
-
|
|
62
|
+
definitions: new DefinitionsCacheInRedis(log, keys, redisClient, settings.sync.__splitFiltersValidation),
|
|
63
63
|
rbSegments: new RBSegmentsCacheInRedis(log, keys, redisClient),
|
|
64
64
|
segments: new SegmentsCacheInRedis(log, keys, redisClient),
|
|
65
65
|
impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
|
|
2
|
+
import { KeyBuilder } from '../KeyBuilder';
|
|
3
|
+
import { IPluggableStorageWrapper } from '../types';
|
|
4
|
+
import { ILogger } from '../../logger/types';
|
|
5
|
+
import { IDefinition, ISplitFiltersValidation } from '../../dtos/types';
|
|
6
|
+
import { LOG_PREFIX } from './constants';
|
|
7
|
+
import { AbstractDefinitionsCacheAsync } from '../AbstractDefinitionsCacheAsync';
|
|
8
|
+
import { returnDifference } from '../../utils/lang/sets';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* IDefinitionsCacheAsync implementation for pluggable storages.
|
|
12
|
+
*/
|
|
13
|
+
export class DefinitionsCachePluggable extends AbstractDefinitionsCacheAsync {
|
|
14
|
+
|
|
15
|
+
private readonly log: ILogger;
|
|
16
|
+
private readonly keys: KeyBuilder;
|
|
17
|
+
private readonly wrapper: IPluggableStorageWrapper;
|
|
18
|
+
private readonly setsFilter: string[];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create a DefinitionsCache that uses a storage wrapper.
|
|
22
|
+
* @param log - Logger instance.
|
|
23
|
+
* @param keys - Key builder.
|
|
24
|
+
* @param wrapper - Adapted wrapper storage.
|
|
25
|
+
*/
|
|
26
|
+
constructor(log: ILogger, keys: KeyBuilder, wrapper: IPluggableStorageWrapper, splitFiltersValidation?: ISplitFiltersValidation) {
|
|
27
|
+
super();
|
|
28
|
+
this.log = log;
|
|
29
|
+
this.keys = keys;
|
|
30
|
+
this.wrapper = wrapper;
|
|
31
|
+
this.setsFilter = splitFiltersValidation ? splitFiltersValidation.groupedFilters.bySet : [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private _decrementCounts(definition: IDefinition) {
|
|
35
|
+
const ttKey = this.keys.buildTrafficTypeKey(definition.trafficTypeName);
|
|
36
|
+
return this.wrapper.decr(ttKey).then(count => {
|
|
37
|
+
if (count === 0) return this.wrapper.del(ttKey);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private _incrementCounts(definition: IDefinition) {
|
|
42
|
+
const ttKey = this.keys.buildTrafficTypeKey(definition.trafficTypeName);
|
|
43
|
+
return this.wrapper.incr(ttKey);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private _updateSets(definitionName: string, setsOfRemovedDefinition?: string[] | null, setsOfAddedDefinition?: string[] | null) {
|
|
47
|
+
const removeFromSets = returnDifference(setsOfRemovedDefinition, setsOfAddedDefinition);
|
|
48
|
+
|
|
49
|
+
let addToSets = returnDifference(setsOfAddedDefinition, setsOfRemovedDefinition);
|
|
50
|
+
if (this.setsFilter.length > 0) {
|
|
51
|
+
addToSets = addToSets.filter(set => {
|
|
52
|
+
return this.setsFilter.some(filterSet => filterSet === set);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const items = [definitionName];
|
|
57
|
+
|
|
58
|
+
return Promise.all([
|
|
59
|
+
...removeFromSets.map(setName => this.wrapper.removeItems(this.keys.buildSetKey(setName), items)),
|
|
60
|
+
...addToSets.map(setName => this.wrapper.addItems(this.keys.buildSetKey(setName), items))
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Add a given definition.
|
|
66
|
+
* The returned promise is resolved when the operation success
|
|
67
|
+
* or rejected if it fails (e.g., wrapper operation fails)
|
|
68
|
+
*/
|
|
69
|
+
add(definition: IDefinition): Promise<boolean> {
|
|
70
|
+
const name = definition.name;
|
|
71
|
+
const definitionKey = this.keys.buildDefinitionKey(name);
|
|
72
|
+
return this.wrapper.get(definitionKey).then(definitionFromStorage => {
|
|
73
|
+
|
|
74
|
+
// handling parsing error
|
|
75
|
+
let parsedPreviousDefinition: IDefinition, stringifiedNewDefinition;
|
|
76
|
+
try {
|
|
77
|
+
parsedPreviousDefinition = definitionFromStorage ? JSON.parse(definitionFromStorage) : undefined;
|
|
78
|
+
stringifiedNewDefinition = JSON.stringify(definition);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
throw new Error('Error parsing feature flag definition: ' + e);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return this.wrapper.set(definitionKey, stringifiedNewDefinition).then(() => {
|
|
84
|
+
// avoid unnecessary increment/decrement operations
|
|
85
|
+
if (parsedPreviousDefinition && parsedPreviousDefinition.trafficTypeName === definition.trafficTypeName) return;
|
|
86
|
+
|
|
87
|
+
// update traffic type counts
|
|
88
|
+
return this._incrementCounts(definition).then(() => {
|
|
89
|
+
if (parsedPreviousDefinition) return this._decrementCounts(parsedPreviousDefinition);
|
|
90
|
+
});
|
|
91
|
+
}).then(() => this._updateSets(name, parsedPreviousDefinition && parsedPreviousDefinition.sets, definition.sets));
|
|
92
|
+
}).then(() => true);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Remove a given definition.
|
|
97
|
+
* The returned promise is resolved when the operation success, with a boolean indicating if the definition existed or not.
|
|
98
|
+
* or rejected if it fails (e.g., wrapper operation fails).
|
|
99
|
+
*/
|
|
100
|
+
remove(name: string) {
|
|
101
|
+
return this.get(name).then((definition) => {
|
|
102
|
+
if (definition) {
|
|
103
|
+
return this._decrementCounts(definition).then(() => this._updateSets(name, definition.sets));
|
|
104
|
+
}
|
|
105
|
+
}).then(() => {
|
|
106
|
+
return this.wrapper.del(this.keys.buildDefinitionKey(name));
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get definition.
|
|
112
|
+
* The returned promise is resolved with the definition or null if it's not defined,
|
|
113
|
+
* or rejected if wrapper operation fails.
|
|
114
|
+
*/
|
|
115
|
+
get(name: string): Promise<IDefinition | null> {
|
|
116
|
+
return this.wrapper.get(this.keys.buildDefinitionKey(name))
|
|
117
|
+
.then(maybeDefinition => maybeDefinition && JSON.parse(maybeDefinition));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get list of definitions.
|
|
122
|
+
* The returned promise is resolved with a map of names to their definition or null if it's not defined,
|
|
123
|
+
* or rejected if wrapper operation fails.
|
|
124
|
+
*/
|
|
125
|
+
getMany(names: string[]): Promise<Record<string, IDefinition | null>> {
|
|
126
|
+
const keys = names.map(name => this.keys.buildDefinitionKey(name));
|
|
127
|
+
|
|
128
|
+
return this.wrapper.getMany(keys).then(stringifiedDefinitions => {
|
|
129
|
+
const definitions: Record<string, IDefinition | null> = {};
|
|
130
|
+
names.forEach((name, idx) => {
|
|
131
|
+
const definition = stringifiedDefinitions[idx];
|
|
132
|
+
definitions[name] = definition && JSON.parse(definition);
|
|
133
|
+
});
|
|
134
|
+
return Promise.resolve(definitions);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get list of all definitions.
|
|
140
|
+
* The returned promise is resolved with the list of definitions,
|
|
141
|
+
* or rejected if wrapper operation fails.
|
|
142
|
+
*/
|
|
143
|
+
getAll(): Promise<IDefinition[]> {
|
|
144
|
+
return this.wrapper.getKeysByPrefix(this.keys.buildDefinitionKeyPrefix())
|
|
145
|
+
.then((listOfKeys) => this.wrapper.getMany(listOfKeys))
|
|
146
|
+
.then((definitions) => definitions.map((definition) => {
|
|
147
|
+
return JSON.parse(definition as string);
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get list of definition names.
|
|
153
|
+
* The returned promise is resolved with the list of names,
|
|
154
|
+
* or rejected if wrapper operation fails.
|
|
155
|
+
*/
|
|
156
|
+
getNames(): Promise<string[]> {
|
|
157
|
+
return this.wrapper.getKeysByPrefix(this.keys.buildDefinitionKeyPrefix()).then(
|
|
158
|
+
(listOfKeys) => listOfKeys.map(this.keys.extractKey)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get list of definition names related to a given list of set names.
|
|
164
|
+
* The returned promise is resolved with the list of names per set.
|
|
165
|
+
* It never rejects (If there is a wrapper error for some set, an empty set is returned for it).
|
|
166
|
+
*/
|
|
167
|
+
getNamesBySets(sets: string[]): Promise<Set<string>[]> {
|
|
168
|
+
return Promise.all(sets.map(set => {
|
|
169
|
+
const setKey = this.keys.buildSetKey(set);
|
|
170
|
+
return this.wrapper.getItems(setKey).catch(() => []);
|
|
171
|
+
})).then(namesBySets => namesBySets.map(namesBySet => new Set(namesBySet)));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check traffic type existence.
|
|
176
|
+
* The returned promise is resolved with a boolean indicating whether the TT exist or not.
|
|
177
|
+
* In case of wrapper operation failures, the promise resolves with a true value, assuming that the TT might exist.
|
|
178
|
+
* It will never be rejected.
|
|
179
|
+
*/
|
|
180
|
+
trafficTypeExists(trafficType: string): Promise<boolean> {
|
|
181
|
+
// If there is a number there should be > 0, otherwise the TT is considered as not existent.
|
|
182
|
+
return this.wrapper.get(this.keys.buildTrafficTypeKey(trafficType))
|
|
183
|
+
.then((ttCount: string | null | number) => {
|
|
184
|
+
if (ttCount === null) return false; // if entry doesn't exist, means that TT doesn't exist
|
|
185
|
+
|
|
186
|
+
ttCount = parseInt(ttCount as string, 10);
|
|
187
|
+
if (!isFiniteNumber(ttCount) || ttCount < 0) {
|
|
188
|
+
this.log.info(LOG_PREFIX + `Could not validate traffic type existence of ${trafficType} due to data corruption of some sorts.`);
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return ttCount > 0;
|
|
193
|
+
}).catch(e => {
|
|
194
|
+
this.log.error(LOG_PREFIX + `Could not validate traffic type existence of ${trafficType} due to an error: ${e}.`);
|
|
195
|
+
// If there is an error, bypass the validation so the event can get tracked.
|
|
196
|
+
return true;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Set till number.
|
|
202
|
+
* The returned promise is resolved when the operation success,
|
|
203
|
+
* or rejected if it fails (e.g., wrapper operation fails).
|
|
204
|
+
*/
|
|
205
|
+
setChangeNumber(changeNumber: number) {
|
|
206
|
+
return this.wrapper.set(this.keys.buildDefinitionsTillKey(), changeNumber + '');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get till number or -1 if it's not defined.
|
|
211
|
+
* The returned promise is resolved with the changeNumber or -1 if it doesn't exist or a wrapper operation fails.
|
|
212
|
+
* The promise will never be rejected.
|
|
213
|
+
*/
|
|
214
|
+
getChangeNumber(): Promise<number> {
|
|
215
|
+
return this.wrapper.get(this.keys.buildDefinitionsTillKey()).then((value) => {
|
|
216
|
+
const i = parseInt(value as string, 10);
|
|
217
|
+
|
|
218
|
+
return isNaNNumber(i) ? -1 : i;
|
|
219
|
+
}).catch((e) => {
|
|
220
|
+
this.log.error(LOG_PREFIX + 'Could not retrieve changeNumber from storage. Error: ' + e);
|
|
221
|
+
return -1;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// @TODO implement if required by DataLoader or producer mode
|
|
226
|
+
clear() {
|
|
227
|
+
return Promise.resolve(true);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
}
|