@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
|
@@ -13,10 +13,10 @@ export function clientInputValidationDecorator(settings, client, readinessManage
|
|
|
13
13
|
/**
|
|
14
14
|
* Avoid repeating this validations code
|
|
15
15
|
*/
|
|
16
|
-
function validateEvaluationParams(maybeKey,
|
|
16
|
+
function validateEvaluationParams(maybeKey, maybeFeatureFlagNameOrNames, maybeAttributes, methodName) {
|
|
17
17
|
var multi = startsWith(methodName, 'getTreatments');
|
|
18
18
|
var key = validateKey(log, maybeKey, methodName);
|
|
19
|
-
var splitOrSplits = multi ? validateSplits(log,
|
|
19
|
+
var splitOrSplits = multi ? validateSplits(log, maybeFeatureFlagNameOrNames, methodName) : validateSplit(log, maybeFeatureFlagNameOrNames, methodName);
|
|
20
20
|
var attributes = validateAttributes(log, maybeAttributes, methodName);
|
|
21
21
|
var isOperational = validateIfNotDestroyed(log, readinessManager, methodName);
|
|
22
22
|
validateIfOperational(log, readinessManager, methodName);
|
|
@@ -31,8 +31,8 @@ export function clientInputValidationDecorator(settings, client, readinessManage
|
|
|
31
31
|
function wrapResult(value) {
|
|
32
32
|
return isSync ? value : Promise.resolve(value);
|
|
33
33
|
}
|
|
34
|
-
function getTreatment(maybeKey,
|
|
35
|
-
var params = validateEvaluationParams(maybeKey,
|
|
34
|
+
function getTreatment(maybeKey, maybeFeatureFlagName, maybeAttributes) {
|
|
35
|
+
var params = validateEvaluationParams(maybeKey, maybeFeatureFlagName, maybeAttributes, 'getTreatment');
|
|
36
36
|
if (params.valid) {
|
|
37
37
|
return client.getTreatment(params.key, params.splitOrSplits, params.attributes);
|
|
38
38
|
}
|
|
@@ -40,8 +40,8 @@ export function clientInputValidationDecorator(settings, client, readinessManage
|
|
|
40
40
|
return wrapResult(CONTROL);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
function getTreatmentWithConfig(maybeKey,
|
|
44
|
-
var params = validateEvaluationParams(maybeKey,
|
|
43
|
+
function getTreatmentWithConfig(maybeKey, maybeFeatureFlagName, maybeAttributes) {
|
|
44
|
+
var params = validateEvaluationParams(maybeKey, maybeFeatureFlagName, maybeAttributes, 'getTreatmentWithConfig');
|
|
45
45
|
if (params.valid) {
|
|
46
46
|
return client.getTreatmentWithConfig(params.key, params.splitOrSplits, params.attributes);
|
|
47
47
|
}
|
|
@@ -49,8 +49,8 @@ export function clientInputValidationDecorator(settings, client, readinessManage
|
|
|
49
49
|
return wrapResult(objectAssign({}, CONTROL_WITH_CONFIG));
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
function getTreatments(maybeKey,
|
|
53
|
-
var params = validateEvaluationParams(maybeKey,
|
|
52
|
+
function getTreatments(maybeKey, maybeFeatureFlagNames, maybeAttributes) {
|
|
53
|
+
var params = validateEvaluationParams(maybeKey, maybeFeatureFlagNames, maybeAttributes, 'getTreatments');
|
|
54
54
|
if (params.valid) {
|
|
55
55
|
return client.getTreatments(params.key, params.splitOrSplits, params.attributes);
|
|
56
56
|
}
|
|
@@ -61,8 +61,8 @@ export function clientInputValidationDecorator(settings, client, readinessManage
|
|
|
61
61
|
return wrapResult(res_1);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
function getTreatmentsWithConfig(maybeKey,
|
|
65
|
-
var params = validateEvaluationParams(maybeKey,
|
|
64
|
+
function getTreatmentsWithConfig(maybeKey, maybeFeatureFlagNames, maybeAttributes) {
|
|
65
|
+
var params = validateEvaluationParams(maybeKey, maybeFeatureFlagNames, maybeAttributes, 'getTreatmentsWithConfig');
|
|
66
66
|
if (params.valid) {
|
|
67
67
|
return client.getTreatmentsWithConfig(params.key, params.splitOrSplits, params.attributes);
|
|
68
68
|
}
|
|
@@ -49,7 +49,7 @@ export function sdkClientFactory(params, isSharedClient) {
|
|
|
49
49
|
// Cleanup event listeners
|
|
50
50
|
sdkReadinessManager.readinessManager.destroy();
|
|
51
51
|
signalListener && signalListener.stop();
|
|
52
|
-
// Release the
|
|
52
|
+
// Release the SDK Key if it is the main client
|
|
53
53
|
if (!isSharedClient)
|
|
54
54
|
releaseApiKey(settings.core.authorizationKey);
|
|
55
55
|
if (uniqueKeysTracker)
|
package/esm/sdkFactory/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { NONE, OPTIMIZED } from '../utils/constants';
|
|
|
18
18
|
export function sdkFactory(params) {
|
|
19
19
|
var settings = params.settings, platform = params.platform, storageFactory = params.storageFactory, splitApiFactory = params.splitApiFactory, extraProps = params.extraProps, syncManagerFactory = params.syncManagerFactory, SignalListener = params.SignalListener, impressionsObserverFactory = params.impressionsObserverFactory, integrationsManagerFactory = params.integrationsManagerFactory, sdkManagerFactory = params.sdkManagerFactory, sdkClientMethodFactory = params.sdkClientMethodFactory, filterAdapterFactory = params.filterAdapterFactory;
|
|
20
20
|
var log = settings.log, impressionsMode = settings.sync.impressionsMode;
|
|
21
|
-
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid
|
|
21
|
+
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
|
|
22
22
|
// On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
|
|
23
23
|
// We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
|
|
24
24
|
validateAndTrackApiKey(log, settings.core.authorizationKey);
|
package/esm/sdkManager/index.js
CHANGED
|
@@ -2,6 +2,9 @@ import { objectAssign } from '../utils/lang/objectAssign';
|
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
3
|
import { find } from '../utils/lang';
|
|
4
4
|
import { validateSplit, validateSplitExistance, validateIfNotDestroyed, validateIfOperational } from '../utils/inputValidation';
|
|
5
|
+
var SPLIT_FN_LABEL = 'split';
|
|
6
|
+
var SPLITS_FN_LABEL = 'splits';
|
|
7
|
+
var NAMES_FN_LABEL = 'names';
|
|
5
8
|
function collectTreatments(splitObject) {
|
|
6
9
|
var conditions = splitObject.conditions;
|
|
7
10
|
// Rollout conditions are supposed to have the entire partitions list, so we find the first one.
|
|
@@ -35,15 +38,14 @@ function objectsToViews(splitObjects) {
|
|
|
35
38
|
}
|
|
36
39
|
export function sdkManagerFactory(log, splits, _a) {
|
|
37
40
|
var readinessManager = _a.readinessManager, sdkStatus = _a.sdkStatus;
|
|
38
|
-
var SPLIT_FN_LABEL = 'split';
|
|
39
41
|
return objectAssign(
|
|
40
42
|
// Proto-linkage of the readiness Event Emitter
|
|
41
43
|
Object.create(sdkStatus), {
|
|
42
44
|
/**
|
|
43
|
-
* Get the
|
|
45
|
+
* Get the feature flag object corresponding to the given feature flag name if valid
|
|
44
46
|
*/
|
|
45
|
-
split: function (
|
|
46
|
-
var splitName = validateSplit(log,
|
|
47
|
+
split: function (featureFlagName) {
|
|
48
|
+
var splitName = validateSplit(log, featureFlagName, SPLIT_FN_LABEL);
|
|
47
49
|
if (!validateIfNotDestroyed(log, readinessManager, SPLIT_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
|
|
48
50
|
return null;
|
|
49
51
|
}
|
|
@@ -58,10 +60,10 @@ export function sdkManagerFactory(log, splits, _a) {
|
|
|
58
60
|
return objectToView(split);
|
|
59
61
|
},
|
|
60
62
|
/**
|
|
61
|
-
* Get the
|
|
63
|
+
* Get the feature flag objects present on the factory storage
|
|
62
64
|
*/
|
|
63
65
|
splits: function () {
|
|
64
|
-
if (!validateIfNotDestroyed(log, readinessManager,
|
|
66
|
+
if (!validateIfNotDestroyed(log, readinessManager, SPLITS_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
|
|
65
67
|
return [];
|
|
66
68
|
}
|
|
67
69
|
var currentSplits = splits.getAll();
|
|
@@ -70,10 +72,10 @@ export function sdkManagerFactory(log, splits, _a) {
|
|
|
70
72
|
objectsToViews(currentSplits);
|
|
71
73
|
},
|
|
72
74
|
/**
|
|
73
|
-
* Get the
|
|
75
|
+
* Get the feature flag names present on the factory storage
|
|
74
76
|
*/
|
|
75
77
|
names: function () {
|
|
76
|
-
if (!validateIfNotDestroyed(log, readinessManager,
|
|
78
|
+
if (!validateIfNotDestroyed(log, readinessManager, NAMES_FN_LABEL) || !validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
|
|
77
79
|
return [];
|
|
78
80
|
}
|
|
79
81
|
var splitNames = splits.getSplitNames();
|
package/esm/services/splitApi.js
CHANGED
|
@@ -17,6 +17,7 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
17
17
|
var SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
18
18
|
var splitHttpClient = splitHttpClientFactory(settings, platform.getFetch, platform.getOptions);
|
|
19
19
|
return {
|
|
20
|
+
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
20
21
|
getSdkAPIHealthCheck: function () {
|
|
21
22
|
var url = urls.sdk + "/version";
|
|
22
23
|
return splitHttpClient(url).then(function () { return true; }).catch(function () { return false; });
|
|
@@ -50,7 +50,7 @@ export function splitHttpClientFactory(settings, getFetch, getOptions) {
|
|
|
50
50
|
if (resp) { // An HTTP error
|
|
51
51
|
switch (resp.status) {
|
|
52
52
|
case 404:
|
|
53
|
-
msg = 'Invalid
|
|
53
|
+
msg = 'Invalid SDK key or resource not found.';
|
|
54
54
|
break;
|
|
55
55
|
// Don't use resp.statusText since reason phrase is removed in HTTP/2
|
|
56
56
|
default:
|
|
@@ -34,10 +34,8 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
34
34
|
SplitsCacheInLocal.prototype._decrementCounts = function (split) {
|
|
35
35
|
try {
|
|
36
36
|
if (split) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
this._decrementCount(ttKey);
|
|
40
|
-
}
|
|
37
|
+
var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
38
|
+
this._decrementCount(ttKey);
|
|
41
39
|
if (usesSegments(split)) {
|
|
42
40
|
var segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
43
41
|
this._decrementCount(segmentsCountKey);
|
|
@@ -51,11 +49,9 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
51
49
|
SplitsCacheInLocal.prototype._incrementCounts = function (split) {
|
|
52
50
|
try {
|
|
53
51
|
if (split) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
|
|
58
|
-
}
|
|
52
|
+
var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
53
|
+
// @ts-expect-error
|
|
54
|
+
localStorage.setItem(ttKey, toNumber(localStorage.getItem(ttKey)) + 1);
|
|
59
55
|
if (usesSegments(split)) {
|
|
60
56
|
var segmentsCountKey = this.keys.buildSplitsWithSegmentCountKey();
|
|
61
57
|
// @ts-expect-error
|
|
@@ -92,9 +88,9 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
92
88
|
var splitKey = this.keys.buildSplitKey(name);
|
|
93
89
|
var splitFromLocalStorage = localStorage.getItem(splitKey);
|
|
94
90
|
var previousSplit = splitFromLocalStorage ? JSON.parse(splitFromLocalStorage) : null;
|
|
95
|
-
this._decrementCounts(previousSplit);
|
|
96
91
|
localStorage.setItem(splitKey, JSON.stringify(split));
|
|
97
92
|
this._incrementCounts(split);
|
|
93
|
+
this._decrementCounts(previousSplit);
|
|
98
94
|
return true;
|
|
99
95
|
}
|
|
100
96
|
catch (e) {
|
|
@@ -24,12 +24,10 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
|
|
|
24
24
|
SplitsCacheInMemory.prototype.addSplit = function (name, split) {
|
|
25
25
|
var previousSplit = this.getSplit(name);
|
|
26
26
|
if (previousSplit) { // We had this Split already
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
delete this.ttCache[previousTtName];
|
|
32
|
-
}
|
|
27
|
+
var previousTtName = previousSplit.trafficTypeName;
|
|
28
|
+
this.ttCache[previousTtName]--;
|
|
29
|
+
if (!this.ttCache[previousTtName])
|
|
30
|
+
delete this.ttCache[previousTtName];
|
|
33
31
|
if (usesSegments(previousSplit)) { // Substract from segments count for the previous version of this Split.
|
|
34
32
|
this.splitsWithSegmentsCount--;
|
|
35
33
|
}
|
|
@@ -39,11 +37,7 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
|
|
|
39
37
|
this.splitsCache[name] = split;
|
|
40
38
|
// Update TT cache
|
|
41
39
|
var ttName = split.trafficTypeName;
|
|
42
|
-
|
|
43
|
-
if (!this.ttCache[ttName])
|
|
44
|
-
this.ttCache[ttName] = 0;
|
|
45
|
-
this.ttCache[ttName]++;
|
|
46
|
-
}
|
|
40
|
+
this.ttCache[ttName] = (this.ttCache[ttName] || 0) + 1;
|
|
47
41
|
// Add to segments count for the new version of the Split
|
|
48
42
|
if (usesSegments(split))
|
|
49
43
|
this.splitsWithSegmentsCount++;
|
|
@@ -59,11 +53,9 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
|
|
|
59
53
|
// Delete the Split
|
|
60
54
|
delete this.splitsCache[name];
|
|
61
55
|
var ttName = split.trafficTypeName;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
delete this.ttCache[ttName];
|
|
66
|
-
}
|
|
56
|
+
this.ttCache[ttName]--; // Update tt cache
|
|
57
|
+
if (!this.ttCache[ttName])
|
|
58
|
+
delete this.ttCache[ttName];
|
|
67
59
|
// Update the segments count.
|
|
68
60
|
if (usesSegments(split))
|
|
69
61
|
this.splitsWithSegmentsCount--;
|
|
@@ -35,19 +35,15 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
|
|
|
35
35
|
}
|
|
36
36
|
SplitsCacheInRedis.prototype._decrementCounts = function (split) {
|
|
37
37
|
var _this = this;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
44
|
-
}
|
|
38
|
+
var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
39
|
+
return this.redis.decr(ttKey).then(function (count) {
|
|
40
|
+
if (count === 0)
|
|
41
|
+
return _this.redis.del(ttKey);
|
|
42
|
+
});
|
|
45
43
|
};
|
|
46
44
|
SplitsCacheInRedis.prototype._incrementCounts = function (split) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return this.redis.incr(ttKey);
|
|
50
|
-
}
|
|
45
|
+
var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
46
|
+
return this.redis.incr(ttKey);
|
|
51
47
|
};
|
|
52
48
|
/**
|
|
53
49
|
* Add a given split.
|
|
@@ -65,18 +61,19 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
|
|
|
65
61
|
newStringifiedSplit = JSON.stringify(split);
|
|
66
62
|
}
|
|
67
63
|
catch (e) {
|
|
68
|
-
throw new Error('Error parsing
|
|
64
|
+
throw new Error('Error parsing feature flag definition: ' + e);
|
|
69
65
|
}
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
66
|
+
return _this.redis.set(splitKey, newStringifiedSplit).then(function () {
|
|
67
|
+
// avoid unnecessary increment/decrement operations
|
|
68
|
+
if (parsedPreviousSplit && parsedPreviousSplit.trafficTypeName === split.trafficTypeName)
|
|
69
|
+
return;
|
|
70
|
+
// update traffic type counts
|
|
71
|
+
return _this._incrementCounts(split).then(function () {
|
|
72
|
+
if (parsedPreviousSplit)
|
|
73
|
+
return _this._decrementCounts(parsedPreviousSplit);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}).then(function () { return true; });
|
|
80
77
|
};
|
|
81
78
|
/**
|
|
82
79
|
* Add a list of splits.
|
|
@@ -226,7 +223,7 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
|
|
|
226
223
|
return Promise.resolve(splits);
|
|
227
224
|
})
|
|
228
225
|
.catch(function (e) {
|
|
229
|
-
_this.log.error(LOG_PREFIX + ("Could not grab
|
|
226
|
+
_this.log.error(LOG_PREFIX + ("Could not grab feature flags due to an error: " + e + "."));
|
|
230
227
|
return Promise.reject(e);
|
|
231
228
|
});
|
|
232
229
|
};
|
|
@@ -22,19 +22,15 @@ var SplitsCachePluggable = /** @class */ (function (_super) {
|
|
|
22
22
|
}
|
|
23
23
|
SplitsCachePluggable.prototype._decrementCounts = function (split) {
|
|
24
24
|
var _this = this;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
}
|
|
25
|
+
var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
26
|
+
return this.wrapper.decr(ttKey).then(function (count) {
|
|
27
|
+
if (count === 0)
|
|
28
|
+
return _this.wrapper.del(ttKey);
|
|
29
|
+
});
|
|
32
30
|
};
|
|
33
31
|
SplitsCachePluggable.prototype._incrementCounts = function (split) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return this.wrapper.incr(ttKey);
|
|
37
|
-
}
|
|
32
|
+
var ttKey = this.keys.buildTrafficTypeKey(split.trafficTypeName);
|
|
33
|
+
return this.wrapper.incr(ttKey);
|
|
38
34
|
};
|
|
39
35
|
/**
|
|
40
36
|
* Add a given split.
|
|
@@ -52,14 +48,18 @@ var SplitsCachePluggable = /** @class */ (function (_super) {
|
|
|
52
48
|
stringifiedNewSplit = JSON.stringify(split);
|
|
53
49
|
}
|
|
54
50
|
catch (e) {
|
|
55
|
-
throw new Error('Error parsing
|
|
51
|
+
throw new Error('Error parsing feature flag definition: ' + e);
|
|
56
52
|
}
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
return _this.wrapper.set(splitKey, stringifiedNewSplit).then(function () {
|
|
54
|
+
// avoid unnecessary increment/decrement operations
|
|
55
|
+
if (parsedPreviousSplit && parsedPreviousSplit.trafficTypeName === split.trafficTypeName)
|
|
56
|
+
return;
|
|
57
|
+
// update traffic type counts
|
|
58
|
+
return _this._incrementCounts(split).then(function () {
|
|
59
|
+
if (parsedPreviousSplit)
|
|
60
|
+
return _this._decrementCounts(parsedPreviousSplit);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
63
|
}).then(function () { return true; });
|
|
64
64
|
};
|
|
65
65
|
/**
|
|
@@ -81,7 +81,7 @@ export function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segment
|
|
|
81
81
|
// @TODO although factory status is destroyed, synchronization is not stopped
|
|
82
82
|
if (readiness)
|
|
83
83
|
readiness.destroy();
|
|
84
|
-
log.error(LOG_PREFIX_INSTANTIATION + ": you passed a client-side type authorizationKey, please grab an
|
|
84
|
+
log.error(LOG_PREFIX_INSTANTIATION + ": you passed a client-side type authorizationKey, please grab an SDK Key from the Split user interface that is of type server-side.");
|
|
85
85
|
}
|
|
86
86
|
else {
|
|
87
87
|
log.warn(LOG_PREFIX_SYNC_SEGMENTS + "Error while doing fetch of segments. " + error);
|
|
@@ -2,7 +2,6 @@ import { _Set, setToArray } from '../../../utils/lang/sets';
|
|
|
2
2
|
import { timeout } from '../../../utils/promise/timeout';
|
|
3
3
|
import { SDK_SPLITS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../../../readiness/constants';
|
|
4
4
|
import { SYNC_SPLITS_FETCH, SYNC_SPLITS_NEW, SYNC_SPLITS_REMOVED, SYNC_SPLITS_SEGMENTS, SYNC_SPLITS_FETCH_FAILS, SYNC_SPLITS_FETCH_RETRY } from '../../../logger/constants';
|
|
5
|
-
import { _Map } from '../../../utils/lang/maps';
|
|
6
5
|
// Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
|
|
7
6
|
// Returns a promise that could be rejected.
|
|
8
7
|
// @TODO review together with Segments and MySegments storage APIs
|
|
@@ -35,25 +34,19 @@ export function parseSegments(_a) {
|
|
|
35
34
|
* Exported for testing purposes.
|
|
36
35
|
*/
|
|
37
36
|
export function computeSplitsMutation(entries) {
|
|
38
|
-
var uniqueEntries = new _Map();
|
|
39
|
-
entries.forEach(function (split) {
|
|
40
|
-
var prevSplit = uniqueEntries.get(split.name);
|
|
41
|
-
if (!prevSplit || prevSplit.changeNumber < split.changeNumber)
|
|
42
|
-
uniqueEntries.set(split.name, split);
|
|
43
|
-
});
|
|
44
37
|
var segments = new _Set();
|
|
45
|
-
var computed =
|
|
46
|
-
uniqueEntries.forEach(function (split) {
|
|
38
|
+
var computed = entries.reduce(function (accum, split) {
|
|
47
39
|
if (split.status === 'ACTIVE') {
|
|
48
|
-
|
|
40
|
+
accum.added.push([split.name, split]);
|
|
49
41
|
parseSegments(split).forEach(function (segmentName) {
|
|
50
42
|
segments.add(segmentName);
|
|
51
43
|
});
|
|
52
44
|
}
|
|
53
45
|
else {
|
|
54
|
-
|
|
46
|
+
accum.removed.push(split.name);
|
|
55
47
|
}
|
|
56
|
-
|
|
48
|
+
return accum;
|
|
49
|
+
}, { added: [], removed: [], segments: [] });
|
|
57
50
|
computed.segments = setToArray(segments);
|
|
58
51
|
return computed;
|
|
59
52
|
}
|
|
@@ -110,7 +110,7 @@ export function pushManagerFactory(params, pollingManager) {
|
|
|
110
110
|
if (disconnected)
|
|
111
111
|
return;
|
|
112
112
|
log.error(ERROR_STREAMING_AUTH, [error.message]);
|
|
113
|
-
// Handle 4XX HTTP errors: 401 (invalid
|
|
113
|
+
// Handle 4XX HTTP errors: 401 (invalid SDK Key) or 400 (using incorrect SDK Key, i.e., client-side SDK Key on server-side)
|
|
114
114
|
if (error.statusCode >= 400 && error.statusCode < 500) {
|
|
115
115
|
telemetryTracker.streamingEvent(AUTH_REJECTION);
|
|
116
116
|
pushEmitter.emit(PUSH_NONRETRYABLE_ERROR);
|
|
@@ -25,8 +25,8 @@ function getActiveFactories() {
|
|
|
25
25
|
return Object.keys(usedKeysMap).length;
|
|
26
26
|
}
|
|
27
27
|
function getRedundantActiveFactories() {
|
|
28
|
-
return Object.keys(usedKeysMap).reduce(function (acum,
|
|
29
|
-
return acum + usedKeysMap[
|
|
28
|
+
return Object.keys(usedKeysMap).reduce(function (acum, sdkKey) {
|
|
29
|
+
return acum + usedKeysMap[sdkKey] - 1;
|
|
30
30
|
}, 0);
|
|
31
31
|
}
|
|
32
32
|
export function getTelemetryConfigStats(mode, storageType) {
|
|
@@ -1,47 +1,48 @@
|
|
|
1
|
-
import { ERROR_NULL, ERROR_EMPTY, ERROR_INVALID,
|
|
1
|
+
import { ERROR_NULL, ERROR_EMPTY, ERROR_INVALID, WARN_SDK_KEY, LOG_PREFIX_INSTANTIATION } from '../../logger/constants';
|
|
2
2
|
import { isString } from '../lang';
|
|
3
|
-
var item = '
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
var item = 'sdk_key';
|
|
4
|
+
// @TODO replace ApiKey with SdkKey in function names
|
|
5
|
+
/** validates the given SDK key */
|
|
6
|
+
export function validateApiKey(log, maybeSdkKey) {
|
|
7
|
+
var sdkKey = false;
|
|
8
|
+
if (maybeSdkKey == undefined) { // eslint-disable-line eqeqeq
|
|
8
9
|
log.error(ERROR_NULL, [LOG_PREFIX_INSTANTIATION, item]);
|
|
9
10
|
}
|
|
10
|
-
else if (isString(
|
|
11
|
-
if (
|
|
12
|
-
|
|
11
|
+
else if (isString(maybeSdkKey)) {
|
|
12
|
+
if (maybeSdkKey.length > 0)
|
|
13
|
+
sdkKey = maybeSdkKey;
|
|
13
14
|
else
|
|
14
15
|
log.error(ERROR_EMPTY, [LOG_PREFIX_INSTANTIATION, item]);
|
|
15
16
|
}
|
|
16
17
|
else {
|
|
17
18
|
log.error(ERROR_INVALID, [LOG_PREFIX_INSTANTIATION, item]);
|
|
18
19
|
}
|
|
19
|
-
return
|
|
20
|
+
return sdkKey;
|
|
20
21
|
}
|
|
21
22
|
// Exported for telemetry
|
|
22
23
|
export var usedKeysMap = {};
|
|
23
|
-
/** validates the given
|
|
24
|
-
export function validateAndTrackApiKey(log,
|
|
25
|
-
var
|
|
26
|
-
// If
|
|
27
|
-
if (
|
|
28
|
-
if (!usedKeysMap[
|
|
24
|
+
/** validates the given SDK key and also warns if it is in use */
|
|
25
|
+
export function validateAndTrackApiKey(log, maybeSdkKey) {
|
|
26
|
+
var sdkKey = validateApiKey(log, maybeSdkKey);
|
|
27
|
+
// If sdkKey is correct, we'll save it as the instance creation should work.
|
|
28
|
+
if (sdkKey) {
|
|
29
|
+
if (!usedKeysMap[sdkKey]) {
|
|
29
30
|
// If this key is not present, only warning scenarios is that we have factories for other keys.
|
|
30
|
-
usedKeysMap[
|
|
31
|
+
usedKeysMap[sdkKey] = 1;
|
|
31
32
|
if (Object.keys(usedKeysMap).length > 1) {
|
|
32
|
-
log.warn(
|
|
33
|
+
log.warn(WARN_SDK_KEY, ['an instance of the Split factory']);
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
else {
|
|
36
|
-
log.warn(
|
|
37
|
-
usedKeysMap[
|
|
37
|
+
log.warn(WARN_SDK_KEY, [usedKeysMap[sdkKey] + " factory/ies with this SDK Key"]);
|
|
38
|
+
usedKeysMap[sdkKey]++;
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
|
-
return
|
|
41
|
+
return sdkKey;
|
|
41
42
|
}
|
|
42
|
-
export function releaseApiKey(
|
|
43
|
-
if (usedKeysMap[
|
|
44
|
-
usedKeysMap[
|
|
45
|
-
if (usedKeysMap[
|
|
46
|
-
delete usedKeysMap[
|
|
43
|
+
export function releaseApiKey(sdkKey) {
|
|
44
|
+
if (usedKeysMap[sdkKey])
|
|
45
|
+
usedKeysMap[sdkKey]--;
|
|
46
|
+
if (usedKeysMap[sdkKey] === 0)
|
|
47
|
+
delete usedKeysMap[sdkKey];
|
|
47
48
|
}
|
|
@@ -10,12 +10,12 @@ function validateSplitsData(log, maybeSplitsData, method) {
|
|
|
10
10
|
if (isObject(maybeSplitsData)) {
|
|
11
11
|
var splitNames = Object.keys(maybeSplitsData);
|
|
12
12
|
if (splitNames.length === 0)
|
|
13
|
-
log.warn(method + ": preloadedData.splitsData doesn't contain
|
|
13
|
+
log.warn(method + ": preloadedData.splitsData doesn't contain feature flag definitions.");
|
|
14
14
|
// @TODO in the future, consider handling the possibility of having parsed definitions of splits
|
|
15
15
|
if (splitNames.every(function (splitName) { return validateSplit(log, splitName, method) && isString(maybeSplitsData[splitName]); }))
|
|
16
16
|
return true;
|
|
17
17
|
}
|
|
18
|
-
log.error(method + ": preloadedData.splitsData must be a map of
|
|
18
|
+
log.error(method + ": preloadedData.splitsData must be a map of feature flag names to their stringified definitions.");
|
|
19
19
|
return false;
|
|
20
20
|
}
|
|
21
21
|
function validateMySegmentsData(log, maybeMySegmentsData, method) {
|
|
@@ -37,7 +37,7 @@ function validateSegmentsData(log, maybeSegmentsData, method) {
|
|
|
37
37
|
if (segmentNames.every(function (segmentName) { return isString(maybeSegmentsData[segmentName]); }))
|
|
38
38
|
return true;
|
|
39
39
|
}
|
|
40
|
-
log.error(method + ": preloadedData.segmentsData must be a map of segment names to their
|
|
40
|
+
log.error(method + ": preloadedData.segmentsData must be a map of segment names to their stringified definitions.");
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
43
|
export function validatePreloadedData(log, maybePreloadedData, method) {
|
|
@@ -3,7 +3,7 @@ import { isString } from '../lang';
|
|
|
3
3
|
// include BOM and nbsp
|
|
4
4
|
var TRIMMABLE_SPACES_REGEX = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/;
|
|
5
5
|
export function validateSplit(log, maybeSplit, method, item) {
|
|
6
|
-
if (item === void 0) { item = '
|
|
6
|
+
if (item === void 0) { item = 'feature flag name'; }
|
|
7
7
|
if (maybeSplit == undefined) { // eslint-disable-line eqeqeq
|
|
8
8
|
log.error(ERROR_NULL, [method, item]);
|
|
9
9
|
}
|
|
@@ -2,8 +2,8 @@ import { ERROR_EMPTY_ARRAY } from '../../logger/constants';
|
|
|
2
2
|
import { uniq } from '../lang';
|
|
3
3
|
import { validateSplit } from './split';
|
|
4
4
|
export function validateSplits(log, maybeSplits, method, listName, item) {
|
|
5
|
-
if (listName === void 0) { listName = '
|
|
6
|
-
if (item === void 0) { item = '
|
|
5
|
+
if (listName === void 0) { listName = 'feature flag names'; }
|
|
6
|
+
if (item === void 0) { item = 'feature flag name'; }
|
|
7
7
|
if (Array.isArray(maybeSplits) && maybeSplits.length > 0) {
|
|
8
8
|
var validatedArray_1 = [];
|
|
9
9
|
// Remove invalid values
|
|
@@ -81,7 +81,7 @@ function fromSecondsToMillis(n) {
|
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
83
|
* Validates the given config and use it to build a settings object.
|
|
84
|
-
* NOTE: it doesn't validate the
|
|
84
|
+
* NOTE: it doesn't validate the SDK Key. Call `validateApiKey` or `validateAndTrackApiKey` for that after settings validation.
|
|
85
85
|
*
|
|
86
86
|
* @param config user defined configuration
|
|
87
87
|
* @param validationParams defaults and fields validators used to validate and creates a settings object from a given config
|
package/package.json
CHANGED
package/src/logger/constants.ts
CHANGED
|
@@ -94,7 +94,7 @@ export const WARN_INTEGRATION_INVALID = 218;
|
|
|
94
94
|
export const WARN_SPLITS_FILTER_IGNORED = 219;
|
|
95
95
|
export const WARN_SPLITS_FILTER_INVALID = 220;
|
|
96
96
|
export const WARN_SPLITS_FILTER_EMPTY = 221;
|
|
97
|
-
export const
|
|
97
|
+
export const WARN_SDK_KEY = 222;
|
|
98
98
|
export const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 223;
|
|
99
99
|
|
|
100
100
|
export const ERROR_ENGINE_COMBINER_IFELSEIF = 300;
|
|
@@ -136,7 +136,7 @@ export const LOG_PREFIX_SYNC = 'sync';
|
|
|
136
136
|
export const LOG_PREFIX_SYNC_MANAGER = LOG_PREFIX_SYNC + ':sync-manager: ';
|
|
137
137
|
export const LOG_PREFIX_SYNC_OFFLINE = LOG_PREFIX_SYNC + ':offline: ';
|
|
138
138
|
export const LOG_PREFIX_SYNC_STREAMING = LOG_PREFIX_SYNC + ':streaming: ';
|
|
139
|
-
export const LOG_PREFIX_SYNC_SPLITS = LOG_PREFIX_SYNC + ':
|
|
139
|
+
export const LOG_PREFIX_SYNC_SPLITS = LOG_PREFIX_SYNC + ':featureflag-changes: ';
|
|
140
140
|
export const LOG_PREFIX_SYNC_SEGMENTS = LOG_PREFIX_SYNC + ':segment-changes: ';
|
|
141
141
|
export const LOG_PREFIX_SYNC_MYSEGMENTS = LOG_PREFIX_SYNC + ':my-segments: ';
|
|
142
142
|
export const LOG_PREFIX_SYNC_POLLING = LOG_PREFIX_SYNC + ':polling-manager: ';
|
|
@@ -13,8 +13,8 @@ export const codesDebug: [number, string][] = codesInfo.concat([
|
|
|
13
13
|
[c.ENGINE_MATCHER_CONTAINS_ALL, c.LOG_PREFIX_ENGINE_MATCHER + '[containsAllMatcher] %s contains all elements of %s? %s'],
|
|
14
14
|
[c.ENGINE_MATCHER_CONTAINS_ANY, c.LOG_PREFIX_ENGINE_MATCHER + '[containsAnyMatcher] %s contains at least an element of %s? %s'],
|
|
15
15
|
[c.ENGINE_MATCHER_CONTAINS_STRING, c.LOG_PREFIX_ENGINE_MATCHER + '[containsStringMatcher] %s contains %s? %s'],
|
|
16
|
-
[c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[dependencyMatcher] parent
|
|
17
|
-
[c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[dependencyMatcher] will evaluate parent
|
|
16
|
+
[c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[dependencyMatcher] parent feature flag "%s" evaluated to "%s" with label "%s". %s evaluated treatment is part of [%s] ? %s.'],
|
|
17
|
+
[c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[dependencyMatcher] will evaluate parent feature flag: "%s" with key: %s %s'],
|
|
18
18
|
[c.ENGINE_MATCHER_EQUAL, c.LOG_PREFIX_ENGINE_MATCHER + '[equalToMatcher] is %s equal to %s? %s'],
|
|
19
19
|
[c.ENGINE_MATCHER_EQUAL_TO_SET, c.LOG_PREFIX_ENGINE_MATCHER + '[equalToSetMatcher] is %s equal to set %s? %s'],
|
|
20
20
|
[c.ENGINE_MATCHER_ENDS_WITH, c.LOG_PREFIX_ENGINE_MATCHER + '[endsWithMatcher] %s ends with %s? %s'],
|
|
@@ -35,15 +35,15 @@ export const codesDebug: [number, string][] = codesInfo.concat([
|
|
|
35
35
|
[c.RETRIEVE_CLIENT_EXISTING, 'Retrieving existing SDK client.'],
|
|
36
36
|
[c.RETRIEVE_MANAGER, 'Retrieving manager instance.'],
|
|
37
37
|
// synchronizer
|
|
38
|
-
[c.SYNC_OFFLINE_DATA, c.LOG_PREFIX_SYNC_OFFLINE + '
|
|
39
|
-
[c.SYNC_SPLITS_FETCH, c.LOG_PREFIX_SYNC_SPLITS + 'Spin up
|
|
40
|
-
[c.SYNC_SPLITS_NEW, c.LOG_PREFIX_SYNC_SPLITS + 'New
|
|
41
|
-
[c.SYNC_SPLITS_REMOVED, c.LOG_PREFIX_SYNC_SPLITS + 'Removed
|
|
38
|
+
[c.SYNC_OFFLINE_DATA, c.LOG_PREFIX_SYNC_OFFLINE + 'Feature flags data: \n%s'],
|
|
39
|
+
[c.SYNC_SPLITS_FETCH, c.LOG_PREFIX_SYNC_SPLITS + 'Spin up feature flags update using since = %s'],
|
|
40
|
+
[c.SYNC_SPLITS_NEW, c.LOG_PREFIX_SYNC_SPLITS + 'New feature flags %s'],
|
|
41
|
+
[c.SYNC_SPLITS_REMOVED, c.LOG_PREFIX_SYNC_SPLITS + 'Removed feature flags %s'],
|
|
42
42
|
[c.SYNC_SPLITS_SEGMENTS, c.LOG_PREFIX_SYNC_SPLITS + 'Segment names collected %s'],
|
|
43
43
|
[c.STREAMING_NEW_MESSAGE, c.LOG_PREFIX_SYNC_STREAMING + 'New SSE message received, with data: %s.'],
|
|
44
44
|
[c.SYNC_TASK_START, c.LOG_PREFIX_SYNC + ': Starting %s. Running each %s millis'],
|
|
45
45
|
[c.SYNC_TASK_EXECUTE, c.LOG_PREFIX_SYNC + ': Running %s'],
|
|
46
46
|
[c.SYNC_TASK_STOP, c.LOG_PREFIX_SYNC + ': Stopping %s'],
|
|
47
47
|
// initialization / settings validation
|
|
48
|
-
[c.SETTINGS_SPLITS_FILTER, c.LOG_PREFIX_SETTINGS + ':
|
|
48
|
+
[c.SETTINGS_SPLITS_FILTER, c.LOG_PREFIX_SETTINGS + ': feature flags filtering criteria is "%s".']
|
|
49
49
|
]);
|