@splitsoftware/splitio-commons 1.9.1 → 1.9.2-rc.0

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 (130) hide show
  1. package/cjs/evaluator/index.js +18 -1
  2. package/cjs/logger/constants.js +7 -2
  3. package/cjs/logger/messages/error.js +2 -0
  4. package/cjs/logger/messages/warn.js +3 -1
  5. package/cjs/myLogger.js +34 -0
  6. package/cjs/sdkClient/client.js +33 -0
  7. package/cjs/sdkClient/clientAttributesDecoration.js +20 -0
  8. package/cjs/sdkClient/clientCS.js +4 -0
  9. package/cjs/sdkClient/clientInputValidation.js +52 -3
  10. package/cjs/sdkManager/index.js +2 -1
  11. package/cjs/services/splitApi.js +7 -1
  12. package/cjs/storages/KeyBuilder.js +3 -0
  13. package/cjs/storages/KeyBuilderSS.js +4 -0
  14. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +63 -27
  15. package/cjs/storages/inLocalStorage/index.js +2 -2
  16. package/cjs/storages/inMemory/InMemoryStorage.js +2 -2
  17. package/cjs/storages/inMemory/InMemoryStorageCS.js +3 -3
  18. package/cjs/storages/inMemory/SplitsCacheInMemory.js +47 -2
  19. package/cjs/storages/inRedis/SplitsCacheInRedis.js +11 -0
  20. package/cjs/storages/pluggable/SplitsCachePluggable.js +11 -0
  21. package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
  22. package/cjs/sync/polling/updaters/splitChangesUpdater.js +24 -4
  23. package/cjs/sync/submitters/telemetrySubmitter.js +15 -1
  24. package/cjs/utils/constants/index.js +6 -2
  25. package/cjs/utils/lang/sets.js +9 -1
  26. package/cjs/utils/settingsValidation/splitFilters.js +77 -2
  27. package/esm/evaluator/index.js +16 -0
  28. package/esm/logger/constants.js +5 -0
  29. package/esm/logger/messages/error.js +2 -0
  30. package/esm/logger/messages/warn.js +3 -1
  31. package/esm/myLogger.js +31 -0
  32. package/esm/sdkClient/client.js +35 -2
  33. package/esm/sdkClient/clientAttributesDecoration.js +20 -0
  34. package/esm/sdkClient/clientCS.js +4 -0
  35. package/esm/sdkClient/clientInputValidation.js +52 -3
  36. package/esm/sdkManager/index.js +2 -1
  37. package/esm/services/splitApi.js +7 -1
  38. package/esm/storages/KeyBuilder.js +3 -0
  39. package/esm/storages/KeyBuilderSS.js +4 -0
  40. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +63 -27
  41. package/esm/storages/inLocalStorage/index.js +2 -2
  42. package/esm/storages/inMemory/InMemoryStorage.js +2 -2
  43. package/esm/storages/inMemory/InMemoryStorageCS.js +3 -3
  44. package/esm/storages/inMemory/SplitsCacheInMemory.js +47 -2
  45. package/esm/storages/inRedis/SplitsCacheInRedis.js +11 -0
  46. package/esm/storages/pluggable/SplitsCachePluggable.js +11 -0
  47. package/esm/sync/polling/syncTasks/splitsSyncTask.js +1 -1
  48. package/esm/sync/polling/updaters/splitChangesUpdater.js +24 -4
  49. package/esm/sync/submitters/telemetrySubmitter.js +15 -1
  50. package/esm/utils/constants/index.js +4 -0
  51. package/esm/utils/lang/sets.js +7 -0
  52. package/esm/utils/settingsValidation/splitFilters.js +76 -2
  53. package/package.json +2 -2
  54. package/src/dtos/types.ts +3 -2
  55. package/src/evaluator/index.ts +24 -0
  56. package/src/logger/constants.ts +5 -0
  57. package/src/logger/messages/error.ts +2 -0
  58. package/src/logger/messages/warn.ts +3 -1
  59. package/src/myLogger.ts +36 -0
  60. package/src/sdkClient/client.ts +42 -2
  61. package/src/sdkClient/clientAttributesDecoration.ts +24 -0
  62. package/src/sdkClient/clientCS.ts +4 -0
  63. package/src/sdkClient/clientInputValidation.ts +56 -4
  64. package/src/sdkManager/index.ts +2 -1
  65. package/src/services/splitApi.ts +6 -1
  66. package/src/storages/AbstractSplitsCacheAsync.ts +2 -0
  67. package/src/storages/AbstractSplitsCacheSync.ts +3 -0
  68. package/src/storages/KeyBuilder.ts +4 -0
  69. package/src/storages/KeyBuilderSS.ts +4 -0
  70. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +74 -28
  71. package/src/storages/inLocalStorage/index.ts +2 -2
  72. package/src/storages/inMemory/InMemoryStorage.ts +2 -2
  73. package/src/storages/inMemory/InMemoryStorageCS.ts +3 -3
  74. package/src/storages/inMemory/SplitsCacheInMemory.ts +50 -1
  75. package/src/storages/inRedis/SplitsCacheInRedis.ts +12 -0
  76. package/src/storages/pluggable/SplitsCachePluggable.ts +12 -0
  77. package/src/storages/types.ts +7 -3
  78. package/src/sync/polling/syncTasks/splitsSyncTask.ts +1 -0
  79. package/src/sync/polling/updaters/splitChangesUpdater.ts +27 -4
  80. package/src/sync/submitters/telemetrySubmitter.ts +19 -2
  81. package/src/sync/submitters/types.ts +7 -1
  82. package/src/types.ts +118 -1
  83. package/src/utils/constants/index.ts +4 -0
  84. package/src/utils/lang/sets.ts +8 -0
  85. package/src/utils/settingsValidation/splitFilters.ts +82 -2
  86. package/types/dtos/types.d.ts +1 -0
  87. package/types/evaluator/index.d.ts +1 -0
  88. package/types/logger/constants.d.ts +5 -0
  89. package/types/myLogger.d.ts +5 -0
  90. package/types/sdkClient/clientAttributesDecoration.d.ts +4 -0
  91. package/types/sdkClient/types.d.ts +18 -0
  92. package/types/storages/AbstractSplitsCacheAsync.d.ts +2 -0
  93. package/types/storages/AbstractSplitsCacheSync.d.ts +2 -0
  94. package/types/storages/KeyBuilder.d.ts +1 -0
  95. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +6 -1
  96. package/types/storages/inMemory/CountsCacheInMemory.d.ts +20 -0
  97. package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +20 -0
  98. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +9 -1
  99. package/types/storages/inRedis/CountsCacheInRedis.d.ts +9 -0
  100. package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +9 -0
  101. package/types/storages/inRedis/SplitsCacheInRedis.d.ts +8 -0
  102. package/types/storages/metadataBuilder.d.ts +3 -0
  103. package/types/storages/pluggable/SplitsCachePluggable.d.ts +8 -0
  104. package/types/storages/types.d.ts +4 -0
  105. package/types/sync/offline/LocalhostFromFile.d.ts +2 -0
  106. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +2 -0
  107. package/types/sync/offline/updaters/splitChangesUpdater.d.ts +0 -0
  108. package/types/sync/polling/updaters/splitChangesUpdater.d.ts +3 -3
  109. package/types/sync/submitters/eventsSyncTask.d.ts +8 -0
  110. package/types/sync/submitters/impressionCountsSubmitterInRedis.d.ts +5 -0
  111. package/types/sync/submitters/impressionCountsSyncTask.d.ts +13 -0
  112. package/types/sync/submitters/impressionsSyncTask.d.ts +14 -0
  113. package/types/sync/submitters/metricsSyncTask.d.ts +12 -0
  114. package/types/sync/submitters/submitterSyncTask.d.ts +10 -0
  115. package/types/sync/submitters/types.d.ts +7 -1
  116. package/types/sync/submitters/uniqueKeysSubmitterInRedis.d.ts +5 -0
  117. package/types/sync/syncTaskComposite.d.ts +5 -0
  118. package/types/trackers/filter/bloomFilter.d.ts +10 -0
  119. package/types/trackers/filter/dictionaryFilter.d.ts +8 -0
  120. package/types/trackers/filter/types.d.ts +5 -0
  121. package/types/types.d.ts +118 -1
  122. package/types/utils/constants/index.d.ts +4 -0
  123. package/types/utils/lang/sets.d.ts +1 -0
  124. package/types/utils/settingsValidation/splitFilters.d.ts +1 -0
  125. package/types/utils/timeTracker/index.d.ts +70 -0
  126. package/types/sdkClient/identity.d.ts +0 -6
  127. package/types/utils/inputValidation/sdkKey.d.ts +0 -7
  128. /package/types/storages/inMemory/{UniqueKeysCacheInMemory.d.ts → uniqueKeysCacheInMemory.d.ts} +0 -0
  129. /package/types/storages/inMemory/{UniqueKeysCacheInMemoryCS.d.ts → uniqueKeysCacheInMemoryCS.d.ts} +0 -0
  130. /package/types/storages/inRedis/{UniqueKeysCacheInRedis.d.ts → uniqueKeysCacheInRedis.d.ts} +0 -0
