@splitsoftware/splitio-commons 1.12.1-rc.1 → 1.12.1-rc.3
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 +11 -9
- package/cjs/sdkClient/client.js +4 -4
- package/cjs/sdkFactory/index.js +1 -1
- package/cjs/sdkManager/index.js +9 -6
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +1 -3
- package/cjs/storages/inRedis/RedisAdapter.js +32 -13
- package/cjs/storages/inRedis/SegmentsCacheInRedis.js +2 -2
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +15 -15
- package/cjs/storages/pluggable/SplitsCachePluggable.js +5 -5
- package/cjs/utils/inputValidation/index.js +5 -5
- package/cjs/utils/inputValidation/{splitExistance.js → splitExistence.js} +3 -3
- package/cjs/utils/inputValidation/{trafficTypeExistance.js → trafficTypeExistence.js} +6 -6
- package/esm/sdkClient/client.js +4 -4
- package/esm/sdkFactory/index.js +1 -1
- package/esm/sdkManager/index.js +10 -7
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +1 -3
- package/esm/storages/inRedis/RedisAdapter.js +32 -13
- package/esm/storages/inRedis/SegmentsCacheInRedis.js +2 -2
- package/esm/storages/inRedis/SplitsCacheInRedis.js +15 -15
- package/esm/storages/pluggable/SplitsCachePluggable.js +5 -5
- package/esm/utils/inputValidation/index.js +2 -2
- package/esm/utils/inputValidation/{splitExistance.js → splitExistence.js} +1 -1
- package/esm/utils/inputValidation/{trafficTypeExistance.js → trafficTypeExistence.js} +4 -4
- package/package.json +1 -1
- package/src/sdkClient/client.ts +4 -4
- package/src/sdkFactory/index.ts +1 -1
- package/src/sdkFactory/types.ts +3 -7
- package/src/sdkManager/index.ts +13 -10
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +4 -5
- package/src/storages/inRedis/EventsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/ImpressionsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/RedisAdapter.ts +38 -16
- package/src/storages/inRedis/SegmentsCacheInRedis.ts +5 -5
- package/src/storages/inRedis/SplitsCacheInRedis.ts +18 -19
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +2 -2
- package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +3 -3
- package/src/storages/pluggable/SplitsCachePluggable.ts +5 -5
- package/src/trackers/impressionObserver/utils.ts +1 -1
- package/src/utils/inputValidation/index.ts +2 -2
- package/src/utils/inputValidation/{splitExistance.ts → splitExistence.ts} +1 -1
- package/src/utils/inputValidation/{trafficTypeExistance.ts → trafficTypeExistence.ts} +4 -4
- package/src/utils/redis/RedisMock.ts +1 -3
- package/types/sdkFactory/types.d.ts +3 -3
- package/types/sdkManager/index.d.ts +2 -3
- package/types/storages/inRedis/EventsCacheInRedis.d.ts +2 -2
- package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +3 -2
- package/types/storages/inRedis/ImpressionsCacheInRedis.d.ts +2 -2
- package/types/storages/inRedis/RedisAdapter.d.ts +1 -1
- package/types/storages/inRedis/SegmentsCacheInRedis.d.ts +3 -3
- package/types/storages/inRedis/SplitsCacheInRedis.d.ts +6 -11
- package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +2 -2
- package/types/storages/inRedis/UniqueKeysCacheInRedis.d.ts +3 -2
- package/types/storages/pluggable/SplitsCachePluggable.d.ts +4 -4
- package/types/trackers/impressionObserver/utils.d.ts +1 -1
- package/types/utils/inputValidation/index.d.ts +2 -2
- package/types/utils/inputValidation/splitExistence.d.ts +7 -0
- package/types/utils/inputValidation/trafficTypeExistence.d.ts +9 -0
package/CHANGES.txt
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
1.12.0 (December XX, 2023)
|
|
2
2
|
- Added support for Flag Sets in "consumer" and "partial consumer" modes for Pluggable and Redis storages.
|
|
3
3
|
- Updated evaluation flow to log a warning when using flag sets that don't contain cached feature flags.
|
|
4
|
+
- Updated Redis adapter to handle timeouts and queueing of some missing commands: 'hincrby', 'popNRaw', and 'pipeline.exec'.
|
|
5
|
+
- Bugfixing - Fixed manager methods in consumer modes to return results in a promise when the SDK is not operational (not ready or destroyed).
|
|
4
6
|
|
|
5
7
|
1.11.0 (November 3, 2023)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
- Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation):
|
|
9
|
+
- Added new variations of the get treatment methods to support evaluating flags in given flag set/s.
|
|
10
|
+
- getTreatmentsByFlagSet and getTreatmentsByFlagSets
|
|
11
|
+
- getTreatmentsWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets
|
|
12
|
+
- Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload.
|
|
13
|
+
- Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init.
|
|
14
|
+
- Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views.
|
|
15
|
+
- Bugfixing - Fixed SDK key validation in NodeJS to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768).
|
|
14
16
|
|
|
15
17
|
1.10.0 (October 20, 2023)
|
|
16
18
|
- Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225).
|
|
@@ -57,7 +59,7 @@
|
|
|
57
59
|
- Added a new impressions mode for the SDK called NONE, to be used in factory when there is no desire to capture impressions on an SDK factory to feed Split's analytics engine. Running NONE mode, the SDK will only capture unique keys evaluated for a particular feature flag instead of full blown impressions.
|
|
58
60
|
- Updated SDK telemetry to support pluggable storage, partial consumer mode, and synchronizer.
|
|
59
61
|
- Updated storage implementations to improve the performance of feature flag evaluations (i.e., `getTreatment(s)` method calls) when using the default storage in memory.
|
|
60
|
-
- Updated evaluation flow to avoid
|
|
62
|
+
- Updated evaluation flow (i.e., `getTreatment(s)` method calls) to avoid calling the storage for cached feature flags when the SDK is not ready or ready from cache. It applies to all SDK modes.
|
|
61
63
|
|
|
62
64
|
1.6.1 (July 22, 2022)
|
|
63
65
|
- Updated GoogleAnalyticsToSplit integration to validate `autoRequire` config parameter and avoid some wrong warning logs when mapping GA hit fields to Split event properties.
|
package/cjs/sdkClient/client.js
CHANGED
|
@@ -4,8 +4,8 @@ exports.clientFactory = void 0;
|
|
|
4
4
|
var evaluator_1 = require("../evaluator");
|
|
5
5
|
var thenable_1 = require("../utils/promise/thenable");
|
|
6
6
|
var key_1 = require("../utils/key");
|
|
7
|
-
var
|
|
8
|
-
var
|
|
7
|
+
var splitExistence_1 = require("../utils/inputValidation/splitExistence");
|
|
8
|
+
var trafficTypeExistence_1 = require("../utils/inputValidation/trafficTypeExistence");
|
|
9
9
|
var labels_1 = require("../utils/labels");
|
|
10
10
|
var constants_1 = require("../utils/constants");
|
|
11
11
|
var constants_2 = require("../logger/constants");
|
|
@@ -102,7 +102,7 @@ function clientFactory(params) {
|
|
|
102
102
|
var bucketingKey = (0, key_1.getBucketing)(key);
|
|
103
103
|
var treatment = evaluation.treatment, label = evaluation.label, changeNumber = evaluation.changeNumber, _a = evaluation.config, config = _a === void 0 ? null : _a;
|
|
104
104
|
log.info(constants_2.IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
|
|
105
|
-
if ((0,
|
|
105
|
+
if ((0, splitExistence_1.validateSplitExistence)(log, readinessManager, featureFlagName, label, invokingMethodName)) {
|
|
106
106
|
log.info(constants_2.IMPRESSION_QUEUEING);
|
|
107
107
|
queue.push({
|
|
108
108
|
feature: featureFlagName,
|
|
@@ -136,7 +136,7 @@ function clientFactory(params) {
|
|
|
136
136
|
properties: properties
|
|
137
137
|
};
|
|
138
138
|
// This may be async but we only warn, we don't actually care if it is valid or not in terms of queueing the event.
|
|
139
|
-
(0,
|
|
139
|
+
(0, trafficTypeExistence_1.validateTrafficTypeExistence)(log, readinessManager, storage.splits, mode, trafficTypeName, 'track');
|
|
140
140
|
var result = eventTracker.track(eventData, size);
|
|
141
141
|
if ((0, thenable_1.thenable)(result)) {
|
|
142
142
|
return result.then(function (result) {
|
package/cjs/sdkFactory/index.js
CHANGED
|
@@ -66,7 +66,7 @@ function sdkFactory(params) {
|
|
|
66
66
|
ctx.signalListener = signalListener;
|
|
67
67
|
// SDK client and manager
|
|
68
68
|
var clientMethod = sdkClientMethodFactory(ctx);
|
|
69
|
-
var managerInstance = sdkManagerFactory(
|
|
69
|
+
var managerInstance = sdkManagerFactory(settings, storage.splits, sdkReadinessManager);
|
|
70
70
|
syncManager && syncManager.start();
|
|
71
71
|
signalListener && signalListener.start();
|
|
72
72
|
log.info(constants_1.NEW_FACTORY);
|
package/cjs/sdkManager/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var objectAssign_1 = require("../utils/lang/objectAssign");
|
|
|
5
5
|
var thenable_1 = require("../utils/promise/thenable");
|
|
6
6
|
var lang_1 = require("../utils/lang");
|
|
7
7
|
var inputValidation_1 = require("../utils/inputValidation");
|
|
8
|
+
var utils_1 = require("../trackers/impressionObserver/utils");
|
|
8
9
|
var SPLIT_FN_LABEL = 'split';
|
|
9
10
|
var SPLITS_FN_LABEL = 'splits';
|
|
10
11
|
var NAMES_FN_LABEL = 'names';
|
|
@@ -41,8 +42,10 @@ function objectsToViews(splitObjects) {
|
|
|
41
42
|
});
|
|
42
43
|
return views;
|
|
43
44
|
}
|
|
44
|
-
function sdkManagerFactory(
|
|
45
|
+
function sdkManagerFactory(settings, splits, _a) {
|
|
45
46
|
var readinessManager = _a.readinessManager, sdkStatus = _a.sdkStatus;
|
|
47
|
+
var log = settings.log;
|
|
48
|
+
var isSync = (0, utils_1.isStorageSync)(settings);
|
|
46
49
|
return (0, objectAssign_1.objectAssign)(
|
|
47
50
|
// Proto-linkage of the readiness Event Emitter
|
|
48
51
|
Object.create(sdkStatus), {
|
|
@@ -52,16 +55,16 @@ function sdkManagerFactory(log, splits, _a) {
|
|
|
52
55
|
split: function (featureFlagName) {
|
|
53
56
|
var splitName = (0, inputValidation_1.validateSplit)(log, featureFlagName, SPLIT_FN_LABEL);
|
|
54
57
|
if (!(0, inputValidation_1.validateIfNotDestroyed)(log, readinessManager, SPLIT_FN_LABEL) || !(0, inputValidation_1.validateIfOperational)(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
|
|
55
|
-
return null;
|
|
58
|
+
return isSync ? null : Promise.resolve(null);
|
|
56
59
|
}
|
|
57
60
|
var split = splits.getSplit(splitName);
|
|
58
61
|
if ((0, thenable_1.thenable)(split)) {
|
|
59
62
|
return split.catch(function () { return null; }).then(function (result) {
|
|
60
|
-
(0, inputValidation_1.
|
|
63
|
+
(0, inputValidation_1.validateSplitExistence)(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
|
|
61
64
|
return objectToView(result);
|
|
62
65
|
});
|
|
63
66
|
}
|
|
64
|
-
(0, inputValidation_1.
|
|
67
|
+
(0, inputValidation_1.validateSplitExistence)(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
|
|
65
68
|
return objectToView(split);
|
|
66
69
|
},
|
|
67
70
|
/**
|
|
@@ -69,7 +72,7 @@ function sdkManagerFactory(log, splits, _a) {
|
|
|
69
72
|
*/
|
|
70
73
|
splits: function () {
|
|
71
74
|
if (!(0, inputValidation_1.validateIfNotDestroyed)(log, readinessManager, SPLITS_FN_LABEL) || !(0, inputValidation_1.validateIfOperational)(log, readinessManager, SPLITS_FN_LABEL)) {
|
|
72
|
-
return [];
|
|
75
|
+
return isSync ? [] : Promise.resolve([]);
|
|
73
76
|
}
|
|
74
77
|
var currentSplits = splits.getAll();
|
|
75
78
|
return (0, thenable_1.thenable)(currentSplits) ?
|
|
@@ -81,7 +84,7 @@ function sdkManagerFactory(log, splits, _a) {
|
|
|
81
84
|
*/
|
|
82
85
|
names: function () {
|
|
83
86
|
if (!(0, inputValidation_1.validateIfNotDestroyed)(log, readinessManager, NAMES_FN_LABEL) || !(0, inputValidation_1.validateIfOperational)(log, readinessManager, NAMES_FN_LABEL)) {
|
|
84
|
-
return [];
|
|
87
|
+
return isSync ? [] : Promise.resolve([]);
|
|
85
88
|
}
|
|
86
89
|
var splitNames = splits.getSplitNames();
|
|
87
90
|
return (0, thenable_1.thenable)(splitNames) ?
|
|
@@ -246,9 +246,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
246
246
|
return;
|
|
247
247
|
var flagSetKey = _this.keys.buildFlagSetKey(featureFlagSet);
|
|
248
248
|
var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
|
|
249
|
-
|
|
250
|
-
flagSetFromLocalStorage = '[]';
|
|
251
|
-
var flagSetCache = new sets_1._Set(JSON.parse(flagSetFromLocalStorage));
|
|
249
|
+
var flagSetCache = new sets_1._Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []);
|
|
252
250
|
flagSetCache.add(featureFlag.name);
|
|
253
251
|
localStorage.setItem(flagSetKey, JSON.stringify((0, sets_1.setToArray)(flagSetCache)));
|
|
254
252
|
});
|
|
@@ -9,7 +9,8 @@ var thenable_1 = require("../../utils/promise/thenable");
|
|
|
9
9
|
var timeout_1 = require("../../utils/promise/timeout");
|
|
10
10
|
var LOG_PREFIX = 'storage:redis-adapter: ';
|
|
11
11
|
// If we ever decide to fully wrap every method, there's a Commander.getBuiltinCommands from ioredis.
|
|
12
|
-
var METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', '
|
|
12
|
+
var METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', 'expire', 'mget', 'lrange', 'ltrim', 'hset', 'hincrby', 'popNRaw'];
|
|
13
|
+
var METHODS_TO_PROMISE_WRAP_EXEC = ['pipeline'];
|
|
13
14
|
// Not part of the settings since it'll vary on each storage. We should be removing storage specific logic from elsewhere.
|
|
14
15
|
var DEFAULT_OPTIONS = {
|
|
15
16
|
connectionTimeout: 10000,
|
|
@@ -27,6 +28,7 @@ var DEFAULT_LIBRARY_OPTIONS = {
|
|
|
27
28
|
var RedisAdapter = /** @class */ (function (_super) {
|
|
28
29
|
(0, tslib_1.__extends)(RedisAdapter, _super);
|
|
29
30
|
function RedisAdapter(log, storageSettings) {
|
|
31
|
+
if (storageSettings === void 0) { storageSettings = {}; }
|
|
30
32
|
var _this = this;
|
|
31
33
|
var options = RedisAdapter._defineOptions(storageSettings);
|
|
32
34
|
// Call the ioredis constructor
|
|
@@ -58,14 +60,15 @@ var RedisAdapter = /** @class */ (function (_super) {
|
|
|
58
60
|
};
|
|
59
61
|
RedisAdapter.prototype._setTimeoutWrappers = function () {
|
|
60
62
|
var instance = this;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
instance
|
|
63
|
+
var wrapCommand = function (originalMethod, methodName) {
|
|
64
|
+
// The value of "this" in this function should be the instance actually executing the method. It might be the instance referred (the base one)
|
|
65
|
+
// or it can be the instance of a Pipeline object.
|
|
66
|
+
return function () {
|
|
64
67
|
var params = arguments;
|
|
68
|
+
var caller = this;
|
|
65
69
|
function commandWrapper() {
|
|
66
|
-
instance.log.debug(LOG_PREFIX +
|
|
67
|
-
|
|
68
|
-
var result = originalMethod.apply(instance, params);
|
|
70
|
+
instance.log.debug(LOG_PREFIX + "Executing " + methodName + ".");
|
|
71
|
+
var result = originalMethod.apply(caller, params);
|
|
69
72
|
if ((0, thenable_1.thenable)(result)) {
|
|
70
73
|
// For handling pending commands on disconnect, add to the set and remove once finished.
|
|
71
74
|
// On sync commands there's no need, only thenables.
|
|
@@ -76,7 +79,7 @@ var RedisAdapter = /** @class */ (function (_super) {
|
|
|
76
79
|
// Both success and error remove from queue.
|
|
77
80
|
result.then(cleanUpRunningCommandsCb, cleanUpRunningCommandsCb);
|
|
78
81
|
return (0, timeout_1.timeout)(instance._options.operationTimeout, result).catch(function (err) {
|
|
79
|
-
instance.log.error(LOG_PREFIX +
|
|
82
|
+
instance.log.error("" + LOG_PREFIX + methodName + " operation threw an error or exceeded configured timeout of " + instance._options.operationTimeout + "ms. Message: " + err);
|
|
80
83
|
// Handling is not the adapter responsibility.
|
|
81
84
|
throw err;
|
|
82
85
|
});
|
|
@@ -84,12 +87,12 @@ var RedisAdapter = /** @class */ (function (_super) {
|
|
|
84
87
|
return result;
|
|
85
88
|
}
|
|
86
89
|
if (instance._notReadyCommandsQueue) {
|
|
87
|
-
return new Promise(function (
|
|
90
|
+
return new Promise(function (resolve, reject) {
|
|
88
91
|
instance._notReadyCommandsQueue.unshift({
|
|
89
|
-
resolve:
|
|
90
|
-
reject:
|
|
92
|
+
resolve: resolve,
|
|
93
|
+
reject: reject,
|
|
91
94
|
command: commandWrapper,
|
|
92
|
-
name:
|
|
95
|
+
name: methodName.toUpperCase()
|
|
93
96
|
});
|
|
94
97
|
});
|
|
95
98
|
}
|
|
@@ -97,6 +100,22 @@ var RedisAdapter = /** @class */ (function (_super) {
|
|
|
97
100
|
return commandWrapper();
|
|
98
101
|
}
|
|
99
102
|
};
|
|
103
|
+
};
|
|
104
|
+
// Wrap regular async methods to track timeouts and queue when Redis is not yet executing commands.
|
|
105
|
+
METHODS_TO_PROMISE_WRAP.forEach(function (methodName) {
|
|
106
|
+
var originalFn = instance[methodName];
|
|
107
|
+
instance[methodName] = wrapCommand(originalFn, methodName);
|
|
108
|
+
});
|
|
109
|
+
// Special handling for pipeline~like methods. We need to wrap the async trigger, which is exec, but return the Pipeline right away.
|
|
110
|
+
METHODS_TO_PROMISE_WRAP_EXEC.forEach(function (methodName) {
|
|
111
|
+
var originalFn = instance[methodName];
|
|
112
|
+
// "First level wrapper" to handle the sync execution and wrap async, queueing later if applicable.
|
|
113
|
+
instance[methodName] = function () {
|
|
114
|
+
var res = originalFn.apply(instance, arguments);
|
|
115
|
+
var originalExec = res.exec;
|
|
116
|
+
res.exec = wrapCommand(originalExec, methodName + '.exec').bind(res);
|
|
117
|
+
return res;
|
|
118
|
+
};
|
|
100
119
|
});
|
|
101
120
|
};
|
|
102
121
|
RedisAdapter.prototype._setDisconnectWrapper = function () {
|
|
@@ -107,7 +126,7 @@ var RedisAdapter = /** @class */ (function (_super) {
|
|
|
107
126
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
108
127
|
params[_i] = arguments[_i];
|
|
109
128
|
}
|
|
110
|
-
setTimeout(function
|
|
129
|
+
setTimeout(function deferredDisconnect() {
|
|
111
130
|
if (instance._runningCommands.size > 0) {
|
|
112
131
|
instance.log.info(LOG_PREFIX + ("Attempting to disconnect but there are " + instance._runningCommands.size + " commands still waiting for resolution. Defering disconnection until those finish."));
|
|
113
132
|
Promise.all((0, sets_1.setToArray)(instance._runningCommands))
|
|
@@ -54,9 +54,9 @@ var SegmentsCacheInRedis = /** @class */ (function () {
|
|
|
54
54
|
SegmentsCacheInRedis.prototype.getRegisteredSegments = function () {
|
|
55
55
|
return this.redis.smembers(this.keys.buildRegisteredSegmentsKey());
|
|
56
56
|
};
|
|
57
|
-
// @TODO remove
|
|
57
|
+
// @TODO remove or implement. It is not being used.
|
|
58
58
|
SegmentsCacheInRedis.prototype.clear = function () {
|
|
59
|
-
return
|
|
59
|
+
return Promise.resolve();
|
|
60
60
|
};
|
|
61
61
|
return SegmentsCacheInRedis;
|
|
62
62
|
}());
|
|
@@ -187,16 +187,20 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
|
|
|
187
187
|
return this.redis.keys(this.keys.searchPatternForSplitKeys()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
|
|
188
188
|
};
|
|
189
189
|
/**
|
|
190
|
-
* Get list of
|
|
191
|
-
* The returned promise is resolved with the list of
|
|
192
|
-
* or rejected if
|
|
190
|
+
* Get list of feature flag names related to a given list of flag set names.
|
|
191
|
+
* The returned promise is resolved with the list of feature flag names per flag set,
|
|
192
|
+
* or rejected if the pipelined redis operation fails (e.g., timeout).
|
|
193
193
|
*/
|
|
194
194
|
SplitsCacheInRedis.prototype.getNamesByFlagSets = function (flagSets) {
|
|
195
195
|
var _this = this;
|
|
196
|
-
return
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
196
|
+
return this.redis.pipeline(flagSets.map(function (flagSet) { return ['smembers', _this.keys.buildFlagSetKey(flagSet)]; })).exec()
|
|
197
|
+
.then(function (results) { return results.map(function (_a, index) {
|
|
198
|
+
var e = _a[0], value = _a[1];
|
|
199
|
+
if (e === null)
|
|
200
|
+
return value;
|
|
201
|
+
_this.log.error(constants_1.LOG_PREFIX + ("Could not read result from get members of flag set " + flagSets[index] + " due to an error: " + e));
|
|
202
|
+
}); })
|
|
203
|
+
.then(function (namesByFlagSets) { return namesByFlagSets.map(function (namesByFlagSet) { return new sets_1._Set(namesByFlagSet); }); });
|
|
200
204
|
};
|
|
201
205
|
/**
|
|
202
206
|
* Check traffic type existence.
|
|
@@ -213,24 +217,20 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
|
|
|
213
217
|
return false; // if entry doesn't exist, means that TT doesn't exist
|
|
214
218
|
ttCount = parseInt(ttCount, 10);
|
|
215
219
|
if (!(0, lang_1.isFiniteNumber)(ttCount) || ttCount < 0) {
|
|
216
|
-
_this.log.info(constants_1.LOG_PREFIX + ("Could not validate traffic type
|
|
220
|
+
_this.log.info(constants_1.LOG_PREFIX + ("Could not validate traffic type existence of " + trafficType + " due to data corruption of some sorts."));
|
|
217
221
|
return false;
|
|
218
222
|
}
|
|
219
223
|
return ttCount > 0;
|
|
220
224
|
})
|
|
221
225
|
.catch(function (e) {
|
|
222
|
-
_this.log.error(constants_1.LOG_PREFIX + ("Could not validate traffic type
|
|
226
|
+
_this.log.error(constants_1.LOG_PREFIX + ("Could not validate traffic type existence of " + trafficType + " due to an error: " + e + "."));
|
|
223
227
|
// If there is an error, bypass the validation so the event can get tracked.
|
|
224
228
|
return true;
|
|
225
229
|
});
|
|
226
230
|
};
|
|
227
|
-
|
|
228
|
-
* Delete everything in the current database.
|
|
229
|
-
*
|
|
230
|
-
* @NOTE documentation says it never fails.
|
|
231
|
-
*/
|
|
231
|
+
// @TODO remove or implement. It is not being used.
|
|
232
232
|
SplitsCacheInRedis.prototype.clear = function () {
|
|
233
|
-
return
|
|
233
|
+
return Promise.resolve();
|
|
234
234
|
};
|
|
235
235
|
/**
|
|
236
236
|
* Fetches multiple splits definitions.
|
|
@@ -161,15 +161,15 @@ var SplitsCachePluggable = /** @class */ (function (_super) {
|
|
|
161
161
|
return this.wrapper.getKeysByPrefix(this.keys.buildSplitKeyPrefix()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
|
|
162
162
|
};
|
|
163
163
|
/**
|
|
164
|
-
* Get list of
|
|
165
|
-
* The returned promise is resolved with the list of
|
|
166
|
-
*
|
|
167
|
-
|
|
164
|
+
* Get list of feature flag names related to a given list of flag set names.
|
|
165
|
+
* The returned promise is resolved with the list of feature flag names per flag set.
|
|
166
|
+
* It never rejects (If there is a wrapper error for some flag set, an empty set is returned for it).
|
|
167
|
+
*/
|
|
168
168
|
SplitsCachePluggable.prototype.getNamesByFlagSets = function (flagSets) {
|
|
169
169
|
var _this = this;
|
|
170
170
|
return Promise.all(flagSets.map(function (flagSet) {
|
|
171
171
|
var flagSetKey = _this.keys.buildFlagSetKey(flagSet);
|
|
172
|
-
return _this.wrapper.getItems(flagSetKey);
|
|
172
|
+
return _this.wrapper.getItems(flagSetKey).catch(function () { return []; });
|
|
173
173
|
})).then(function (namesByFlagSets) { return namesByFlagSets.map(function (namesByFlagSet) { return new sets_1._Set(namesByFlagSet); }); });
|
|
174
174
|
};
|
|
175
175
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validatePreloadedData = exports.
|
|
3
|
+
exports.validatePreloadedData = exports.validateTrafficTypeExistence = exports.validateSplitExistence = exports.validateIfOperational = exports.validateIfNotDestroyed = exports.validateTrafficType = exports.validateSplits = exports.validateSplit = exports.validateKey = exports.validateEventProperties = exports.validateEventValue = exports.validateEvent = exports.validateAttributes = exports.releaseApiKey = exports.validateAndTrackApiKey = exports.validateApiKey = void 0;
|
|
4
4
|
var apiKey_1 = require("./apiKey");
|
|
5
5
|
Object.defineProperty(exports, "validateApiKey", { enumerable: true, get: function () { return apiKey_1.validateApiKey; } });
|
|
6
6
|
Object.defineProperty(exports, "validateAndTrackApiKey", { enumerable: true, get: function () { return apiKey_1.validateAndTrackApiKey; } });
|
|
@@ -24,9 +24,9 @@ Object.defineProperty(exports, "validateTrafficType", { enumerable: true, get: f
|
|
|
24
24
|
var isOperational_1 = require("./isOperational");
|
|
25
25
|
Object.defineProperty(exports, "validateIfNotDestroyed", { enumerable: true, get: function () { return isOperational_1.validateIfNotDestroyed; } });
|
|
26
26
|
Object.defineProperty(exports, "validateIfOperational", { enumerable: true, get: function () { return isOperational_1.validateIfOperational; } });
|
|
27
|
-
var
|
|
28
|
-
Object.defineProperty(exports, "
|
|
29
|
-
var
|
|
30
|
-
Object.defineProperty(exports, "
|
|
27
|
+
var splitExistence_1 = require("./splitExistence");
|
|
28
|
+
Object.defineProperty(exports, "validateSplitExistence", { enumerable: true, get: function () { return splitExistence_1.validateSplitExistence; } });
|
|
29
|
+
var trafficTypeExistence_1 = require("./trafficTypeExistence");
|
|
30
|
+
Object.defineProperty(exports, "validateTrafficTypeExistence", { enumerable: true, get: function () { return trafficTypeExistence_1.validateTrafficTypeExistence; } });
|
|
31
31
|
var preloadedData_1 = require("./preloadedData");
|
|
32
32
|
Object.defineProperty(exports, "validatePreloadedData", { enumerable: true, get: function () { return preloadedData_1.validatePreloadedData; } });
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.validateSplitExistence = void 0;
|
|
4
4
|
var labels_1 = require("../labels");
|
|
5
5
|
var constants_1 = require("../../logger/constants");
|
|
6
6
|
/**
|
|
7
7
|
* This is defined here and in this format mostly because of the logger and the fact that it's considered a validation at product level.
|
|
8
8
|
* But it's not going to run on the input validation layer. In any case, the most compeling reason to use it as we do is to avoid going to Redis and get a split twice.
|
|
9
9
|
*/
|
|
10
|
-
function
|
|
10
|
+
function validateSplitExistence(log, readinessManager, splitName, labelOrSplitObj, method) {
|
|
11
11
|
if (readinessManager.isReady()) { // Only if it's ready we validate this, otherwise it may just be that the SDK is not ready yet.
|
|
12
12
|
if (labelOrSplitObj === labels_1.SPLIT_NOT_FOUND || labelOrSplitObj == null) {
|
|
13
13
|
log.warn(constants_1.WARN_NOT_EXISTENT_SPLIT, [method, splitName]);
|
|
@@ -16,4 +16,4 @@ function validateSplitExistance(log, readinessManager, splitName, labelOrSplitOb
|
|
|
16
16
|
}
|
|
17
17
|
return true;
|
|
18
18
|
}
|
|
19
|
-
exports.
|
|
19
|
+
exports.validateSplitExistence = validateSplitExistence;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.validateTrafficTypeExistence = void 0;
|
|
4
4
|
var thenable_1 = require("../promise/thenable");
|
|
5
5
|
var constants_1 = require("../constants");
|
|
6
6
|
var constants_2 = require("../../logger/constants");
|
|
7
|
-
function
|
|
7
|
+
function logTTExistenceWarning(log, maybeTT, method) {
|
|
8
8
|
log.warn(constants_2.WARN_NOT_EXISTENT_TT, [method, maybeTT]);
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Separated from the previous method since on some cases it'll be async.
|
|
12
12
|
*/
|
|
13
|
-
function
|
|
13
|
+
function validateTrafficTypeExistence(log, readinessManager, splitsCache, mode, maybeTT, method) {
|
|
14
14
|
// If not ready or in localhost mode, we won't run the validation
|
|
15
15
|
if (!readinessManager.isReady() || mode === constants_1.LOCALHOST_MODE)
|
|
16
16
|
return true;
|
|
@@ -18,14 +18,14 @@ function validateTrafficTypeExistance(log, readinessManager, splitsCache, mode,
|
|
|
18
18
|
if ((0, thenable_1.thenable)(res)) {
|
|
19
19
|
return res.then(function (isValid) {
|
|
20
20
|
if (!isValid)
|
|
21
|
-
|
|
21
|
+
logTTExistenceWarning(log, maybeTT, method);
|
|
22
22
|
return isValid; // propagate result
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
else {
|
|
26
26
|
if (!res)
|
|
27
|
-
|
|
27
|
+
logTTExistenceWarning(log, maybeTT, method);
|
|
28
28
|
return res;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
exports.
|
|
31
|
+
exports.validateTrafficTypeExistence = validateTrafficTypeExistence;
|
package/esm/sdkClient/client.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { evaluateFeature, evaluateFeatures, evaluateFeaturesByFlagSets } from '../evaluator';
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
3
|
import { getMatching, getBucketing } from '../utils/key';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { validateSplitExistence } from '../utils/inputValidation/splitExistence';
|
|
5
|
+
import { validateTrafficTypeExistence } from '../utils/inputValidation/trafficTypeExistence';
|
|
6
6
|
import { SDK_NOT_READY } from '../utils/labels';
|
|
7
7
|
import { CONTROL, TREATMENT, TREATMENTS, TREATMENT_WITH_CONFIG, TREATMENTS_WITH_CONFIG, TRACK, TREATMENTS_WITH_CONFIG_BY_FLAGSETS, TREATMENTS_BY_FLAGSETS, TREATMENTS_BY_FLAGSET, TREATMENTS_WITH_CONFIG_BY_FLAGSET } from '../utils/constants';
|
|
8
8
|
import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
|
|
@@ -99,7 +99,7 @@ export function clientFactory(params) {
|
|
|
99
99
|
var bucketingKey = getBucketing(key);
|
|
100
100
|
var treatment = evaluation.treatment, label = evaluation.label, changeNumber = evaluation.changeNumber, _a = evaluation.config, config = _a === void 0 ? null : _a;
|
|
101
101
|
log.info(IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
|
|
102
|
-
if (
|
|
102
|
+
if (validateSplitExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) {
|
|
103
103
|
log.info(IMPRESSION_QUEUEING);
|
|
104
104
|
queue.push({
|
|
105
105
|
feature: featureFlagName,
|
|
@@ -133,7 +133,7 @@ export function clientFactory(params) {
|
|
|
133
133
|
properties: properties
|
|
134
134
|
};
|
|
135
135
|
// This may be async but we only warn, we don't actually care if it is valid or not in terms of queueing the event.
|
|
136
|
-
|
|
136
|
+
validateTrafficTypeExistence(log, readinessManager, storage.splits, mode, trafficTypeName, 'track');
|
|
137
137
|
var result = eventTracker.track(eventData, size);
|
|
138
138
|
if (thenable(result)) {
|
|
139
139
|
return result.then(function (result) {
|
package/esm/sdkFactory/index.js
CHANGED
|
@@ -63,7 +63,7 @@ export function sdkFactory(params) {
|
|
|
63
63
|
ctx.signalListener = signalListener;
|
|
64
64
|
// SDK client and manager
|
|
65
65
|
var clientMethod = sdkClientMethodFactory(ctx);
|
|
66
|
-
var managerInstance = sdkManagerFactory(
|
|
66
|
+
var managerInstance = sdkManagerFactory(settings, storage.splits, sdkReadinessManager);
|
|
67
67
|
syncManager && syncManager.start();
|
|
68
68
|
signalListener && signalListener.start();
|
|
69
69
|
log.info(NEW_FACTORY);
|
package/esm/sdkManager/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
3
|
import { find } from '../utils/lang';
|
|
4
|
-
import { validateSplit,
|
|
4
|
+
import { validateSplit, validateSplitExistence, validateIfNotDestroyed, validateIfOperational } from '../utils/inputValidation';
|
|
5
|
+
import { isStorageSync } from '../trackers/impressionObserver/utils';
|
|
5
6
|
var SPLIT_FN_LABEL = 'split';
|
|
6
7
|
var SPLITS_FN_LABEL = 'splits';
|
|
7
8
|
var NAMES_FN_LABEL = 'names';
|
|
@@ -38,8 +39,10 @@ function objectsToViews(splitObjects) {
|
|
|
38
39
|
});
|
|
39
40
|
return views;
|
|
40
41
|
}
|
|
41
|
-
export function sdkManagerFactory(
|
|
42
|
+
export function sdkManagerFactory(settings, splits, _a) {
|
|
42
43
|
var readinessManager = _a.readinessManager, sdkStatus = _a.sdkStatus;
|
|
44
|
+
var log = settings.log;
|
|
45
|
+
var isSync = isStorageSync(settings);
|
|
43
46
|
return objectAssign(
|
|
44
47
|
// Proto-linkage of the readiness Event Emitter
|
|
45
48
|
Object.create(sdkStatus), {
|
|
@@ -49,16 +52,16 @@ export function sdkManagerFactory(log, splits, _a) {
|
|
|
49
52
|
split: function (featureFlagName) {
|
|
50
53
|
var splitName = validateSplit(log, featureFlagName, SPLIT_FN_LABEL);
|
|
51
54
|
if (!validateIfNotDestroyed(log, readinessManager, SPLIT_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
|
|
52
|
-
return null;
|
|
55
|
+
return isSync ? null : Promise.resolve(null);
|
|
53
56
|
}
|
|
54
57
|
var split = splits.getSplit(splitName);
|
|
55
58
|
if (thenable(split)) {
|
|
56
59
|
return split.catch(function () { return null; }).then(function (result) {
|
|
57
|
-
|
|
60
|
+
validateSplitExistence(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
|
|
58
61
|
return objectToView(result);
|
|
59
62
|
});
|
|
60
63
|
}
|
|
61
|
-
|
|
64
|
+
validateSplitExistence(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
|
|
62
65
|
return objectToView(split);
|
|
63
66
|
},
|
|
64
67
|
/**
|
|
@@ -66,7 +69,7 @@ export function sdkManagerFactory(log, splits, _a) {
|
|
|
66
69
|
*/
|
|
67
70
|
splits: function () {
|
|
68
71
|
if (!validateIfNotDestroyed(log, readinessManager, SPLITS_FN_LABEL) || !validateIfOperational(log, readinessManager, SPLITS_FN_LABEL)) {
|
|
69
|
-
return [];
|
|
72
|
+
return isSync ? [] : Promise.resolve([]);
|
|
70
73
|
}
|
|
71
74
|
var currentSplits = splits.getAll();
|
|
72
75
|
return thenable(currentSplits) ?
|
|
@@ -78,7 +81,7 @@ export function sdkManagerFactory(log, splits, _a) {
|
|
|
78
81
|
*/
|
|
79
82
|
names: function () {
|
|
80
83
|
if (!validateIfNotDestroyed(log, readinessManager, NAMES_FN_LABEL) || !validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
|
|
81
|
-
return [];
|
|
84
|
+
return isSync ? [] : Promise.resolve([]);
|
|
82
85
|
}
|
|
83
86
|
var splitNames = splits.getSplitNames();
|
|
84
87
|
return thenable(splitNames) ?
|
|
@@ -243,9 +243,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
243
243
|
return;
|
|
244
244
|
var flagSetKey = _this.keys.buildFlagSetKey(featureFlagSet);
|
|
245
245
|
var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
|
|
246
|
-
|
|
247
|
-
flagSetFromLocalStorage = '[]';
|
|
248
|
-
var flagSetCache = new _Set(JSON.parse(flagSetFromLocalStorage));
|
|
246
|
+
var flagSetCache = new _Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []);
|
|
249
247
|
flagSetCache.add(featureFlag.name);
|
|
250
248
|
localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
|
|
251
249
|
});
|