@splitsoftware/splitio-commons 1.12.1-rc.1 → 1.12.1-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGES.txt +2 -1
  2. package/cjs/sdkClient/client.js +4 -4
  3. package/cjs/sdkManager/index.js +2 -2
  4. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +1 -3
  5. package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +1 -5
  6. package/cjs/storages/inRedis/RedisAdapter.js +12 -10
  7. package/cjs/storages/inRedis/SplitsCacheInRedis.js +14 -10
  8. package/cjs/storages/pluggable/SplitsCachePluggable.js +5 -5
  9. package/cjs/utils/inputValidation/index.js +5 -5
  10. package/cjs/utils/inputValidation/{splitExistance.js → splitExistence.js} +3 -3
  11. package/cjs/utils/inputValidation/{trafficTypeExistance.js → trafficTypeExistence.js} +6 -6
  12. package/cjs/utils/redis/RedisMock.js +1 -7
  13. package/esm/sdkClient/client.js +4 -4
  14. package/esm/sdkManager/index.js +3 -3
  15. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +1 -3
  16. package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +1 -5
  17. package/esm/storages/inRedis/RedisAdapter.js +12 -10
  18. package/esm/storages/inRedis/SplitsCacheInRedis.js +14 -10
  19. package/esm/storages/pluggable/SplitsCachePluggable.js +5 -5
  20. package/esm/utils/inputValidation/index.js +2 -2
  21. package/esm/utils/inputValidation/{splitExistance.js → splitExistence.js} +1 -1
  22. package/esm/utils/inputValidation/{trafficTypeExistance.js → trafficTypeExistence.js} +4 -4
  23. package/esm/utils/redis/RedisMock.js +1 -7
  24. package/package.json +1 -1
  25. package/src/sdkClient/client.ts +4 -4
  26. package/src/sdkManager/index.ts +3 -3
  27. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +4 -5
  28. package/src/storages/inRedis/EventsCacheInRedis.ts +3 -3
  29. package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +4 -8
  30. package/src/storages/inRedis/ImpressionsCacheInRedis.ts +3 -3
  31. package/src/storages/inRedis/RedisAdapter.ts +16 -12
  32. package/src/storages/inRedis/SegmentsCacheInRedis.ts +3 -3
  33. package/src/storages/inRedis/SplitsCacheInRedis.ts +17 -14
  34. package/src/storages/inRedis/TelemetryCacheInRedis.ts +2 -2
  35. package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +3 -3
  36. package/src/storages/pluggable/SplitsCachePluggable.ts +5 -5
  37. package/src/utils/inputValidation/index.ts +2 -2
  38. package/src/utils/inputValidation/{splitExistance.ts → splitExistence.ts} +1 -1
  39. package/src/utils/inputValidation/{trafficTypeExistance.ts → trafficTypeExistence.ts} +4 -4
  40. package/src/utils/redis/RedisMock.ts +1 -11
  41. package/types/storages/inRedis/EventsCacheInRedis.d.ts +2 -2
  42. package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +3 -2
  43. package/types/storages/inRedis/ImpressionsCacheInRedis.d.ts +2 -2
  44. package/types/storages/inRedis/RedisAdapter.d.ts +3 -2
  45. package/types/storages/inRedis/SegmentsCacheInRedis.d.ts +2 -2
  46. package/types/storages/inRedis/SplitsCacheInRedis.d.ts +5 -5
  47. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +2 -2
  48. package/types/storages/inRedis/UniqueKeysCacheInRedis.d.ts +3 -2
  49. package/types/storages/pluggable/SplitsCachePluggable.d.ts +4 -4
  50. package/types/utils/inputValidation/index.d.ts +2 -2
  51. package/types/utils/inputValidation/splitExistence.d.ts +7 -0
  52. package/types/utils/inputValidation/trafficTypeExistence.d.ts +9 -0
  53. package/types/utils/redis/RedisMock.d.ts +0 -1
package/CHANGES.txt CHANGED
@@ -1,6 +1,7 @@
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 wrap missing commands: 'hincrby', 'popNRaw', 'flushdb' and 'pipeline.exec'.
4
5
 
5
6
  1.11.0 (November 3, 2023)
6
7
  - 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):
@@ -57,7 +58,7 @@
57
58
  - 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
