@splitsoftware/splitio-commons 1.8.2-rc.0 → 1.8.2-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 +24 -22
- package/README.md +1 -1
- package/cjs/logger/constants.js +3 -3
- package/cjs/logger/messages/debug.js +7 -7
- package/cjs/logger/messages/error.js +2 -2
- package/cjs/logger/messages/info.js +3 -3
- package/cjs/logger/messages/warn.js +7 -7
- package/cjs/sdkClient/client.js +20 -20
- package/cjs/sdkClient/clientAttributesDecoration.js +8 -8
- package/cjs/sdkClient/clientInputValidation.js +10 -10
- package/cjs/sdkClient/sdkClient.js +1 -1
- package/cjs/sdkFactory/index.js +1 -1
- package/cjs/sdkManager/index.js +10 -8
- package/cjs/services/splitApi.js +1 -0
- package/cjs/services/splitHttpClient.js +1 -1
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +6 -10
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +8 -16
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +20 -23
- package/cjs/storages/pluggable/SplitsCachePluggable.js +18 -18
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +5 -12
- package/cjs/sync/streaming/pushManager.js +1 -1
- package/cjs/sync/submitters/telemetrySubmitter.js +2 -2
- package/cjs/utils/inputValidation/apiKey.js +26 -25
- package/cjs/utils/inputValidation/preloadedData.js +3 -3
- package/cjs/utils/inputValidation/split.js +1 -1
- package/cjs/utils/inputValidation/splits.js +2 -2
- package/cjs/utils/settingsValidation/index.js +1 -1
- package/esm/logger/constants.js +2 -2
- package/esm/logger/messages/debug.js +7 -7
- package/esm/logger/messages/error.js +2 -2
- package/esm/logger/messages/info.js +3 -3
- package/esm/logger/messages/warn.js +7 -7
- package/esm/sdkClient/client.js +20 -20
- package/esm/sdkClient/clientAttributesDecoration.js +8 -8
- package/esm/sdkClient/clientInputValidation.js +10 -10
- package/esm/sdkClient/sdkClient.js +1 -1
- package/esm/sdkFactory/index.js +1 -1
- package/esm/sdkManager/index.js +10 -8
- package/esm/services/splitApi.js +1 -0
- package/esm/services/splitHttpClient.js +1 -1
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +6 -10
- package/esm/storages/inMemory/SplitsCacheInMemory.js +8 -16
- package/esm/storages/inRedis/SplitsCacheInRedis.js +20 -23
- package/esm/storages/pluggable/SplitsCachePluggable.js +18 -18
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/esm/sync/polling/updaters/splitChangesUpdater.js +5 -12
- package/esm/sync/streaming/pushManager.js +1 -1
- package/esm/sync/submitters/telemetrySubmitter.js +2 -2
- package/esm/utils/inputValidation/apiKey.js +27 -26
- package/esm/utils/inputValidation/preloadedData.js +3 -3
- package/esm/utils/inputValidation/split.js +1 -1
- package/esm/utils/inputValidation/splits.js +2 -2
- package/esm/utils/settingsValidation/index.js +1 -1
- package/package.json +1 -1
- package/src/logger/constants.ts +2 -2
- package/src/logger/messages/debug.ts +7 -7
- package/src/logger/messages/error.ts +2 -2
- package/src/logger/messages/info.ts +3 -3
- package/src/logger/messages/warn.ts +7 -7
- package/src/sdkClient/client.ts +20 -20
- package/src/sdkClient/clientAttributesDecoration.ts +10 -10
- package/src/sdkClient/clientInputValidation.ts +10 -10
- package/src/sdkClient/sdkClient.ts +3 -3
- package/src/sdkFactory/index.ts +1 -1
- package/src/sdkManager/index.ts +11 -8
- package/src/services/splitApi.ts +1 -0
- package/src/services/splitHttpClient.ts +1 -1
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +6 -10
- package/src/storages/inMemory/AttributesCacheInMemory.ts +7 -6
- package/src/storages/inMemory/SplitsCacheInMemory.ts +6 -14
- package/src/storages/inRedis/SplitsCacheInRedis.ts +19 -20
- package/src/storages/pluggable/SplitsCachePluggable.ts +17 -18
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
- package/src/sync/polling/updaters/splitChangesUpdater.ts +6 -12
- package/src/sync/streaming/pushManager.ts +1 -1
- package/src/sync/submitters/telemetrySubmitter.ts +2 -2
- package/src/types.ts +147 -140
- package/src/utils/inputValidation/apiKey.ts +25 -24
- package/src/utils/inputValidation/preloadedData.ts +3 -3
- package/src/utils/inputValidation/split.ts +1 -1
- package/src/utils/inputValidation/splits.ts +1 -1
- package/src/utils/settingsValidation/index.ts +1 -1
- package/types/logger/constants.d.ts +1 -1
- package/types/sdkClient/clientAttributesDecoration.d.ts +7 -7
- package/types/storages/inMemory/AttributesCacheInMemory.d.ts +4 -3
- package/types/types.d.ts +142 -135
- package/types/utils/inputValidation/apiKey.d.ts +5 -5
- package/types/utils/inputValidation/sdkKey.d.ts +7 -0
- package/types/utils/settingsValidation/index.d.ts +1 -1
- package/types/storages/metadataBuilder.d.ts +0 -3
- /package/types/storages/inMemory/{uniqueKeysCacheInMemory.d.ts → UniqueKeysCacheInMemory.d.ts} +0 -0
- /package/types/storages/inMemory/{uniqueKeysCacheInMemoryCS.d.ts → UniqueKeysCacheInMemoryCS.d.ts} +0 -0
- /package/types/storages/inRedis/{uniqueKeysCacheInRedis.d.ts → UniqueKeysCacheInRedis.d.ts} +0 -0
|
@@ -2,7 +2,7 @@ import * as c from '../constants';
|
|
|
2
2
|
|
|
3
3
|
export const codesError: [number, string][] = [
|
|
4
4
|
// evaluator
|
|
5
|
-
[c.ERROR_ENGINE_COMBINER_IFELSEIF, c.LOG_PREFIX_ENGINE_COMBINER + 'Invalid
|
|
5
|
+
[c.ERROR_ENGINE_COMBINER_IFELSEIF, c.LOG_PREFIX_ENGINE_COMBINER + 'Invalid feature flag, no valid rules found'],
|
|
6
6
|
// SDK
|
|
7
7
|
[c.ERROR_LOGLEVEL_INVALID, 'logger: Invalid Log Level - No changes to the logs will be applied.'],
|
|
8
8
|
[c.ERROR_CLIENT_CANNOT_GET_READY, 'The SDK will not get ready. Reason: %s'],
|
|
@@ -10,7 +10,7 @@ export const codesError: [number, string][] = [
|
|
|
10
10
|
[c.ERROR_IMPRESSIONS_LISTENER, c.LOG_PREFIX_IMPRESSIONS_TRACKER + 'Impression listener logImpression method threw: %s.'],
|
|
11
11
|
[c.ERROR_EVENTS_TRACKER, c.LOG_PREFIX_EVENTS_TRACKER + 'Failed to queue %s'],
|
|
12
12
|
// synchronizer
|
|
13
|
-
[c.ERROR_SYNC_OFFLINE_LOADING, c.LOG_PREFIX_SYNC_OFFLINE + 'There was an issue loading the mock
|
|
13
|
+
[c.ERROR_SYNC_OFFLINE_LOADING, c.LOG_PREFIX_SYNC_OFFLINE + 'There was an issue loading the mock feature flags data. No changes will be applied to the current cache. %s'],
|
|
14
14
|
[c.ERROR_STREAMING_SSE, c.LOG_PREFIX_SYNC_STREAMING + 'Failed to connect or error on streaming connection, with error message: %s'],
|
|
15
15
|
[c.ERROR_STREAMING_AUTH, c.LOG_PREFIX_SYNC_STREAMING + 'Failed to authenticate for streaming. Error: %s.'],
|
|
16
16
|
[c.ERROR_HTTP, 'Response status is not OK. Status: %s. URL: %s. Message: %s'],
|
|
@@ -8,7 +8,7 @@ export const codesInfo: [number, string][] = codesWarn.concat([
|
|
|
8
8
|
[c.CLIENT_READY_FROM_CACHE, READY_MSG + ' from cache'],
|
|
9
9
|
[c.CLIENT_READY, READY_MSG],
|
|
10
10
|
// SDK
|
|
11
|
-
[c.IMPRESSION, c.LOG_PREFIX_IMPRESSIONS_TRACKER +'
|
|
11
|
+
[c.IMPRESSION, c.LOG_PREFIX_IMPRESSIONS_TRACKER +'Feature flag: %s. Key: %s. Evaluation: %s. Label: %s'],
|
|
12
12
|
[c.IMPRESSION_QUEUEING, c.LOG_PREFIX_IMPRESSIONS_TRACKER +'Queueing corresponding impression.'],
|
|
13
13
|
[c.NEW_SHARED_CLIENT, 'New shared client instance created.'],
|
|
14
14
|
[c.NEW_FACTORY, 'New Split SDK instance created.'],
|
|
@@ -22,13 +22,13 @@ export const codesInfo: [number, string][] = codesWarn.concat([
|
|
|
22
22
|
[c.POLLING_SMART_PAUSING, c.LOG_PREFIX_SYNC_POLLING + 'Turning segments data polling %s.'],
|
|
23
23
|
[c.POLLING_START, c.LOG_PREFIX_SYNC_POLLING + 'Starting polling'],
|
|
24
24
|
[c.POLLING_STOP, c.LOG_PREFIX_SYNC_POLLING + 'Stopping polling'],
|
|
25
|
-
[c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying download of
|
|
25
|
+
[c.SYNC_SPLITS_FETCH_RETRY, c.LOG_PREFIX_SYNC_SPLITS + 'Retrying download of feature flags #%s. Reason: %s'],
|
|
26
26
|
[c.SUBMITTERS_PUSH_FULL_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full %s queue and reseting timer.'],
|
|
27
27
|
[c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s.'],
|
|
28
28
|
[c.STREAMING_REFRESH_TOKEN, c.LOG_PREFIX_SYNC_STREAMING + 'Refreshing streaming token in %s seconds, and connecting streaming in %s seconds.'],
|
|
29
29
|
[c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect streaming in %s seconds.'],
|
|
30
30
|
[c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting streaming.'],
|
|
31
|
-
[c.STREAMING_DISABLED, c.LOG_PREFIX_SYNC_STREAMING + 'Streaming is disabled for given
|
|
31
|
+
[c.STREAMING_DISABLED, c.LOG_PREFIX_SYNC_STREAMING + 'Streaming is disabled for given SDK key. Switching to polling mode.'],
|
|
32
32
|
[c.STREAMING_DISCONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Disconnecting streaming.'],
|
|
33
33
|
[c.SYNC_START_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming not available. Starting polling.'],
|
|
34
34
|
[c.SYNC_CONTINUE_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming couldn\'t connect. Continue polling.'],
|
|
@@ -7,7 +7,7 @@ export const codesWarn: [number, string][] = codesError.concat([
|
|
|
7
7
|
[c.ENGINE_VALUE_NO_ATTRIBUTES, c.LOG_PREFIX_ENGINE_VALUE + 'Defined attribute [%s], no attributes received.'],
|
|
8
8
|
// synchronizer
|
|
9
9
|
[c.SYNC_MYSEGMENTS_FETCH_RETRY, c.LOG_PREFIX_SYNC_MYSEGMENTS + 'Retrying download of segments #%s. Reason: %s'],
|
|
10
|
-
[c.SYNC_SPLITS_FETCH_FAILS, c.LOG_PREFIX_SYNC_SPLITS + 'Error while doing fetch of
|
|
10
|
+
[c.SYNC_SPLITS_FETCH_FAILS, c.LOG_PREFIX_SYNC_SPLITS + 'Error while doing fetch of feature flags. %s'],
|
|
11
11
|
[c.STREAMING_PARSING_ERROR_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE error notification: %s'],
|
|
12
12
|
[c.STREAMING_PARSING_MESSAGE_FAILS, c.LOG_PREFIX_SYNC_STREAMING + 'Error parsing SSE message notification: %s'],
|
|
13
13
|
[c.STREAMING_FALLBACK, c.LOG_PREFIX_SYNC_STREAMING + 'Falling back to polling mode. Reason: %s'],
|
|
@@ -21,15 +21,15 @@ export const codesWarn: [number, string][] = codesError.concat([
|
|
|
21
21
|
[c.WARN_TRIMMING_PROPERTIES, '%s: Event has more than 300 properties. Some of them will be trimmed when processed.'],
|
|
22
22
|
[c.WARN_CONVERTING, '%s: %s "%s" is not of type string, converting.'],
|
|
23
23
|
[c.WARN_TRIMMING, '%s: %s "%s" has extra whitespace, trimming.'],
|
|
24
|
-
[c.WARN_NOT_EXISTENT_SPLIT, '%s:
|
|
24
|
+
[c.WARN_NOT_EXISTENT_SPLIT, '%s: feature flag "%s" does not exist in this environment. Please double check what feature flags exist in the Split user interface.'],
|
|
25
25
|
[c.WARN_LOWERCASE_TRAFFIC_TYPE, '%s: traffic_type_name should be all lowercase - converting string to lowercase.'],
|
|
26
|
-
[c.WARN_NOT_EXISTENT_TT, '%s: traffic type "%s" does not have any corresponding
|
|
26
|
+
[c.WARN_NOT_EXISTENT_TT, '%s: traffic type "%s" does not have any corresponding feature flag in this environment, make sure you\'re tracking your events to a valid traffic type defined in the Split user interface.'],
|
|
27
27
|
// initialization / settings validation
|
|
28
28
|
[c.WARN_INTEGRATION_INVALID, c.LOG_PREFIX_SETTINGS+': %s integration item(s) at settings is invalid. %s'],
|
|
29
|
-
[c.WARN_SPLITS_FILTER_IGNORED, c.LOG_PREFIX_SETTINGS+':
|
|
30
|
-
[c.WARN_SPLITS_FILTER_INVALID, c.LOG_PREFIX_SETTINGS+':
|
|
31
|
-
[c.WARN_SPLITS_FILTER_EMPTY, c.LOG_PREFIX_SETTINGS+':
|
|
32
|
-
[c.
|
|
29
|
+
[c.WARN_SPLITS_FILTER_IGNORED, c.LOG_PREFIX_SETTINGS+': feature flag filters have been configured but will have no effect if mode is not "%s", since synchronization is being deferred to an external tool.'],
|
|
30
|
+
[c.WARN_SPLITS_FILTER_INVALID, c.LOG_PREFIX_SETTINGS+': feature flag filter at position %s is invalid. It must be an object with a valid filter type ("byName" or "byPrefix") and a list of "values".'],
|
|
31
|
+
[c.WARN_SPLITS_FILTER_EMPTY, c.LOG_PREFIX_SETTINGS+': feature flag filter configuration must be a non-empty array of filter objects.'],
|
|
32
|
+
[c.WARN_SDK_KEY, c.LOG_PREFIX_SETTINGS+': You already have %s. We recommend keeping only one instance of the factory at all times (Singleton pattern) and reusing it throughout your application'],
|
|
33
33
|
|
|
34
34
|
[c.STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching MySegments due to an error processing %s notification: %s'],
|
|
35
35
|
]);
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -13,10 +13,10 @@ import { isStorageSync } from '../trackers/impressionObserver/utils';
|
|
|
13
13
|
|
|
14
14
|
const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
|
|
15
15
|
|
|
16
|
-
function treatmentsNotReady(
|
|
16
|
+
function treatmentsNotReady(featureFlagNames: string[]) {
|
|
17
17
|
const evaluations: Record<string, IEvaluationResult> = {};
|
|
18
|
-
|
|
19
|
-
evaluations[
|
|
18
|
+
featureFlagNames.forEach(featureFlagName => {
|
|
19
|
+
evaluations[featureFlagName] = treatmentNotReady;
|
|
20
20
|
});
|
|
21
21
|
return evaluations;
|
|
22
22
|
}
|
|
@@ -28,12 +28,12 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
28
28
|
const { sdkReadinessManager: { readinessManager }, storage, settings, impressionsTracker, eventTracker, telemetryTracker } = params;
|
|
29
29
|
const { log, mode } = settings;
|
|
30
30
|
|
|
31
|
-
function getTreatment(key: SplitIO.SplitKey,
|
|
31
|
+
function getTreatment(key: SplitIO.SplitKey, featureFlagName: string, attributes: SplitIO.Attributes | undefined, withConfig = false) {
|
|
32
32
|
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
|
|
33
33
|
|
|
34
34
|
const wrapUp = (evaluationResult: IEvaluationResult) => {
|
|
35
35
|
const queue: ImpressionDTO[] = [];
|
|
36
|
-
const treatment = processEvaluation(evaluationResult,
|
|
36
|
+
const treatment = processEvaluation(evaluationResult, featureFlagName, key, attributes, withConfig, `getTreatment${withConfig ? 'withConfig' : ''}`, queue);
|
|
37
37
|
impressionsTracker.track(queue, attributes);
|
|
38
38
|
|
|
39
39
|
stopTelemetryTracker(queue[0] && queue[0].label);
|
|
@@ -41,7 +41,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
const evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
|
|
44
|
-
evaluateFeature(log, key,
|
|
44
|
+
evaluateFeature(log, key, featureFlagName, attributes, storage) :
|
|
45
45
|
isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
|
|
46
46
|
treatmentNotReady :
|
|
47
47
|
Promise.resolve(treatmentNotReady); // Promisify if async
|
|
@@ -49,18 +49,18 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
49
49
|
return thenable(evaluation) ? evaluation.then((res) => wrapUp(res)) : wrapUp(evaluation);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function getTreatmentWithConfig(key: SplitIO.SplitKey,
|
|
53
|
-
return getTreatment(key,
|
|
52
|
+
function getTreatmentWithConfig(key: SplitIO.SplitKey, featureFlagName: string, attributes: SplitIO.Attributes | undefined) {
|
|
53
|
+
return getTreatment(key, featureFlagName, attributes, true);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
function getTreatments(key: SplitIO.SplitKey,
|
|
56
|
+
function getTreatments(key: SplitIO.SplitKey, featureFlagNames: string[], attributes: SplitIO.Attributes | undefined, withConfig = false) {
|
|
57
57
|
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENTS_WITH_CONFIG : TREATMENTS);
|
|
58
58
|
|
|
59
59
|
const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
|
|
60
60
|
const queue: ImpressionDTO[] = [];
|
|
61
61
|
const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
|
|
62
|
-
Object.keys(evaluationResults).forEach(
|
|
63
|
-
treatments[
|
|
62
|
+
Object.keys(evaluationResults).forEach(featureFlagName => {
|
|
63
|
+
treatments[featureFlagName] = processEvaluation(evaluationResults[featureFlagName], featureFlagName, key, attributes, withConfig, `getTreatments${withConfig ? 'withConfig' : ''}`, queue);
|
|
64
64
|
});
|
|
65
65
|
impressionsTracker.track(queue, attributes);
|
|
66
66
|
|
|
@@ -69,22 +69,22 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
|
|
72
|
-
evaluateFeatures(log, key,
|
|
72
|
+
evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
|
|
73
73
|
isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
|
|
74
|
-
treatmentsNotReady(
|
|
75
|
-
Promise.resolve(treatmentsNotReady(
|
|
74
|
+
treatmentsNotReady(featureFlagNames) :
|
|
75
|
+
Promise.resolve(treatmentsNotReady(featureFlagNames)); // Promisify if async
|
|
76
76
|
|
|
77
77
|
return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function getTreatmentsWithConfig(key: SplitIO.SplitKey,
|
|
81
|
-
return getTreatments(key,
|
|
80
|
+
function getTreatmentsWithConfig(key: SplitIO.SplitKey, featureFlagNames: string[], attributes: SplitIO.Attributes | undefined) {
|
|
81
|
+
return getTreatments(key, featureFlagNames, attributes, true);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// Internal function
|
|
85
85
|
function processEvaluation(
|
|
86
86
|
evaluation: IEvaluationResult,
|
|
87
|
-
|
|
87
|
+
featureFlagName: string,
|
|
88
88
|
key: SplitIO.SplitKey,
|
|
89
89
|
attributes: SplitIO.Attributes | undefined,
|
|
90
90
|
withConfig: boolean,
|
|
@@ -95,12 +95,12 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
95
95
|
const bucketingKey = getBucketing(key);
|
|
96
96
|
|
|
97
97
|
const { treatment, label, changeNumber, config = null } = evaluation;
|
|
98
|
-
log.info(IMPRESSION, [
|
|
98
|
+
log.info(IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
|
|
99
99
|
|
|
100
|
-
if (validateSplitExistance(log, readinessManager,
|
|
100
|
+
if (validateSplitExistance(log, readinessManager, featureFlagName, label, invokingMethodName)) {
|
|
101
101
|
log.info(IMPRESSION_QUEUEING);
|
|
102
102
|
queue.push({
|
|
103
|
-
feature:
|
|
103
|
+
feature: featureFlagName,
|
|
104
104
|
keyName: matchingKey,
|
|
105
105
|
treatment,
|
|
106
106
|
time: Date.now(),
|
|
@@ -18,20 +18,20 @@ export function clientAttributesDecoration<TClient extends SplitIO.IClient | Spl
|
|
|
18
18
|
const clientGetTreatmentsWithConfig = client.getTreatmentsWithConfig;
|
|
19
19
|
const clientTrack = client.track;
|
|
20
20
|
|
|
21
|
-
function getTreatment(maybeKey: SplitIO.SplitKey,
|
|
22
|
-
return clientGetTreatment(maybeKey,
|
|
21
|
+
function getTreatment(maybeKey: SplitIO.SplitKey, maybeFeatureFlagName: string, maybeAttributes?: SplitIO.Attributes) {
|
|
22
|
+
return clientGetTreatment(maybeKey, maybeFeatureFlagName, combineAttributes(maybeAttributes));
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function getTreatmentWithConfig(maybeKey: SplitIO.SplitKey,
|
|
26
|
-
return clientGetTreatmentWithConfig(maybeKey,
|
|
25
|
+
function getTreatmentWithConfig(maybeKey: SplitIO.SplitKey, maybeFeatureFlagName: string, maybeAttributes?: SplitIO.Attributes) {
|
|
26
|
+
return clientGetTreatmentWithConfig(maybeKey, maybeFeatureFlagName, combineAttributes(maybeAttributes));
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function getTreatments(maybeKey: SplitIO.SplitKey,
|
|
30
|
-
return clientGetTreatments(maybeKey,
|
|
29
|
+
function getTreatments(maybeKey: SplitIO.SplitKey, maybeFeatureFlagNames: string[], maybeAttributes?: SplitIO.Attributes) {
|
|
30
|
+
return clientGetTreatments(maybeKey, maybeFeatureFlagNames, combineAttributes(maybeAttributes));
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function getTreatmentsWithConfig(maybeKey: SplitIO.SplitKey,
|
|
34
|
-
return clientGetTreatmentsWithConfig(maybeKey,
|
|
33
|
+
function getTreatmentsWithConfig(maybeKey: SplitIO.SplitKey, maybeFeatureFlagNames: string[], maybeAttributes?: SplitIO.Attributes) {
|
|
34
|
+
return clientGetTreatmentsWithConfig(maybeKey, maybeFeatureFlagNames, combineAttributes(maybeAttributes));
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
function track(maybeKey: SplitIO.SplitKey, maybeTT: string, maybeEvent: string, maybeEventValue?: number, maybeProperties?: SplitIO.Properties) {
|
|
@@ -60,7 +60,7 @@ export function clientAttributesDecoration<TClient extends SplitIO.IClient | Spl
|
|
|
60
60
|
* @param {string, number, boolean, list} attributeValue Attribute value
|
|
61
61
|
* @returns {boolean} true if the attribute was stored and false otherways
|
|
62
62
|
*/
|
|
63
|
-
setAttribute(attributeName: string, attributeValue:
|
|
63
|
+
setAttribute(attributeName: string, attributeValue: SplitIO.AttributeType) {
|
|
64
64
|
const attribute: Record<string, Object> = {};
|
|
65
65
|
attribute[attributeName] = attributeValue;
|
|
66
66
|
if (!validateAttributesDeep(log, attribute, 'setAttribute')) return false;
|
|
@@ -95,7 +95,7 @@ export function clientAttributesDecoration<TClient extends SplitIO.IClient | Spl
|
|
|
95
95
|
*
|
|
96
96
|
* @returns {Object} returns all the stored attributes
|
|
97
97
|
*/
|
|
98
|
-
getAttributes()
|
|
98
|
+
getAttributes() {
|
|
99
99
|
return attributeStorage.getAll();
|
|
100
100
|
},
|
|
101
101
|
|
|
@@ -30,10 +30,10 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
|
|
|
30
30
|
/**
|
|
31
31
|
* Avoid repeating this validations code
|
|
32
32
|
*/
|
|
33
|
-
function validateEvaluationParams(maybeKey: SplitIO.SplitKey,
|
|
33
|
+
function validateEvaluationParams(maybeKey: SplitIO.SplitKey, maybeFeatureFlagNameOrNames: string | string[], maybeAttributes: SplitIO.Attributes | undefined, methodName: string) {
|
|
34
34
|
const multi = startsWith(methodName, 'getTreatments');
|
|
35
35
|
const key = validateKey(log, maybeKey, methodName);
|
|
36
|
-
const splitOrSplits = multi ? validateSplits(log,
|
|
36
|
+
const splitOrSplits = multi ? validateSplits(log, maybeFeatureFlagNameOrNames, methodName) : validateSplit(log, maybeFeatureFlagNameOrNames, methodName);
|
|
37
37
|
const attributes = validateAttributes(log, maybeAttributes, methodName);
|
|
38
38
|
const isOperational = validateIfNotDestroyed(log, readinessManager, methodName);
|
|
39
39
|
|
|
@@ -53,8 +53,8 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
|
|
|
53
53
|
return isSync ? value : Promise.resolve(value);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
function getTreatment(maybeKey: SplitIO.SplitKey,
|
|
57
|
-
const params = validateEvaluationParams(maybeKey,
|
|
56
|
+
function getTreatment(maybeKey: SplitIO.SplitKey, maybeFeatureFlagName: string, maybeAttributes?: SplitIO.Attributes) {
|
|
57
|
+
const params = validateEvaluationParams(maybeKey, maybeFeatureFlagName, maybeAttributes, 'getTreatment');
|
|
58
58
|
|
|
59
59
|
if (params.valid) {
|
|
60
60
|
return client.getTreatment(params.key as SplitIO.SplitKey, params.splitOrSplits as string, params.attributes as SplitIO.Attributes | undefined);
|
|
@@ -63,8 +63,8 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
function getTreatmentWithConfig(maybeKey: SplitIO.SplitKey,
|
|
67
|
-
const params = validateEvaluationParams(maybeKey,
|
|
66
|
+
function getTreatmentWithConfig(maybeKey: SplitIO.SplitKey, maybeFeatureFlagName: string, maybeAttributes?: SplitIO.Attributes) {
|
|
67
|
+
const params = validateEvaluationParams(maybeKey, maybeFeatureFlagName, maybeAttributes, 'getTreatmentWithConfig');
|
|
68
68
|
|
|
69
69
|
if (params.valid) {
|
|
70
70
|
return client.getTreatmentWithConfig(params.key as SplitIO.SplitKey, params.splitOrSplits as string, params.attributes as SplitIO.Attributes | undefined);
|
|
@@ -73,8 +73,8 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
function getTreatments(maybeKey: SplitIO.SplitKey,
|
|
77
|
-
const params = validateEvaluationParams(maybeKey,
|
|
76
|
+
function getTreatments(maybeKey: SplitIO.SplitKey, maybeFeatureFlagNames: string[], maybeAttributes?: SplitIO.Attributes) {
|
|
77
|
+
const params = validateEvaluationParams(maybeKey, maybeFeatureFlagNames, maybeAttributes, 'getTreatments');
|
|
78
78
|
|
|
79
79
|
if (params.valid) {
|
|
80
80
|
return client.getTreatments(params.key as SplitIO.SplitKey, params.splitOrSplits as string[], params.attributes as SplitIO.Attributes | undefined);
|
|
@@ -86,8 +86,8 @@ export function clientInputValidationDecorator<TClient extends SplitIO.IClient |
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
function getTreatmentsWithConfig(maybeKey: SplitIO.SplitKey,
|
|
90
|
-
const params = validateEvaluationParams(maybeKey,
|
|
89
|
+
function getTreatmentsWithConfig(maybeKey: SplitIO.SplitKey, maybeFeatureFlagNames: string[], maybeAttributes?: SplitIO.Attributes) {
|
|
90
|
+
const params = validateEvaluationParams(maybeKey, maybeFeatureFlagNames, maybeAttributes, 'getTreatmentsWithConfig');
|
|
91
91
|
|
|
92
92
|
if (params.valid) {
|
|
93
93
|
return client.getTreatmentsWithConfig(params.key as SplitIO.SplitKey, params.splitOrSplits as string[], params.attributes as SplitIO.Attributes | undefined);
|
|
@@ -20,9 +20,9 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
|
|
|
20
20
|
//get the actual time elapsed in ms
|
|
21
21
|
const timeElapsed = now - lastActionTime;
|
|
22
22
|
//check if the time elapsed is less than desired cooldown
|
|
23
|
-
if (timeElapsed < time){
|
|
23
|
+
if (timeElapsed < time) {
|
|
24
24
|
//if yes, return message with remaining time in seconds
|
|
25
|
-
settings.log.warn(`Flush cooldown, remaining time ${(time-timeElapsed)/1000} seconds`);
|
|
25
|
+
settings.log.warn(`Flush cooldown, remaining time ${(time - timeElapsed) / 1000} seconds`);
|
|
26
26
|
return Promise.resolve();
|
|
27
27
|
} else {
|
|
28
28
|
//Do the requested action and re-assign the lastActionTime
|
|
@@ -64,7 +64,7 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
|
|
|
64
64
|
sdkReadinessManager.readinessManager.destroy();
|
|
65
65
|
signalListener && signalListener.stop();
|
|
66
66
|
|
|
67
|
-
// Release the
|
|
67
|
+
// Release the SDK Key if it is the main client
|
|
68
68
|
if (!isSharedClient) releaseApiKey(settings.core.authorizationKey);
|
|
69
69
|
|
|
70
70
|
if (uniqueKeysTracker) uniqueKeysTracker.stop();
|
package/src/sdkFactory/index.ts
CHANGED
|
@@ -26,7 +26,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
|
|
|
26
26
|
filterAdapterFactory } = params;
|
|
27
27
|
const { log, sync: { impressionsMode } } = settings;
|
|
28
28
|
|
|
29
|
-
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid
|
|
29
|
+
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
|
|
30
30
|
// On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
|
|
31
31
|
|
|
32
32
|
// We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
|
package/src/sdkManager/index.ts
CHANGED
|
@@ -8,6 +8,10 @@ import { ISplit } from '../dtos/types';
|
|
|
8
8
|
import { SplitIO } from '../types';
|
|
9
9
|
import { ILogger } from '../logger/types';
|
|
10
10
|
|
|
11
|
+
const SPLIT_FN_LABEL = 'split';
|
|
12
|
+
const SPLITS_FN_LABEL = 'splits';
|
|
13
|
+
const NAMES_FN_LABEL = 'names';
|
|
14
|
+
|
|
11
15
|
function collectTreatments(splitObject: ISplit) {
|
|
12
16
|
const conditions = splitObject.conditions;
|
|
13
17
|
// Rollout conditions are supposed to have the entire partitions list, so we find the first one.
|
|
@@ -47,17 +51,16 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
|
|
|
47
51
|
splits: TSplitCache,
|
|
48
52
|
{ readinessManager, sdkStatus }: ISdkReadinessManager
|
|
49
53
|
): TSplitCache extends ISplitsCacheAsync ? SplitIO.IAsyncManager : SplitIO.IManager {
|
|
50
|
-
const SPLIT_FN_LABEL = 'split';
|
|
51
54
|
|
|
52
55
|
return objectAssign(
|
|
53
56
|
// Proto-linkage of the readiness Event Emitter
|
|
54
57
|
Object.create(sdkStatus),
|
|
55
58
|
{
|
|
56
59
|
/**
|
|
57
|
-
* Get the
|
|
60
|
+
* Get the feature flag object corresponding to the given feature flag name if valid
|
|
58
61
|
*/
|
|
59
|
-
split(
|
|
60
|
-
const splitName = validateSplit(log,
|
|
62
|
+
split(featureFlagName: string) {
|
|
63
|
+
const splitName = validateSplit(log, featureFlagName, SPLIT_FN_LABEL);
|
|
61
64
|
if (!validateIfNotDestroyed(log, readinessManager, SPLIT_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
|
|
62
65
|
return null;
|
|
63
66
|
}
|
|
@@ -76,10 +79,10 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
|
|
|
76
79
|
return objectToView(split);
|
|
77
80
|
},
|
|
78
81
|
/**
|
|
79
|
-
* Get the
|
|
82
|
+
* Get the feature flag objects present on the factory storage
|
|
80
83
|
*/
|
|
81
84
|
splits() {
|
|
82
|
-
if (!validateIfNotDestroyed(log, readinessManager,
|
|
85
|
+
if (!validateIfNotDestroyed(log, readinessManager, SPLITS_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
|
|
83
86
|
return [];
|
|
84
87
|
}
|
|
85
88
|
const currentSplits = splits.getAll();
|
|
@@ -89,10 +92,10 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
|
|
|
89
92
|
objectsToViews(currentSplits);
|
|
90
93
|
},
|
|
91
94
|
/**
|
|
92
|
-
* Get the
|
|
95
|
+
* Get the feature flag names present on the factory storage
|
|
93
96
|
*/
|
|
94
97
|
names() {
|
|
95
|
-
if (!validateIfNotDestroyed(log, readinessManager,
|
|
98
|
+
if (!validateIfNotDestroyed(log, readinessManager, NAMES_FN_LABEL) || !validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
|
|
96
99
|
return [];
|
|
97
100
|
}
|
|
98
101
|
const splitNames = splits.getSplitNames();
|
package/src/services/splitApi.ts
CHANGED
|
@@ -30,6 +30,7 @@ export function splitApiFactory(
|
|
|
30
30
|
const splitHttpClient = splitHttpClientFactory(settings, platform.getFetch, platform.getOptions);
|
|
31
31
|
|
|
32
32
|
return {
|
|
33
|
+
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
33
34
|
getSdkAPIHealthCheck() {
|
|
34
35
|
const url = `${urls.sdk}/version`;
|
|
35
36
|
return splitHttpClient(url).then(() => true).catch(() => false);
|
|
@@ -55,7 +55,7 @@ export function splitHttpClientFactory(settings: Pick<ISettings, 'log' | 'versio
|
|
|
55
55
|
|
|
56
56
|
if (resp) { // An HTTP error
|
|
57
57
|
switch (resp.status) {
|
|
58
|
-
case 404: msg = 'Invalid
|
|
58
|
+
case 404: msg = 'Invalid SDK key or resource not found.';
|
|
59
59
|
break;
|
|
60
60
|
// Don't use resp.statusText since reason phrase is removed in HTTP/2
|
|
61
61
|
default: msg = error.message;
|
|
@@ -41,10 +41,8 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
41
41
|
private _decrementCounts(split: ISplit | null) {
|
|
42
42
|
try {
|
|
43
43
|
if (split) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
this._decrementCount(ttKey);
|
|
47
|
-
}
|
|
44
|
+
const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
45
|
+
this._decrementCount(ttKey);
|
|
48
46
|
|
|
49
47
|
if (usesSegments(split)) {
|
|
50
48
|
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
@@ -59,11 +57,9 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
59
57
|
private _incrementCounts(split: ISplit) {
|
|
60
58
|
try {
|
|
61
59
|
if (split) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
|
|
66
|
-
}
|
|
60
|
+
const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
61
|
+
// @ts-expect-error
|
|
62
|
+
localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
|
|
67
63
|
|
|
68
64
|
if (usesSegments(split)) {
|
|
69
65
|
const segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
@@ -104,11 +100,11 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
104
100
|
const splitKey = this.keys.buildSplitKey(name);
|
|
105
101
|
const splitFromLocalStorage = localStorage.getItem(splitKey);
|
|
106
102
|
const previousSplit = splitFromLocalStorage ? JSON.parse(splitFromLocalStorage) : null;
|
|
107
|
-
this._decrementCounts(previousSplit);
|
|
108
103
|
|
|
109
104
|
localStorage.setItem(splitKey, JSON.stringify(split));
|
|
110
105
|
|
|
111
106
|
this._incrementCounts(split);
|
|
107
|
+
this._decrementCounts(previousSplit);
|
|
112
108
|
|
|
113
109
|
return true;
|
|
114
110
|
} catch (e) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { SplitIO } from '../../types';
|
|
1
2
|
import { objectAssign } from '../../utils/lang/objectAssign';
|
|
2
3
|
|
|
3
4
|
export class AttributesCacheInMemory {
|
|
4
5
|
|
|
5
|
-
private attributesCache: Record<string,
|
|
6
|
+
private attributesCache: Record<string, SplitIO.AttributeType> = {};
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -12,7 +13,7 @@ export class AttributesCacheInMemory {
|
|
|
12
13
|
* @param {Object} attributeValue attribute value
|
|
13
14
|
* @returns {boolean} the attribute was stored
|
|
14
15
|
*/
|
|
15
|
-
setAttribute(attributeName: string, attributeValue:
|
|
16
|
+
setAttribute(attributeName: string, attributeValue: SplitIO.AttributeType) {
|
|
16
17
|
this.attributesCache[attributeName] = attributeValue;
|
|
17
18
|
return true;
|
|
18
19
|
}
|
|
@@ -23,7 +24,7 @@ export class AttributesCacheInMemory {
|
|
|
23
24
|
* @param {string} attributeName attribute name
|
|
24
25
|
* @returns {Object?} stored attribute value
|
|
25
26
|
*/
|
|
26
|
-
getAttribute(attributeName: string)
|
|
27
|
+
getAttribute(attributeName: string) {
|
|
27
28
|
return this.attributesCache[attributeName];
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -33,7 +34,7 @@ export class AttributesCacheInMemory {
|
|
|
33
34
|
* @param {[string, Object]} attributes attributes to create or update
|
|
34
35
|
* @returns {boolean} attributes were stored
|
|
35
36
|
*/
|
|
36
|
-
setAttributes(attributes: Record<string, Object>)
|
|
37
|
+
setAttributes(attributes: Record<string, Object>) {
|
|
37
38
|
this.attributesCache = objectAssign(this.attributesCache, attributes);
|
|
38
39
|
return true;
|
|
39
40
|
}
|
|
@@ -43,7 +44,7 @@ export class AttributesCacheInMemory {
|
|
|
43
44
|
*
|
|
44
45
|
* @returns {Map<string, Object>} stored attributes
|
|
45
46
|
*/
|
|
46
|
-
getAll()
|
|
47
|
+
getAll() {
|
|
47
48
|
return this.attributesCache;
|
|
48
49
|
}
|
|
49
50
|
|
|
@@ -53,7 +54,7 @@ export class AttributesCacheInMemory {
|
|
|
53
54
|
* @param {string} attributeName attribute to remove
|
|
54
55
|
* @returns {boolean} attribute removed
|
|
55
56
|
*/
|
|
56
|
-
removeAttribute(attributeName: string)
|
|
57
|
+
removeAttribute(attributeName: string) {
|
|
57
58
|
if (Object.keys(this.attributesCache).indexOf(attributeName) >= 0) {
|
|
58
59
|
delete this.attributesCache[attributeName];
|
|
59
60
|
return true;
|
|
@@ -24,11 +24,9 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
24
24
|
const previousSplit = this.getSplit(name);
|
|
25
25
|
if (previousSplit) { // We had this Split already
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (!this.ttCache[previousTtName]) delete this.ttCache[previousTtName];
|
|
31
|
-
}
|
|
27
|
+
const previousTtName = previousSplit.trafficTypeName;
|
|
28
|
+
this.ttCache[previousTtName]--;
|
|
29
|
+
if (!this.ttCache[previousTtName]) delete this.ttCache[previousTtName];
|
|
32
30
|
|
|
33
31
|
if (usesSegments(previousSplit)) { // Substract from segments count for the previous version of this Split.
|
|
34
32
|
this.splitsWithSegmentsCount--;
|
|
@@ -40,10 +38,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
40
38
|
this.splitsCache[name] = split;
|
|
41
39
|
// Update TT cache
|
|
42
40
|
const ttName = split.trafficTypeName;
|
|
43
|
-
|
|
44
|
-
if (!this.ttCache[ttName]) this.ttCache[ttName] = 0;
|
|
45
|
-
this.ttCache[ttName]++;
|
|
46
|
-
}
|
|
41
|
+
this.ttCache[ttName] = (this.ttCache[ttName] || 0) + 1;
|
|
47
42
|
|
|
48
43
|
// Add to segments count for the new version of the Split
|
|
49
44
|
if (usesSegments(split)) this.splitsWithSegmentsCount++;
|
|
@@ -61,11 +56,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
61
56
|
delete this.splitsCache[name];
|
|
62
57
|
|
|
63
58
|
const ttName = split.trafficTypeName;
|
|
64
|
-
|
|
65
|
-
if (ttName)
|
|
66
|
-
this.ttCache[ttName]--; // Update tt cache
|
|
67
|
-
if (!this.ttCache[ttName]) delete this.ttCache[ttName];
|
|
68
|
-
}
|
|
59
|
+
this.ttCache[ttName]--; // Update tt cache
|
|
60
|
+
if (!this.ttCache[ttName]) delete this.ttCache[ttName];
|
|
69
61
|
|
|
70
62
|
// Update the segments count.
|
|
71
63
|
if (usesSegments(split)) this.splitsWithSegmentsCount--;
|
|
@@ -45,19 +45,15 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
private _decrementCounts(split: ISplit) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return this.redis.
|
|
51
|
-
|
|
52
|
-
});
|
|
53
|
-
}
|
|
48
|
+
const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
49
|
+
return this.redis.decr(ttKey).then(count => {
|
|
50
|
+
if (count === 0) return this.redis.del(ttKey);
|
|
51
|
+
});
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
private _incrementCounts(split: ISplit) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return this.redis.incr(ttKey);
|
|
60
|
-
}
|
|
55
|
+
const ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
56
|
+
return this.redis.incr(ttKey);
|
|
61
57
|
}
|
|
62
58
|
|
|
63
59
|
/**
|
|
@@ -70,21 +66,24 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
70
66
|
return this.redis.get(splitKey).then(splitFromStorage => {
|
|
71
67
|
|
|
72
68
|
// handling parsing errors
|
|
73
|
-
let parsedPreviousSplit, newStringifiedSplit;
|
|
69
|
+
let parsedPreviousSplit: ISplit, newStringifiedSplit;
|
|
74
70
|
try {
|
|
75
71
|
parsedPreviousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : undefined;
|
|
76
72
|
newStringifiedSplit = JSON.stringify(split);
|
|
77
73
|
} catch (e) {
|
|
78
|
-
throw new Error('Error parsing
|
|
74
|
+
throw new Error('Error parsing feature flag definition: ' + e);
|
|
79
75
|
}
|
|
80
76
|
|
|
81
|
-
return
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
77
|
+
return this.redis.set(splitKey, newStringifiedSplit).then(() => {
|
|
78
|
+
// avoid unnecessary increment/decrement operations
|
|
79
|
+
if (parsedPreviousSplit && parsedPreviousSplit.trafficTypeName === split.trafficTypeName) return;
|
|
80
|
+
|
|
81
|
+
// update traffic type counts
|
|
82
|
+
return this._incrementCounts(split).then(() => {
|
|
83
|
+
if (parsedPreviousSplit) return this._decrementCounts(parsedPreviousSplit);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}).then(() => true);
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
/**
|
|
@@ -247,7 +246,7 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
247
246
|
return Promise.resolve(splits);
|
|
248
247
|
})
|
|
249
248
|
.catch(e => {
|
|
250
|
-
this.log.error(LOG_PREFIX + `Could not grab
|
|
249
|
+
this.log.error(LOG_PREFIX + `Could not grab feature flags due to an error: ${e}.`);
|
|
251
250
|
return Promise.reject(e);
|
|
252
251
|
});
|
|
253
252
|
}
|