@@ -3,6 +3,7 @@ import { validateAttributes, validateEvent, validateEventValue, validateEventPro
3
3
  import { startsWith } from '../utils/lang';
4
4
  import { CONTROL, CONTROL_WITH_CONFIG } from '../utils/constants';
5
5
  import { isStorageSync } from '../trackers/impressionObserver/utils';
6
+ import { flagSetsAreValid } from '../utils/settingsValidation/splitFilters';
6
7
  /**
7
8
  * Decorator that validates the input before actually executing the client methods.
8
9
  * We should "guard" the client here, while not polluting the "real" implementation of those methods.
@@ -13,18 +14,26 @@ export function clientInputValidationDecorator(settings, client, readinessManage
13
14
  /**
14
15
  * Avoid repeating this validations code
15
16
  */
16
- function validateEvaluationParams(maybeKey, maybeFeatureFlagNameOrNames, maybeAttributes, methodName) {
17
+ function validateEvaluationParams(maybeKey, maybeFeatureFlagNameOrNames, maybeAttributes, methodName, maybeFlagSetNameOrNames) {
17
18
  var multi = startsWith(methodName, 'getTreatments');
18
19
  var key = validateKey(log, maybeKey, methodName);
19
- var splitOrSplits = multi ? validateSplits(log, maybeFeatureFlagNameOrNames, methodName) : validateSplit(log, maybeFeatureFlagNameOrNames, methodName);
20
+ var splitOrSplits = false;
21
+ var flagSetOrFlagSets = [];
22
+ if (maybeFeatureFlagNameOrNames) {
23
+ splitOrSplits = multi ? validateSplits(log, maybeFeatureFlagNameOrNames, methodName) : validateSplit(log, maybeFeatureFlagNameOrNames, methodName);
24
+ }
20
25
  var attributes = validateAttributes(log, maybeAttributes, methodName);
21
26
  var isNotDestroyed = validateIfNotDestroyed(log, readinessManager, methodName);
27
+ if (maybeFlagSetNameOrNames) {
28
+ flagSetOrFlagSets = flagSetsAreValid(log, methodName, maybeFlagSetNameOrNames, settings.sync.__splitFiltersValidation.groupedFilters.bySet);
29
+ }
22
30
  validateIfOperational(log, readinessManager, methodName);
23
- var valid = isNotDestroyed && key && splitOrSplits && attributes !== false;
31
+ var valid = isNotDestroyed && key && (splitOrSplits || flagSetOrFlagSets.length > 0) && attributes !== false;
24
32
  return {
25
33
  valid: valid,
26
34
  key: key,
27
35
  splitOrSplits: splitOrSplits,
36
+ flagSetOrFlagSets: flagSetOrFlagSets,
28
37
  attributes: attributes
29
38
  };
30
39
  }
@@ -73,6 +82,42 @@ export function clientInputValidationDecorator(settings, client, readinessManage
73
82
  return wrapResult(res_2);
74
83
  }
75
84
  }
85
+ function getTreatmentsByFlagSets(maybeKey, maybeFlagSets, maybeAttributes) {
86
+ var params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, 'getTreatmentsByFlagSets', maybeFlagSets);
87
+ if (params.valid) {
88
+ return client.getTreatmentsByFlagSets(params.key, params.flagSetOrFlagSets, params.attributes);
89
+ }
90
+ else {
91
+ return wrapResult({});
92
+ }
93
+ }
94
+ function getTreatmentsWithConfigByFlagSets(maybeKey, maybeFlagSets, maybeAttributes) {
95
+ var params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, 'getTreatmentsWithConfigByFlagSets', maybeFlagSets);
96
+ if (params.valid) {
97
+ return client.getTreatmentsWithConfigByFlagSets(params.key, params.flagSetOrFlagSets, params.attributes);
98
+ }
99
+ else {
100
+ return wrapResult({});
101
+ }
102
+ }
103
+ function getTreatmentsByFlagSet(maybeKey, maybeFlagSet, maybeAttributes) {
104
+ var params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, 'getTreatmentsByFlagSet', [maybeFlagSet]);
105
+ if (params.valid) {
106
+ return client.getTreatmentsByFlagSet(params.key, params.flagSetOrFlagSets[0], params.attributes);
107
+ }
108
+ else {
109
+ return wrapResult({});
110
+ }
111
+ }
112
+ function getTreatmentsWithConfigByFlagSet(maybeKey, maybeFlagSet, maybeAttributes) {
113
+ var params = validateEvaluationParams(maybeKey, undefined, maybeAttributes, 'getTreatmentsWithConfigByFlagSet', [maybeFlagSet]);
114
+ if (params.valid) {
115
+ return client.getTreatmentsWithConfigByFlagSet(params.key, params.flagSetOrFlagSets[0], params.attributes);
116
+ }
117
+ else {
118
+ return wrapResult({});
119
+ }
120
+ }
76
121
  function track(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties) {
77
122
  var key = validateKey(log, maybeKey, 'track');
78
123
  var tt = validateTrafficType(log, maybeTT, 'track');
@@ -92,6 +137,10 @@ export function clientInputValidationDecorator(settings, client, readinessManage
92
137
  getTreatmentWithConfig: getTreatmentWithConfig,
93
138
  getTreatments: getTreatments,
94
139
  getTreatmentsWithConfig: getTreatmentsWithConfig,
140
+ getTreatmentsByFlagSets: getTreatmentsByFlagSets,
141
+ getTreatmentsWithConfigByFlagSets: getTreatmentsWithConfigByFlagSets,
142
+ getTreatmentsByFlagSet: getTreatmentsByFlagSet,
143
+ getTreatmentsWithConfigByFlagSet: getTreatmentsWithConfigByFlagSet,
95
144
  track: track
96
145
  };
97
146
  }