59
  - Updated SDK telemetry to support pluggable storage, partial consumer mode, and synchronizer.
59
60
  - 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 unnecessarily storage calls when the SDK is not ready.
61
+ - 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
62
 
62
63
  1.6.1 (July 22, 2022)
63
64
  - Updated GoogleAnalyticsToSplit integration to validate `autoRequire` config parameter and avoid some wrong warning logs when mapping GA hit fields to Split event properties.
@@ -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 splitExistance_1 = require("../utils/inputValidation/splitExistance");
8
- var trafficTypeExistance_1 = require("../utils/inputValidation/trafficTypeExistance");
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, splitExistance_1.validateSplitExistance)(log, readinessManager, featureFlagName, label, invokingMethodName)) {
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, trafficTypeExistance_1.validateTrafficTypeExistance)(log, readinessManager, storage.splits, mode, trafficTypeName, 'track');
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) {
@@ -57,11 +57,11 @@ function sdkManagerFactory(log, splits, _a) {
57
57
  var split = splits.getSplit(splitName);
58
58
  if ((0, thenable_1.thenable)(split)) {
59
59
  return split.catch(function () { return null; }).then(function (result) {
60
- (0, inputValidation_1.validateSplitExistance)(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
60
+ (0, inputValidation_1.validateSplitExistence)(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
61
61
  return objectToView(result);
62
62
  });
63
63
  }
64
- (0, inputValidation_1.validateSplitExistance)(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
64
+ (0, inputValidation_1.validateSplitExistence)(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
65
65
  return objectToView(split);
66
66
  },
67
67
  /**
@@ -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
- if (!flagSetFromLocalStorage)
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
  });
@@ -23,11 +23,7 @@ var ImpressionCountsCacheInRedis = /** @class */ (function (_super) {
23
23
  var keys = Object.keys(counts);
24
24
  if (!keys.length)
25
25
  return Promise.resolve(false);
26
- var pipeline = this.redis.pipeline();
27
- keys.forEach(function (key) {
28
- pipeline.hincrby(_this.key, key, counts[key]);
29
- });
30
- return pipeline.exec()
26
+ return this.redis.pipelineExec(keys.map(function (key) { return ['hincrby', _this.key, key, counts[key]]; }))
31
27
  .then(function (data) {
32
28
  // If this is the creation of the key on Redis, set the expiration for it in 3600 seconds.
33
29
  if (data.length && data.length === keys.length) {
@@ -9,7 +9,7 @@ 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', 'pipeline', 'expire', 'mget', 'lrange', 'ltrim', 'hset'];
12
+ var METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', 'expire', 'mget', 'lrange', 'ltrim', 'hset', 'hincrby', 'popNRaw', 'flushdb', 'pipelineExec'];
13
13
  // Not part of the settings since it'll vary on each storage. We should be removing storage specific logic from elsewhere.
14
14
  var DEFAULT_OPTIONS = {
15
15
  connectionTimeout: 10000,
@@ -40,6 +40,9 @@ var RedisAdapter = /** @class */ (function (_super) {
40
40
  _this._setDisconnectWrapper();
41
41
  return _this;
42
42
  }
43
+ RedisAdapter.prototype.pipelineExec = function (commands) {
44
+ return this.pipeline(commands).exec();
45
+ };
43
46
  RedisAdapter.prototype._listenToEvents = function () {
44
47
  var _this = this;
45
48
  this.once('ready', function () {
@@ -58,13 +61,12 @@ var RedisAdapter = /** @class */ (function (_super) {
58
61
  };
59
62
  RedisAdapter.prototype._setTimeoutWrappers = function () {
60
63
  var instance = this;
61
- METHODS_TO_PROMISE_WRAP.forEach(function (method) {
62
- var originalMethod = instance[method];
63
- instance[method] = function () {
64
+ METHODS_TO_PROMISE_WRAP.forEach(function (methodName) {
65
+ var originalMethod = instance[methodName];
66
+ instance[methodName] = function () {
64
67
  var params = arguments;
65
68
  function commandWrapper() {
66
- instance.log.debug(LOG_PREFIX + ("Executing " + method + "."));
67
- // Return original method
69
+ instance.log.debug(LOG_PREFIX + "Executing " + methodName + ".");
68
70
  var result = originalMethod.apply(instance, params);
69
71
  if ((0, thenable_1.thenable)(result)) {
70
72
  // For handling pending commands on disconnect, add to the set and remove once finished.
@@ -76,7 +78,7 @@ var RedisAdapter = /** @class */ (function (_super) {
76
78
  // Both success and error remove from queue.
77
79
  result.then(cleanUpRunningCommandsCb, cleanUpRunningCommandsCb);
78
80
  return (0, timeout_1.timeout)(instance._options.operationTimeout, result).catch(function (err) {
79
- instance.log.error(LOG_PREFIX + (method + " operation threw an error or exceeded configured timeout of " + instance._options.operationTimeout + "ms. Message: " + err));
81
+ instance.log.error("" + LOG_PREFIX + methodName + " operation threw an error or exceeded configured timeout of " + instance._options.operationTimeout + "ms. Message: " + err);
80
82
  // Handling is not the adapter responsibility.
81
83
  throw err;
82
84
  });
@@ -89,7 +91,7 @@ var RedisAdapter = /** @class */ (function (_super) {
89
91
  resolve: res,
90
92
  reject: rej,
91
93
  command: commandWrapper,
92
- name: method.toUpperCase()
94
+ name: methodName.toUpperCase()
93
95
  });
94
96
  });
95
97
  }
@@ -107,7 +109,7 @@ var RedisAdapter = /** @class */ (function (_super) {
107
109
  for (var _i = 0; _i < arguments.length; _i++) {
108
110
  params[_i] = arguments[_i];
109
111
  }
110
- setTimeout(function deferedDisconnect() {
112
+ setTimeout(function deferredDisconnect() {
111
113
  if (instance._runningCommands.size > 0) {
112
114
  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
115
  Promise.all((0, sets_1.setToArray)(instance._runningCommands))
@@ -158,7 +160,7 @@ var RedisAdapter = /** @class */ (function (_super) {
158
160
  * Parses the options into what we care about.
159
161
  */
160
162
  RedisAdapter._defineOptions = function (_a) {
161
- var connectionTimeout = _a.connectionTimeout, operationTimeout = _a.operationTimeout, url = _a.url, host = _a.host, port = _a.port, db = _a.db, pass = _a.pass, tls = _a.tls;
163
+ var _b = _a === void 0 ? {} : _a, connectionTimeout = _b.connectionTimeout, operationTimeout = _b.operationTimeout, url = _b.url, host = _b.host, port = _b.port, db = _b.db, pass = _b.pass, tls = _b.tls;
162
164
  var parsedOptions = {
163
165
  connectionTimeout: connectionTimeout,
164
166
  operationTimeout: operationTimeout,
@@ -171,7 +171,7 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
171
171
  SplitsCacheInRedis.prototype.getAll = function () {
172
172
  var _this = this;
173
173
  return this.redis.keys(this.keys.searchPatternForSplitKeys())
174
- .then(function (listOfKeys) { return _this.redis.pipeline(listOfKeys.map(function (k) { return ['get', k]; })).exec(); })
174
+ .then(function (listOfKeys) { return _this.redis.pipelineExec(listOfKeys.map(function (k) { return ['get', k]; })); })
175
175
  .then(processPipelineAnswer)
176
176
  .then(function (splitDefinitions) { return splitDefinitions.map(function (splitDefinition) {
177
177
  return JSON.parse(splitDefinition);
@@ -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 split names related to a given flag set names list.
191
- * The returned promise is resolved with the list of split names,
192
- * or rejected if any wrapper operation fails.
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.
193
193
  */
194
194
  SplitsCacheInRedis.prototype.getNamesByFlagSets = function (flagSets) {
195
195
  var _this = this;
196
- return Promise.all(flagSets.map(function (flagSet) {
197
- var flagSetKey = _this.keys.buildFlagSetKey(flagSet);
198
- return _this.redis.smembers(flagSetKey);
199
- })).then(function (namesByFlagSets) { return namesByFlagSets.map(function (namesByFlagSet) { return new sets_1._Set(namesByFlagSet); }); });
196
+ return this.redis.pipelineExec(flagSets.map(function (flagSet) { return ['smembers', _this.keys.buildFlagSetKey(flagSet)]; }))
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,13 +217,13 @@ 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 existance of " + trafficType + " due to data corruption of some sorts."));
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 existance of " + trafficType + " due to an error: " + e + "."));
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
  });
@@ -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 split names related to a given flag set names list.
165
- * The returned promise is resolved with the list of split names,
166
- * or rejected if any wrapper operation fails.
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.validateTrafficTypeExistance = exports.validateSplitExistance = 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;
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 splitExistance_1 = require("./splitExistance");
28
- Object.defineProperty(exports, "validateSplitExistance", { enumerable: true, get: function () { return splitExistance_1.validateSplitExistance; } });
29
- var trafficTypeExistance_1 = require("./trafficTypeExistance");
30
- Object.defineProperty(exports, "validateTrafficTypeExistance", { enumerable: true, get: function () { return trafficTypeExistance_1.validateTrafficTypeExistance; } });
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.validateSplitExistance = void 0;
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 validateSplitExistance(log, readinessManager, splitName, labelOrSplitObj, method) {
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.validateSplitExistance = validateSplitExistance;
19
+ exports.validateSplitExistence = validateSplitExistence;
@@ -1,16 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateTrafficTypeExistance = void 0;
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 logTTExistanceWarning(log, maybeTT, method) {
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 validateTrafficTypeExistance(log, readinessManager, splitsCache, mode, maybeTT, method) {
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
- logTTExistanceWarning(log, maybeTT, method);
21
+ logTTExistenceWarning(log, maybeTT, method);
22
22
  return isValid; // propagate result
23
23
  });
24
24
  }
25
25
  else {
26
26
  if (!res)
27
- logTTExistanceWarning(log, maybeTT, method);
27
+ logTTExistenceWarning(log, maybeTT, method);
28
28
  return res;
29
29
  }
30
30
  }
31
- exports.validateTrafficTypeExistance = validateTrafficTypeExistance;
31
+ exports.validateTrafficTypeExistence = validateTrafficTypeExistence;
@@ -9,22 +9,16 @@ function asyncFunction(data) {
9
9
  return Promise.resolve(data);
10
10
  }
11
11
  var IDENTITY_METHODS = [];
12
- var ASYNC_METHODS = ['rpush', 'hincrby'];
13
- var PIPELINE_METHODS = ['rpush', 'hincrby'];
12
+ var ASYNC_METHODS = ['rpush', 'hincrby', 'pipelineExec'];
14
13
  var RedisMock = /** @class */ (function () {
15
14
  function RedisMock() {
16
15
  var _this = this;
17
- this.pipelineMethods = { exec: jest.fn(asyncFunction) };
18
16
  IDENTITY_METHODS.forEach(function (method) {
19
17
  _this[method] = jest.fn(identityFunction);
20
18
  });
21
19
  ASYNC_METHODS.forEach(function (method) {
22
20
  _this[method] = jest.fn(asyncFunction);
23
21
  });
24
- PIPELINE_METHODS.forEach(function (method) {
25
- _this.pipelineMethods[method] = _this[method];
26
- });
27
- this.pipeline = jest.fn(function () { return _this.pipelineMethods; });
28
22
  }
29
23
  return RedisMock;
30
24
  }());
@@ -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 { validateSplitExistance } from '../utils/inputValidation/splitExistance';
5
- import { validateTrafficTypeExistance } from '../utils/inputValidation/trafficTypeExistance';
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 (validateSplitExistance(log, readinessManager, featureFlagName, label, invokingMethodName)) {
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
- validateTrafficTypeExistance(log, readinessManager, storage.splits, mode, trafficTypeName, 'track');
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) {
@@ -1,7 +1,7 @@
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, validateSplitExistance, validateIfNotDestroyed, validateIfOperational } from '../utils/inputValidation';
4
+ import { validateSplit, validateSplitExistence, validateIfNotDestroyed, validateIfOperational } from '../utils/inputValidation';
5
5
  var SPLIT_FN_LABEL = 'split';
6
6
  var SPLITS_FN_LABEL = 'splits';
7
7
  var NAMES_FN_LABEL = 'names';
@@ -54,11 +54,11 @@ export function sdkManagerFactory(log, splits, _a) {
54
54
  var split = splits.getSplit(splitName);
55
55
  if (thenable(split)) {
56
56
  return split.catch(function () { return null; }).then(function (result) {
57
- validateSplitExistance(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
57
+ validateSplitExistence(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
58
58
  return objectToView(result);
59
59
  });
60
60
  }
61
- validateSplitExistance(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
61
+ validateSplitExistence(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
62
62
  return objectToView(split);
63
63
  },
64
64
  /**
@@ -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
- if (!flagSetFromLocalStorage)
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
  });
@@ -20,11 +20,7 @@ var ImpressionCountsCacheInRedis = /** @class */ (function (_super) {
20
20
  var keys = Object.keys(counts);
21
21
  if (!keys.length)
22
22
  return Promise.resolve(false);
23
- var pipeline = this.redis.pipeline();
24
- keys.forEach(function (key) {
25
- pipeline.hincrby(_this.key, key, counts[key]);
26
- });
27
- return pipeline.exec()
23
+ return this.redis.pipelineExec(keys.map(function (key) { return ['hincrby', _this.key, key, counts[key]]; }))
28
24
  .then(function (data) {
29
25
  // If this is the creation of the key on Redis, set the expiration for it in 3600 seconds.
30
26
  if (data.length && data.length === keys.length) {
@@ -6,7 +6,7 @@ import { thenable } from '../../utils/promise/thenable';
6
6
  import { timeout } from '../../utils/promise/timeout';
7
7
  var LOG_PREFIX = 'storage:redis-adapter: ';
8
8
  // If we ever decide to fully wrap every method, there's a Commander.getBuiltinCommands from ioredis.
9
- var METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', 'pipeline', 'expire', 'mget', 'lrange', 'ltrim', 'hset'];
9
+ var METHODS_TO_PROMISE_WRAP = ['set', 'exec', 'del', 'get', 'keys', 'sadd', 'srem', 'sismember', 'smembers', 'incr', 'rpush', 'expire', 'mget', 'lrange', 'ltrim', 'hset', 'hincrby', 'popNRaw', 'flushdb', 'pipelineExec'];
10
10
  // Not part of the settings since it'll vary on each storage. We should be removing storage specific logic from elsewhere.
11
11
  var DEFAULT_OPTIONS = {
12
12
  connectionTimeout: 10000,
@@ -37,6 +37,9 @@ var RedisAdapter = /** @class */ (function (_super) {
37
37
  _this._setDisconnectWrapper();
38
38
  return _this;
39
39
  }
40
+ RedisAdapter.prototype.pipelineExec = function (commands) {
41
+ return this.pipeline(commands).exec();
42
+ };
40
43
  RedisAdapter.prototype._listenToEvents = function () {
41
44
  var _this = this;
42
45
  this.once('ready', function () {
@@ -55,13 +58,12 @@ var RedisAdapter = /** @class */ (function (_super) {
55
58
  };
56
59
  RedisAdapter.prototype._setTimeoutWrappers = function () {
57
60
  var instance = this;
58
- METHODS_TO_PROMISE_WRAP.forEach(function (method) {
59
- var originalMethod = instance[method];
60
- instance[method] = function () {
61
+ METHODS_TO_PROMISE_WRAP.forEach(function (methodName) {
62
+ var originalMethod = instance[methodName];
63
+ instance[methodName] = function () {
61
64
  var params = arguments;
62
65
  function commandWrapper() {
63
- instance.log.debug(LOG_PREFIX + ("Executing " + method + "."));
64
- // Return original method
66
+ instance.log.debug(LOG_PREFIX + "Executing " + methodName + ".");
65
67
  var result = originalMethod.apply(instance, params);
66
68
  if (thenable(result)) {
67
69
  // For handling pending commands on disconnect, add to the set and remove once finished.
@@ -73,7 +75,7 @@ var RedisAdapter = /** @class */ (function (_super) {
73
75
  // Both success and error remove from queue.
74
76
  result.then(cleanUpRunningCommandsCb, cleanUpRunningCommandsCb);
75
77
  return timeout(instance._options.operationTimeout, result).catch(function (err) {
76
- instance.log.error(LOG_PREFIX + (method + " operation threw an error or exceeded configured timeout of " + instance._options.operationTimeout + "ms. Message: " + err));
78
+ instance.log.error("" + LOG_PREFIX + methodName + " operation threw an error or exceeded configured timeout of " + instance._options.operationTimeout + "ms. Message: " + err);
77
79
  // Handling is not the adapter responsibility.
78
80
  throw err;
79
81
  });
@@ -86,7 +88,7 @@ var RedisAdapter = /** @class */ (function (_super) {
86
88
  resolve: res,
87
89
  reject: rej,
88
90
  command: commandWrapper,
89
- name: method.toUpperCase()
91
+ name: methodName.toUpperCase()
90
92
  });
91
93
  });
92
94
  }
@@ -104,7 +106,7 @@ var RedisAdapter = /** @class */ (function (_super) {
104
106
  for (var _i = 0; _i < arguments.length; _i++) {
105
107
  params[_i] = arguments[_i];
106
108
  }
107
- setTimeout(function deferedDisconnect() {
109
+ setTimeout(function deferredDisconnect() {
108
110
  if (instance._runningCommands.size > 0) {
109
111
  instance.log.info(LOG_PREFIX + ("Attempting to disconnect but there are " + instance._runningCommands.size + " commands still waiting for resolution. Defering disconnection until those finish."));
110
112
  Promise.all(setToArray(instance._runningCommands))
@@ -155,7 +157,7 @@ var RedisAdapter = /** @class */ (function (_super) {
155
157
  * Parses the options into what we care about.
156
158
  */
157
159
  RedisAdapter._defineOptions = function (_a) {
158
- var connectionTimeout = _a.connectionTimeout, operationTimeout = _a.operationTimeout, url = _a.url, host = _a.host, port = _a.port, db = _a.db, pass = _a.pass, tls = _a.tls;
160
+ var _b = _a === void 0 ? {} : _a, connectionTimeout = _b.connectionTimeout, operationTimeout = _b.operationTimeout, url = _b.url, host = _b.host, port = _b.port, db = _b.db, pass = _b.pass, tls = _b.tls;
159
161
  var parsedOptions = {
160
162
  connectionTimeout: connectionTimeout,
161
163
  operationTimeout: operationTimeout,
@@ -168,7 +168,7 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
168
168
  SplitsCacheInRedis.prototype.getAll = function () {
169
169
  var _this = this;
170
170
  return this.redis.keys(this.keys.searchPatternForSplitKeys())
171
- .then(function (listOfKeys) { return _this.redis.pipeline(listOfKeys.map(function (k) { return ['get', k]; })).exec(); })
171
+ .then(function (listOfKeys) { return _this.redis.pipelineExec(listOfKeys.map(function (k) { return ['get', k]; })); })
172
172
  .then(processPipelineAnswer)
173
173
  .then(function (splitDefinitions) { return splitDefinitions.map(function (splitDefinition) {
174
174
  return JSON.parse(splitDefinition);
@@ -184,16 +184,20 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
184
184
  return this.redis.keys(this.keys.searchPatternForSplitKeys()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
185
185
  };
186
186
  /**
187
- * Get list of split names related to a given flag set names list.
188
- * The returned promise is resolved with the list of split names,
189
- * or rejected if any wrapper operation fails.
187
+ * Get list of feature flag names related to a given list of flag set names.
188
+ * The returned promise is resolved with the list of feature flag names per flag set,
189
+ * or rejected if the pipelined redis operation fails.
190
190
  */
191
191
  SplitsCacheInRedis.prototype.getNamesByFlagSets = function (flagSets) {
192
192
  var _this = this;
193
- return Promise.all(flagSets.map(function (flagSet) {
194
- var flagSetKey = _this.keys.buildFlagSetKey(flagSet);
195
- return _this.redis.smembers(flagSetKey);
196
- })).then(function (namesByFlagSets) { return namesByFlagSets.map(function (namesByFlagSet) { return new _Set(namesByFlagSet); }); });
193
+ return this.redis.pipelineExec(flagSets.map(function (flagSet) { return ['smembers', _this.keys.buildFlagSetKey(flagSet)]; }))
194
+ .then(function (results) { return results.map(function (_a, index) {
195
+ var e = _a[0], value = _a[1];
196
+ if (e === null)
197
+ return value;
198
+ _this.log.error(LOG_PREFIX + ("Could not read result from get members of flag set " + flagSets[index] + " due to an error: " + e));
199
+ }); })
200
+ .then(function (namesByFlagSets) { return namesByFlagSets.map(function (namesByFlagSet) { return new _Set(namesByFlagSet); }); });
197
201
  };
198
202
  /**
199
203
  * Check traffic type existence.
@@ -210,13 +214,13 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
210
214
  return false; // if entry doesn't exist, means that TT doesn't exist
211
215
  ttCount = parseInt(ttCount, 10);
212
216
  if (!isFiniteNumber(ttCount) || ttCount < 0) {
213
- _this.log.info(LOG_PREFIX + ("Could not validate traffic type existance of " + trafficType + " due to data corruption of some sorts."));
217
+ _this.log.info(LOG_PREFIX + ("Could not validate traffic type existence of " + trafficType + " due to data corruption of some sorts."));
214
218
  return false;
215
219
  }
216
220
  return ttCount > 0;
217
221
  })
218
222
  .catch(function (e) {
219
- _this.log.error(LOG_PREFIX + ("Could not validate traffic type existance of " + trafficType + " due to an error: " + e + "."));
223
+ _this.log.error(LOG_PREFIX + ("Could not validate traffic type existence of " + trafficType + " due to an error: " + e + "."));
220
224
  // If there is an error, bypass the validation so the event can get tracked.
221
225
  return true;
222
226
  });
@@ -158,15 +158,15 @@ var SplitsCachePluggable = /** @class */ (function (_super) {
158
158
  return this.wrapper.getKeysByPrefix(this.keys.buildSplitKeyPrefix()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
159
159
  };
160
160
  /**
161
- * Get list of split names related to a given flag set names list.
162
- * The returned promise is resolved with the list of split names,
163
- * or rejected if any wrapper operation fails.
164
- */
161
+ * Get list of feature flag names related to a given list of flag set names.
162
+ * The returned promise is resolved with the list of feature flag names per flag set.
163
+ * It never rejects (If there is a wrapper error for some flag set, an empty set is returned for it).
164
+ */
165
165
  SplitsCachePluggable.prototype.getNamesByFlagSets = function (flagSets) {
166
166
  var _this = this;
167
167
  return Promise.all(flagSets.map(function (flagSet) {
168
168
  var flagSetKey = _this.keys.buildFlagSetKey(flagSet);
169
- return _this.wrapper.getItems(flagSetKey);
169
+ return _this.wrapper.getItems(flagSetKey).catch(function () { return []; });
170
170
  })).then(function (namesByFlagSets) { return namesByFlagSets.map(function (namesByFlagSet) { return new _Set(namesByFlagSet); }); });
171
171
  };
172
172
  /**
@@ -8,6 +8,6 @@ export { validateSplit } from './split';
8
8
  export { validateSplits } from './splits';
9
9
  export { validateTrafficType } from './trafficType';
10
10
  export { validateIfNotDestroyed, validateIfOperational } from './isOperational';
11
- export { validateSplitExistance } from './splitExistance';
12
- export { validateTrafficTypeExistance } from './trafficTypeExistance';
11
+ export { validateSplitExistence } from './splitExistence';
12
+ export { validateTrafficTypeExistence } from './trafficTypeExistence';
13
13
  export { validatePreloadedData } from './preloadedData';
@@ -4,7 +4,7 @@ import { WARN_NOT_EXISTENT_SPLIT } from '../../logger/constants';
4
4
  * This is defined here and in this format mostly because of the logger and the fact that it's considered a validation at product level.
5
5
  * But it's not going to run on the input validation layer. In any case, the most compeling reason to use it as we do is to avoid going to Redis and get a split twice.
6
6
  */
7
- export function validateSplitExistance(log, readinessManager, splitName, labelOrSplitObj, method) {
7
+ export function validateSplitExistence(log, readinessManager, splitName, labelOrSplitObj, method) {
8
8
  if (readinessManager.isReady()) { // Only if it's ready we validate this, otherwise it may just be that the SDK is not ready yet.
9
9
  if (labelOrSplitObj === SPLIT_NOT_FOUND || labelOrSplitObj == null) {
10
10
  log.warn(WARN_NOT_EXISTENT_SPLIT, [method, splitName]);