@@ -24,7 +24,8 @@ function objectToView(splitObject) {
24
24
  killed: splitObject.killed,
25
25
  changeNumber: splitObject.changeNumber || 0,
26
26
  treatments: collectTreatments(splitObject),
27
- configs: splitObject.configurations || {}
27
+ configs: splitObject.configurations || {},
28
+ sets: splitObject.sets || []
28
29
  };
29
30
  }
30
31
  function objectsToViews(splitObjects) {
@@ -1,6 +1,7 @@
1
1
  import { splitHttpClientFactory } from './splitHttpClient';
2
2
  import { objectAssign } from '../utils/lang/objectAssign';
3
3
  import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT } from '../utils/constants';
4
+ import { ERROR_TOO_MANY_SETS } from '../logger/constants';
4
5
  var noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
5
6
  function userKeyToQueryParam(userKey) {
6
7
  return 'users=' + encodeURIComponent(userKey); // no need to check availability of `encodeURIComponent`, since it is a global highly supported.
@@ -37,7 +38,12 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
37
38
  },
38
39
  fetchSplitChanges: function (since, noCache, till) {
39
40
  var url = urls.sdk + "/splitChanges?since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
40
- return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS));
41
+ return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
42
+ .catch(function (err) {
43
+ if (err.statusCode === 414)
44
+ settings.log.error(ERROR_TOO_MANY_SETS);
45
+ throw err;
46
+ });
41
47
  },
42
48
  fetchSegmentChanges: function (since, segmentName, noCache, till) {
43
49
  var url = urls.sdk + "/segmentChanges/" + segmentName + "?since=" + since + (till ? '&till=' + till : '');
@@ -12,6 +12,9 @@ var KeyBuilder = /** @class */ (function () {
12
12
  KeyBuilder.prototype.buildTrafficTypeKey = function (trafficType) {
13
13
  return this.prefix + ".trafficType." + trafficType;
14
14
  };
15
+ KeyBuilder.prototype.buildFlagSetKey = function (flagSet) {
16
+ return this.prefix + ".flagset." + flagSet;
17
+ };
15
18
  KeyBuilder.prototype.buildSplitKey = function (splitName) {
16
19
  return this.prefix + ".split." + splitName;
17
20
  };
@@ -5,6 +5,10 @@ export var METHOD_NAMES = {
5
5
  ts: 'treatments',
6
6
  tc: 'treatmentWithConfig',
7
7
  tcs: 'treatmentsWithConfig',
8
+ tf: 'treatmentsByFlagSet',
9
+ tfs: 'treatmentsByFlagSets',
10
+ tcf: 'treatmentsWithConfigByFlagSet',
11
+ tcfs: 'treatmentsWithConfigByFlagSets',
8
12
  tr: 'track'
9
13
  };
10
14
  var KeyBuilderSS = /** @class */ (function (_super) {
@@ -2,6 +2,7 @@ import { __extends } from "tslib";
2
2
  import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
3
3
  import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
4
4
  import { LOG_PREFIX } from './constants';
5
+ import { _Set, returnSetsUnion, setToArray } from '../../utils/lang/sets';
5
6
  /**
6
7
  * ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
7
8
  */
@@ -13,12 +14,12 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
13
14
  * @param {ISplitFiltersValidation} splitFiltersValidation
14
15
  */
15
16
  function SplitsCacheInLocal(log, keys, expirationTimestamp, splitFiltersValidation) {
16
- if (splitFiltersValidation === void 0) { splitFiltersValidation = { queryString: null, groupedFilters: { byName: [], byPrefix: [] }, validFilters: [] }; }
17
+ if (splitFiltersValidation === void 0) { splitFiltersValidation = { queryString: null, groupedFilters: { bySet: [], byName: [], byPrefix: [] }, validFilters: [] }; }
17
18
  var _this = _super.call(this) || this;
18
19
  _this.log = log;
19
- _this.cacheReadyButNeedsToFlush = false;
20
20
  _this.keys = keys;
21
21
  _this.splitFiltersValidation = splitFiltersValidation;
22
+ _this.flagSetsFilter = _this.splitFiltersValidation.groupedFilters.bySet;
22
23
  _this._checkExpiration(expirationTimestamp);
23
24
  _this._checkFilterQuery();
24
25
  return _this;
@@ -91,6 +92,9 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
91
92
  localStorage.setItem(splitKey, JSON.stringify(split));
92
93
  this._incrementCounts(split);
93
94
  this._decrementCounts(previousSplit);
95
+ if (previousSplit)
96
+ this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
97
+ this.addToFlagSets(split);
94
98
  return true;
95
99
  }
96
100
  catch (e) {
@@ -103,6 +107,8 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
103
107
  var split = this.getSplit(name);
104
108
  localStorage.removeItem(this.keys.buildSplitKey(name));
105
109
  this._decrementCounts(split);
110
+ if (split)
111
+ this.removeFromFlagSets(split.name, split.sets);
106
112
  return true;
107
113
  }
108
114
  catch (e) {
@@ -115,11 +121,6 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
115
121
  return item && JSON.parse(item);
116
122
  };
117
123
  SplitsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
118
- // when cache is ready but using a new split query, we must clear all split data
119
- if (this.cacheReadyButNeedsToFlush) {
120
- this.clear();
121
- this.cacheReadyButNeedsToFlush = false;
122
- }
123
124
  // when using a new split query, we must update it at the store
124
125
  if (this.updateNewFilter) {
125
126
  this.log.info(LOG_PREFIX + 'Split filter query was modified. Updating cache.');
@@ -192,7 +193,7 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
192
193
  * @override
193
194
  */
194
195
  SplitsCacheInLocal.prototype.checkCache = function () {
195
- return this.getChangeNumber() > -1 || this.cacheReadyButNeedsToFlush;
196
+ return this.getChangeNumber() > -1;
196
197
  };
197
198
  /**
198
199
  * Clean Splits cache if its `lastUpdated` timestamp is older than the given `expirationTimestamp`,
@@ -208,31 +209,16 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
208
209
  }
209
210
  };
210
211
  SplitsCacheInLocal.prototype._checkFilterQuery = function () {
211
- var _this = this;
212
- var _a = this.splitFiltersValidation, queryString = _a.queryString, groupedFilters = _a.groupedFilters;
212
+ var queryString = this.splitFiltersValidation.queryString;
213
213
  var queryKey = this.keys.buildSplitsFilterQueryKey();
214
214
  var currentQueryString = localStorage.getItem(queryKey);
215
215
  if (currentQueryString !== queryString) {
216
216
  try {
217
217
  // mark cache to update the new query filter on first successful splits fetch
218
218
  this.updateNewFilter = true;
219
- // if cache is ready:
220
- if (this.checkCache()) {
221
- // * set change number to -1, to fetch splits with -1 `since` value.
222
- localStorage.setItem(this.keys.buildSplitsTillKey(), '-1');
223
- // * remove from cache splits that doesn't match with the new filters
224
- this.getSplitNames().forEach(function (splitName) {
225
- if (queryString && (
226
- // @TODO consider redefining `groupedFilters` to expose a method like `groupedFilters::filter(splitName): boolean`
227
- groupedFilters.byName.indexOf(splitName) > -1 ||
228
- groupedFilters.byPrefix.some(function (prefix) { return splitName.startsWith(prefix + '__'); }))) {
229
- // * set `cacheReadyButNeedsToFlush` so that `checkCache` returns true (the storage is ready to be used) and the data is cleared before updating on first successful splits fetch
230
- _this.cacheReadyButNeedsToFlush = true;
231
- return;
232
- }
233
- _this.removeSplit(splitName);
234
- });
235
- }
219
+ // if there is cache, clear it
220
+ if (this.checkCache())
221
+ this.clear();
236
222
  }
237
223
  catch (e) {
238
224
  this.log.error(LOG_PREFIX + e);
@@ -240,6 +226,56 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
240
226
  }
241
227
  // if the filter didn't change, nothing is done
242
228
  };
229
+ SplitsCacheInLocal.prototype.getNamesByFlagSets = function (flagSets) {
230
+ var _this = this;
231
+ var toReturn = new _Set([]);
232
+ flagSets.forEach(function (flagSet) {
233
+ var flagSetKey = _this.keys.buildFlagSetKey(flagSet);
234
+ var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
235
+ if (flagSetFromLocalStorage) {
236
+ var flagSetCache = new _Set(JSON.parse(flagSetFromLocalStorage));
237
+ toReturn = returnSetsUnion(toReturn, flagSetCache);
238
+ }
239
+ });
240
+ return toReturn;
241
+ };
242
+ SplitsCacheInLocal.prototype.addToFlagSets = function (featureFlag) {
243
+ var _this = this;
244
+ if (!featureFlag.sets)
245
+ return;
246
+ featureFlag.sets.forEach(function (featureFlagSet) {
247
+ if (_this.flagSetsFilter.length > 0 && !_this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === featureFlagSet; }))
248
+ return;
249
+ var flagSetKey = _this.keys.buildFlagSetKey(featureFlagSet);
250
+ var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
251
+ if (!flagSetFromLocalStorage)
252
+ flagSetFromLocalStorage = '[]';
253
+ var flagSetCache = new _Set(JSON.parse(flagSetFromLocalStorage));
254
+ flagSetCache.add(featureFlag.name);
255
+ localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
256
+ });
257
+ };
258
+ SplitsCacheInLocal.prototype.removeFromFlagSets = function (featureFlagName, flagSets) {
259
+ var _this = this;
260
+ if (!flagSets)
261
+ return;
262
+ flagSets.forEach(function (flagSet) {
263
+ _this.removeNames(flagSet, featureFlagName);
264
+ });
265
+ };
266
+ SplitsCacheInLocal.prototype.removeNames = function (flagSetName, featureFlagName) {
267
+ var flagSetKey = this.keys.buildFlagSetKey(flagSetName);
268
+ var flagSetFromLocalStorage = localStorage.getItem(flagSetKey);
269
+ if (!flagSetFromLocalStorage)
270
+ return;
271
+ var flagSetCache = new _Set(JSON.parse(flagSetFromLocalStorage));
272
+ flagSetCache.delete(featureFlagName);
273
+ if (flagSetCache.size === 0) {
274
+ localStorage.removeItem(flagSetKey);
275
+ return;
276
+ }
277
+ localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache)));
278
+ };
243
279
  return SplitsCacheInLocal;
244
280
  }(AbstractSplitsCacheSync));
245
281
  export { SplitsCacheInLocal };
@@ -43,7 +43,7 @@ export function InLocalStorage(options) {
43
43
  uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
44
44
  destroy: function () {
45
45
  var _a;
46
- this.splits = new SplitsCacheInMemory();
46
+ this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
47
47
  this.segments = new MySegmentsCacheInMemory();
48
48
  this.impressions.clear();
49
49
  this.impressionCounts && this.impressionCounts.clear();
@@ -61,7 +61,7 @@ export function InLocalStorage(options) {
61
61
  events: this.events,
62
62
  telemetry: this.telemetry,
63
63
  destroy: function () {
64
- this.splits = new SplitsCacheInMemory();
64
+ this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
65
65
  this.segments = new MySegmentsCacheInMemory();
66
66
  }
67
67
  };
@@ -12,8 +12,8 @@ import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
12
12
  * @param params parameters required by EventsCacheSync
13
13
  */
14
14
  export function InMemoryStorageFactory(params) {
15
- var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, impressionsMode = _a.sync.impressionsMode;
16
- var splits = new SplitsCacheInMemory();
15
+ var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation;
16
+ var splits = new SplitsCacheInMemory(__splitFiltersValidation);
17
17
  var segments = new SegmentsCacheInMemory();
18
18
  var storage = {
19
19
  splits: splits,
@@ -12,8 +12,8 @@ import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
12
12
  * @param params parameters required by EventsCacheSync
13
13
  */
14
14
  export function InMemoryStorageCSFactory(params) {
15
- var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, impressionsMode = _a.sync.impressionsMode;
16
- var splits = new SplitsCacheInMemory();
15
+ var _a = params.settings, _b = _a.scheduler, impressionsQueueSize = _b.impressionsQueueSize, eventsQueueSize = _b.eventsQueueSize, _c = _a.sync, impressionsMode = _c.impressionsMode, __splitFiltersValidation = _c.__splitFiltersValidation;
16
+ var splits = new SplitsCacheInMemory(__splitFiltersValidation);
17
17
  var segments = new MySegmentsCacheInMemory();
18
18
  var storage = {
19
19
  splits: splits,
@@ -43,7 +43,7 @@ export function InMemoryStorageCSFactory(params) {
43
43
  telemetry: this.telemetry,
44
44
  // Set a new splits cache to clean it for the client without affecting other clients
45
45
  destroy: function () {
46
- this.splits = new SplitsCacheInMemory();
46
+ this.splits = new SplitsCacheInMemory(__splitFiltersValidation);
47
47
  this.segments.clear();
48
48
  }
49
49
  };
@@ -1,18 +1,22 @@
1
1
  import { __extends } from "tslib";
2
2
  import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync';
3
3
  import { isFiniteNumber } from '../../utils/lang';
4
+ import { _Set, returnSetsUnion } from '../../utils/lang/sets';
4
5
  /**
5
6
  * Default ISplitsCacheSync implementation that stores split definitions in memory.
6
7
  * Supported by all JS runtimes.
7
8
  */
8
9
  var SplitsCacheInMemory = /** @class */ (function (_super) {
9
10
  __extends(SplitsCacheInMemory, _super);
10
- function SplitsCacheInMemory() {
11
- var _this = _super !== null && _super.apply(this, arguments) || this;
11
+ function SplitsCacheInMemory(splitFiltersValidation) {
12
+ if (splitFiltersValidation === void 0) { splitFiltersValidation = { queryString: null, groupedFilters: { bySet: [], byName: [], byPrefix: [] }, validFilters: [] }; }
13
+ var _this = _super.call(this) || this;
12
14
  _this.splitsCache = {};
13
15
  _this.ttCache = {};
14
16
  _this.changeNumber = -1;
15
17
  _this.splitsWithSegmentsCount = 0;
18
+ _this.flagSetsCache = {};
19
+ _this.flagSetsFilter = splitFiltersValidation.groupedFilters.bySet;
16
20
  return _this;
17
21
  }
18
22
  SplitsCacheInMemory.prototype.clear = function () {
@@ -28,6 +32,7 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
28
32
  this.ttCache[previousTtName]--;
29
33
  if (!this.ttCache[previousTtName])
30
34
  delete this.ttCache[previousTtName];
35
+ this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
31
36
  if (usesSegments(previousSplit)) { // Substract from segments count for the previous version of this Split.
32
37
  this.splitsWithSegmentsCount--;
33
38
  }
@@ -38,6 +43,7 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
38
43
  // Update TT cache
39
44
  var ttName = split.trafficTypeName;
40
45
  this.ttCache[ttName] = (this.ttCache[ttName] || 0) + 1;
46
+ this.addToFlagSets(split);
41
47
  // Add to segments count for the new version of the Split
42
48
  if (usesSegments(split))
43
49
  this.splitsWithSegmentsCount++;
@@ -56,6 +62,7 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
56
62
  this.ttCache[ttName]--; // Update tt cache
57
63
  if (!this.ttCache[ttName])
58
64
  delete this.ttCache[ttName];
65
+ this.removeFromFlagSets(split.name, split.sets);
59
66
  // Update the segments count.
60
67
  if (usesSegments(split))
61
68
  this.splitsWithSegmentsCount--;
@@ -84,6 +91,44 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
84
91
  SplitsCacheInMemory.prototype.usesSegments = function () {
85
92
  return this.getChangeNumber() === -1 || this.splitsWithSegmentsCount > 0;
86
93
  };
94
+ SplitsCacheInMemory.prototype.getNamesByFlagSets = function (flagSets) {
95
+ var _this = this;
96
+ var toReturn = new _Set([]);
97
+ flagSets.forEach(function (flagSet) {
98
+ var featureFlagNames = _this.flagSetsCache[flagSet];
99
+ if (featureFlagNames) {
100
+ toReturn = returnSetsUnion(toReturn, featureFlagNames);
101
+ }
102
+ });
103
+ return toReturn;
104
+ };
105
+ SplitsCacheInMemory.prototype.addToFlagSets = function (featureFlag) {
106
+ var _this = this;
107
+ if (!featureFlag.sets)
108
+ return;
109
+ featureFlag.sets.forEach(function (featureFlagSet) {
110
+ if (_this.flagSetsFilter.length > 0 && !_this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === featureFlagSet; }))
111
+ return;
112
+ if (!_this.flagSetsCache[featureFlagSet])
113
+ _this.flagSetsCache[featureFlagSet] = new _Set([]);
114
+ _this.flagSetsCache[featureFlagSet].add(featureFlag.name);
115
+ });
116
+ };
117
+ SplitsCacheInMemory.prototype.removeFromFlagSets = function (featureFlagName, flagSets) {
118
+ var _this = this;
119
+ if (!flagSets)
120
+ return;
121
+ flagSets.forEach(function (flagSet) {
122
+ _this.removeNames(flagSet, featureFlagName);
123
+ });
124
+ };
125
+ SplitsCacheInMemory.prototype.removeNames = function (flagSetName, featureFlagName) {
126
+ if (!this.flagSetsCache[flagSetName])
127
+ return;
128
+ this.flagSetsCache[flagSetName].delete(featureFlagName);
129
+ if (this.flagSetsCache[flagSetName].size === 0)
130
+ delete this.flagSetsCache[flagSetName];
131
+ };
87
132
  return SplitsCacheInMemory;
88
133
  }(AbstractSplitsCacheSync));
89
134
  export { SplitsCacheInMemory };
@@ -2,6 +2,7 @@ import { __extends } from "tslib";
2
2
  import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
3
3
  import { LOG_PREFIX } from './constants';
4
4
  import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
5
+ import { _Set } from '../../utils/lang/sets';
5
6
  /**
6
7
  * Discard errors for an answer of multiple operations.
7
8
  */
@@ -168,6 +169,16 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
168
169
  var _this = this;
169
170
  return this.redis.keys(this.keys.searchPatternForSplitKeys()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
170
171
  };
172
+ /**
173
+ * Get list of split names related to a given flag set names list.
174
+ * The returned promise is resolved with the list of split names,
175
+ * or rejected if wrapper operation fails.
176
+ * @todo this is a no-op method to be implemented
177
+ */
178
+ SplitsCacheInRedis.prototype.getNamesByFlagSets = function () {
179
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
180
+ return new Promise(function (flagSets) { return new _Set([]); });
181
+ };
171
182
  /**
172
183
  * Check traffic type existence.
173
184
  * The returned promise is resolved with a boolean indicating whether the TT exist or not.
@@ -2,6 +2,7 @@ import { __extends } from "tslib";
2
2
  import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
3
3
  import { LOG_PREFIX } from './constants';
4
4
  import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
5
+ import { _Set } from '../../utils/lang/sets';
5
6
  /**
6
7
  * ISplitsCacheAsync implementation for pluggable storages.
7
8
  */
@@ -142,6 +143,16 @@ var SplitsCachePluggable = /** @class */ (function (_super) {
142
143
  var _this = this;
143
144
  return this.wrapper.getKeysByPrefix(this.keys.buildSplitKeyPrefix()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
144
145
  };
146
+ /**
147
+ * Get list of split names related to a given flag set names list.
148
+ * The returned promise is resolved with the list of split names,
149
+ * or rejected if wrapper operation fails.
150
+ * @todo this is a no-op method to be implemented
151
+ */
152
+ SplitsCachePluggable.prototype.getNamesByFlagSets = function () {
153
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
154
+ return new Promise(function (flagSets) { return new _Set([]); });
155
+ };
145
156
  /**
146
157
  * Check traffic type existence.
147
158
  * The returned promise is resolved with a boolean indicating whether the TT exist or not.
@@ -5,5 +5,5 @@ import { splitChangesUpdaterFactory } from '../updaters/splitChangesUpdater';
5
5
  * Creates a sync task that periodically executes a `splitChangesUpdater` task
6
6
  */
7
7
  export function splitsSyncTaskFactory(fetchSplitChanges, storage, readiness, settings, isClientSide) {
8
- return syncTaskFactory(settings.log, splitChangesUpdaterFactory(settings.log, splitChangesFetcherFactory(fetchSplitChanges), storage.splits, storage.segments, readiness.splits, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, isClientSide), settings.scheduler.featuresRefreshRate, 'splitChangesUpdater');
8
+ return syncTaskFactory(settings.log, splitChangesUpdaterFactory(settings.log, splitChangesFetcherFactory(fetchSplitChanges), storage.splits, storage.segments, settings.sync.__splitFiltersValidation, readiness.splits, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, isClientSide), settings.scheduler.featuresRefreshRate, 'splitChangesUpdater');
9
9
  }
@@ -2,6 +2,7 @@ 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 { startsWith } from '../../../utils/lang';
5
6
  // Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
6
7
  // Returns a promise that could be rejected.
7
8
  // @TODO review together with Segments and MySegments storage APIs
@@ -28,15 +29,34 @@ export function parseSegments(_a) {
28
29
  }
29
30
  return segments;
30
31
  }
32
+ /**
33
+ * If there are defined filters and one feature flag doesn't match with them, its status is changed to 'ARCHIVE' to avoid storing it
34
+ * If there are set filter defined, names filter is ignored
35
+ *
36
+ * @param featureFlag feature flag to be evaluated
37
+ * @param filters splitFiltersValidation bySet | byName
38
+ */
39
+ function matchFilters(featureFlag, filters) {
40
+ var _a = filters.groupedFilters, setsFilter = _a.bySet, namesFilter = _a.byName, prefixFilter = _a.byPrefix;
41
+ if (setsFilter.length > 0)
42
+ return featureFlag.sets && featureFlag.sets.some(function (featureFlagSet) { return setsFilter.indexOf(featureFlagSet) > -1; });
43
+ var namesFilterConfigured = namesFilter.length > 0;
44
+ var prefixFilterConfigured = prefixFilter.length > 0;
45
+ if (!namesFilterConfigured && !prefixFilterConfigured)
46
+ return true;
47
+ var matchNames = namesFilterConfigured && namesFilter.indexOf(featureFlag.name) > -1;
48
+ var matchPrefix = prefixFilterConfigured && prefixFilter.some(function (prefix) { return startsWith(featureFlag.name, prefix); });
49
+ return matchNames || matchPrefix;
50
+ }
31
51
  /**
32
52
  * Given the list of splits from /splitChanges endpoint, it returns the mutations,
33
53
  * i.e., an object with added splits, removed splits and used segments.
34
54
  * Exported for testing purposes.
35
55
  */
36
- export function computeSplitsMutation(entries) {
56
+ export function computeSplitsMutation(entries, filters) {
37
57
  var segments = new _Set();
38
58
  var computed = entries.reduce(function (accum, split) {
39
- if (split.status === 'ACTIVE') {
59
+ if (split.status === 'ACTIVE' && matchFilters(split, filters)) {
40
60
  accum.added.push([split.name, split]);
41
61
  parseSegments(split).forEach(function (segmentName) {
42
62
  segments.add(segmentName);
@@ -64,7 +84,7 @@ export function computeSplitsMutation(entries) {
64
84
  * @param requestTimeoutBeforeReady How long the updater will wait for the request to timeout. Default 0, i.e., never timeout.
65
85
  * @param retriesOnFailureBeforeReady How many retries on `/splitChanges` we the updater do in case of failure or timeout. Default 0, i.e., no retries.
66
86
  */
67
- export function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, segments, splitsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, isClientSide) {
87
+ export function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, segments, splitFiltersValidation, splitsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, isClientSide) {
68
88
  if (requestTimeoutBeforeReady === void 0) { requestTimeoutBeforeReady = 0; }
69
89
  if (retriesOnFailureBeforeReady === void 0) { retriesOnFailureBeforeReady = 0; }
70
90
  var startingUp = true;
@@ -94,7 +114,7 @@ export function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, seg
94
114
  splitChangesFetcher(since, noCache, till, _promiseDecorator))
95
115
  .then(function (splitChanges) {
96
116
  startingUp = false;
97
- var mutation = computeSplitsMutation(splitChanges.splits);
117
+ var mutation = computeSplitsMutation(splitChanges.splits, splitFiltersValidation);
98
118
  log.debug(SYNC_SPLITS_NEW, [mutation.added.length]);
99
119
  log.debug(SYNC_SPLITS_REMOVED, [mutation.removed.length]);
100
120
  log.debug(SYNC_SPLITS_SEGMENTS, [mutation.segments.length]);
@@ -29,6 +29,17 @@ function getRedundantActiveFactories() {
29
29
  return acum + usedKeysMap[sdkKey] - 1;
30
30
  }, 0);
31
31
  }
32
+ function getTelemetryFlagSetsStats(splitFiltersValidation) {
33
+ // Group every configured flag set in an unique array called originalSets
34
+ var flagSetsTotal = 0;
35
+ splitFiltersValidation.validFilters.forEach(function (filter) {
36
+ if (filter.type === 'bySet')
37
+ flagSetsTotal += filter.values.length;
38
+ });
39
+ var flagSetsValid = splitFiltersValidation.groupedFilters.bySet.length;
40
+ var flagSetsIgnored = flagSetsTotal - flagSetsValid;
41
+ return { flagSetsTotal: flagSetsTotal, flagSetsIgnored: flagSetsIgnored };
42
+ }
32
43
  export function getTelemetryConfigStats(mode, storageType) {
33
44
  return {
34
45
  oM: OPERATION_MODE_MAP[mode],
@@ -47,6 +58,7 @@ export function telemetryCacheConfigAdapter(telemetry, settings) {
47
58
  pop: function () {
48
59
  var urls = settings.urls, scheduler = settings.scheduler;
49
60
  var isClientSide = settings.core.key !== undefined;
61
+ var _a = getTelemetryFlagSetsStats(settings.sync.__splitFiltersValidation), flagSetsTotal = _a.flagSetsTotal, flagSetsIgnored = _a.flagSetsIgnored;
50
62
  return objectAssign(getTelemetryConfigStats(settings.mode, settings.storage.type), {
51
63
  sE: settings.streamingEnabled,
52
64
  rR: {
@@ -74,7 +86,9 @@ export function telemetryCacheConfigAdapter(telemetry, settings) {
74
86
  nR: telemetry.getNonReadyUsage(),
75
87
  t: telemetry.popTags(),
76
88
  i: settings.integrations && settings.integrations.map(function (int) { return int.type; }),
77
- uC: settings.userConsent ? USER_CONSENT_MAP[settings.userConsent] : 0
89
+ uC: settings.userConsent ? USER_CONSENT_MAP[settings.userConsent] : 0,
90
+ fsT: flagSetsTotal,
91
+ fsI: flagSetsIgnored
78
92
  });
79
93
  }
80
94
  };
@@ -51,6 +51,10 @@ export var TREATMENT = 't';
51
51
  export var TREATMENTS = 'ts';
52
52
  export var TREATMENT_WITH_CONFIG = 'tc';
53
53
  export var TREATMENTS_WITH_CONFIG = 'tcs';
54
+ export var TREATMENTS_BY_FLAGSET = 'tf';
55
+ export var TREATMENTS_BY_FLAGSETS = 'tfs';
56
+ export var TREATMENTS_WITH_CONFIG_BY_FLAGSET = 'tcf';
57
+ export var TREATMENTS_WITH_CONFIG_BY_FLAGSETS = 'tcfs';
54
58
  export var TRACK = 'tr';
55
59
  export var CONNECTION_ESTABLISHED = 0;
56
60
  export var OCCUPANCY_PRI = 10;
@@ -95,3 +95,10 @@ export function __getSetConstructor() {
95
95
  return SetPoly;
96
96
  }
97
97
  export var _Set = __getSetConstructor();
98
+ export function returnSetsUnion(set, set2) {
99
+ var result = new _Set(setToArray(set));
100
+ set2.forEach(function (value) {
101
+ result.add(value);
102
+ });
103
+ return result;
104
+ }