@splitsoftware/splitio-commons 1.10.0 → 1.10.1-rc.1
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 +9 -0
- package/cjs/evaluator/index.js +18 -1
- package/cjs/logger/constants.js +7 -3
- package/cjs/logger/messages/error.js +2 -0
- package/cjs/logger/messages/warn.js +4 -2
- package/cjs/sdkClient/client.js +33 -0
- package/cjs/sdkClient/clientAttributesDecoration.js +20 -0
- package/cjs/sdkClient/clientCS.js +4 -0
- package/cjs/sdkClient/clientInputValidation.js +52 -3
- package/cjs/sdkManager/index.js +1 -0
- package/cjs/services/splitApi.js +7 -1
- package/cjs/storages/KeyBuilder.js +3 -0
- package/cjs/storages/KeyBuilderSS.js +4 -0
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +63 -27
- package/cjs/storages/inLocalStorage/index.js +2 -2
- package/cjs/storages/inMemory/InMemoryStorage.js +2 -2
- package/cjs/storages/inMemory/InMemoryStorageCS.js +3 -3
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +47 -2
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +11 -0
- package/cjs/storages/pluggable/SplitsCachePluggable.js +11 -0
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +39 -5
- package/cjs/sync/submitters/telemetrySubmitter.js +15 -1
- package/cjs/utils/constants/index.js +6 -2
- package/cjs/utils/lang/sets.js +9 -1
- package/cjs/utils/settingsValidation/index.js +1 -1
- package/cjs/utils/settingsValidation/splitFilters.js +78 -15
- package/esm/evaluator/index.js +16 -0
- package/esm/logger/constants.js +5 -1
- package/esm/logger/messages/error.js +2 -0
- package/esm/logger/messages/warn.js +4 -2
- package/esm/sdkClient/client.js +35 -2
- package/esm/sdkClient/clientAttributesDecoration.js +20 -0
- package/esm/sdkClient/clientCS.js +4 -0
- package/esm/sdkClient/clientInputValidation.js +52 -3
- package/esm/sdkManager/index.js +1 -0
- package/esm/services/splitApi.js +7 -1
- package/esm/storages/KeyBuilder.js +3 -0
- package/esm/storages/KeyBuilderSS.js +4 -0
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +63 -27
- package/esm/storages/inLocalStorage/index.js +2 -2
- package/esm/storages/inMemory/InMemoryStorage.js +2 -2
- package/esm/storages/inMemory/InMemoryStorageCS.js +3 -3
- package/esm/storages/inMemory/SplitsCacheInMemory.js +47 -2
- package/esm/storages/inRedis/SplitsCacheInRedis.js +11 -0
- package/esm/storages/pluggable/SplitsCachePluggable.js +11 -0
- package/esm/sync/polling/syncTasks/splitsSyncTask.js +1 -1
- package/esm/sync/polling/updaters/splitChangesUpdater.js +39 -5
- package/esm/sync/submitters/telemetrySubmitter.js +15 -1
- package/esm/utils/constants/index.js +4 -0
- package/esm/utils/lang/sets.js +7 -0
- package/esm/utils/settingsValidation/index.js +1 -1
- package/esm/utils/settingsValidation/splitFilters.js +72 -10
- package/package.json +2 -2
- package/src/dtos/types.ts +3 -2
- package/src/evaluator/index.ts +24 -0
- package/src/logger/constants.ts +5 -1
- package/src/logger/messages/error.ts +2 -0
- package/src/logger/messages/warn.ts +4 -2
- package/src/sdkClient/client.ts +42 -2
- package/src/sdkClient/clientAttributesDecoration.ts +24 -0
- package/src/sdkClient/clientCS.ts +4 -0
- package/src/sdkClient/clientInputValidation.ts +56 -3
- package/src/sdkManager/index.ts +1 -0
- package/src/services/splitApi.ts +6 -1
- package/src/storages/AbstractSplitsCacheAsync.ts +2 -0
- package/src/storages/AbstractSplitsCacheSync.ts +3 -0
- package/src/storages/KeyBuilder.ts +4 -0
- package/src/storages/KeyBuilderSS.ts +4 -0
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +74 -28
- package/src/storages/inLocalStorage/index.ts +2 -2
- package/src/storages/inMemory/InMemoryStorage.ts +2 -2
- package/src/storages/inMemory/InMemoryStorageCS.ts +3 -3
- package/src/storages/inMemory/SplitsCacheInMemory.ts +50 -1
- package/src/storages/inRedis/SplitsCacheInRedis.ts +12 -0
- package/src/storages/pluggable/SplitsCachePluggable.ts +12 -0
- package/src/storages/types.ts +7 -3
- package/src/sync/polling/syncTasks/splitsSyncTask.ts +1 -0
- package/src/sync/polling/updaters/splitChangesUpdater.ts +40 -6
- package/src/sync/submitters/telemetrySubmitter.ts +19 -2
- package/src/sync/submitters/types.ts +7 -1
- package/src/types.ts +115 -2
- package/src/utils/constants/index.ts +4 -0
- package/src/utils/lang/sets.ts +8 -0
- package/src/utils/settingsValidation/index.ts +1 -1
- package/src/utils/settingsValidation/splitFilters.ts +78 -10
- package/types/dtos/types.d.ts +1 -0
- package/types/evaluator/index.d.ts +1 -0
- package/types/logger/constants.d.ts +5 -1
- package/types/myLogger.d.ts +5 -0
- package/types/sdkClient/clientAttributesDecoration.d.ts +4 -0
- package/types/sdkClient/types.d.ts +18 -0
- package/types/storages/AbstractSplitsCacheAsync.d.ts +2 -0
- package/types/storages/AbstractSplitsCacheSync.d.ts +2 -0
- package/types/storages/KeyBuilder.d.ts +1 -0
- package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +6 -1
- package/types/storages/inMemory/CountsCacheInMemory.d.ts +20 -0
- package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +20 -0
- package/types/storages/inMemory/SplitsCacheInMemory.d.ts +9 -1
- package/types/storages/inRedis/CountsCacheInRedis.d.ts +9 -0
- package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +9 -0
- package/types/storages/inRedis/SplitsCacheInRedis.d.ts +8 -0
- package/types/storages/metadataBuilder.d.ts +3 -0
- package/types/storages/pluggable/SplitsCachePluggable.d.ts +8 -0
- package/types/storages/types.d.ts +4 -0
- package/types/sync/offline/LocalhostFromFile.d.ts +2 -0
- package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +2 -0
- package/types/sync/offline/updaters/splitChangesUpdater.d.ts +0 -0
- package/types/sync/polling/updaters/splitChangesUpdater.d.ts +3 -3
- package/types/sync/submitters/eventsSyncTask.d.ts +8 -0
- package/types/sync/submitters/impressionCountsSubmitterInRedis.d.ts +5 -0
- package/types/sync/submitters/impressionCountsSyncTask.d.ts +13 -0
- package/types/sync/submitters/impressionsSyncTask.d.ts +14 -0
- package/types/sync/submitters/metricsSyncTask.d.ts +12 -0
- package/types/sync/submitters/submitterSyncTask.d.ts +10 -0
- package/types/sync/submitters/types.d.ts +7 -1
- package/types/sync/submitters/uniqueKeysSubmitterInRedis.d.ts +5 -0
- package/types/sync/syncTaskComposite.d.ts +5 -0
- package/types/trackers/filter/bloomFilter.d.ts +10 -0
- package/types/trackers/filter/dictionaryFilter.d.ts +8 -0
- package/types/trackers/filter/types.d.ts +5 -0
- package/types/types.d.ts +114 -1
- package/types/utils/constants/index.d.ts +4 -0
- package/types/utils/lang/sets.d.ts +1 -0
- package/types/utils/settingsValidation/splitFilters.d.ts +2 -2
- package/types/utils/timeTracker/index.d.ts +70 -0
- package/types/sdkClient/identity.d.ts +0 -6
- package/types/utils/inputValidation/sdkKey.d.ts +0 -7
- /package/types/storages/inMemory/{UniqueKeysCacheInMemory.d.ts → uniqueKeysCacheInMemory.d.ts} +0 -0
- /package/types/storages/inMemory/{UniqueKeysCacheInMemoryCS.d.ts → uniqueKeysCacheInMemoryCS.d.ts} +0 -0
- /package/types/storages/inRedis/{UniqueKeysCacheInRedis.d.ts → uniqueKeysCacheInRedis.d.ts} +0 -0
|
@@ -4,18 +4,22 @@ exports.SplitsCacheInMemory = void 0;
|
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var AbstractSplitsCacheSync_1 = require("../AbstractSplitsCacheSync");
|
|
6
6
|
var lang_1 = require("../../utils/lang");
|
|
7
|
+
var sets_1 = require("../../utils/lang/sets");
|
|
7
8
|
/**
|
|
8
9
|
* Default ISplitsCacheSync implementation that stores split definitions in memory.
|
|
9
10
|
* Supported by all JS runtimes.
|
|
10
11
|
*/
|
|
11
12
|
var SplitsCacheInMemory = /** @class */ (function (_super) {
|
|
12
13
|
(0, tslib_1.__extends)(SplitsCacheInMemory, _super);
|
|
13
|
-
function SplitsCacheInMemory() {
|
|
14
|
-
|
|
14
|
+
function SplitsCacheInMemory(splitFiltersValidation) {
|
|
15
|
+
if (splitFiltersValidation === void 0) { splitFiltersValidation = { queryString: null, groupedFilters: { bySet: [], byName: [], byPrefix: [] }, validFilters: [] }; }
|
|
16
|
+
var _this = _super.call(this) || this;
|
|
15
17
|
_this.splitsCache = {};
|
|
16
18
|
_this.ttCache = {};
|
|
17
19
|
_this.changeNumber = -1;
|
|
18
20
|
_this.splitsWithSegmentsCount = 0;
|
|
21
|
+
_this.flagSetsCache = {};
|
|
22
|
+
_this.flagSetsFilter = splitFiltersValidation.groupedFilters.bySet;
|
|
19
23
|
return _this;
|
|
20
24
|
}
|
|
21
25
|
SplitsCacheInMemory.prototype.clear = function () {
|
|
@@ -31,6 +35,7 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
|
|
|
31
35
|
this.ttCache[previousTtName]--;
|
|
32
36
|
if (!this.ttCache[previousTtName])
|
|
33
37
|
delete this.ttCache[previousTtName];
|
|
38
|
+
this.removeFromFlagSets(previousSplit.name, previousSplit.sets);
|
|
34
39
|
if ((0, AbstractSplitsCacheSync_1.usesSegments)(previousSplit)) { // Substract from segments count for the previous version of this Split.
|
|
35
40
|
this.splitsWithSegmentsCount--;
|
|
36
41
|
}
|
|
@@ -41,6 +46,7 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
|
|
|
41
46
|
// Update TT cache
|
|
42
47
|
var ttName = split.trafficTypeName;
|
|
43
48
|
this.ttCache[ttName] = (this.ttCache[ttName] || 0) + 1;
|
|
49
|
+
this.addToFlagSets(split);
|
|
44
50
|
// Add to segments count for the new version of the Split
|
|
45
51
|
if ((0, AbstractSplitsCacheSync_1.usesSegments)(split))
|
|
46
52
|
this.splitsWithSegmentsCount++;
|
|
@@ -59,6 +65,7 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
|
|
|
59
65
|
this.ttCache[ttName]--; // Update tt cache
|
|
60
66
|
if (!this.ttCache[ttName])
|
|
61
67
|
delete this.ttCache[ttName];
|
|
68
|
+
this.removeFromFlagSets(split.name, split.sets);
|
|
62
69
|
// Update the segments count.
|
|
63
70
|
if ((0, AbstractSplitsCacheSync_1.usesSegments)(split))
|
|
64
71
|
this.splitsWithSegmentsCount--;
|
|
@@ -87,6 +94,44 @@ var SplitsCacheInMemory = /** @class */ (function (_super) {
|
|
|
87
94
|
SplitsCacheInMemory.prototype.usesSegments = function () {
|
|
88
95
|
return this.getChangeNumber() === -1 || this.splitsWithSegmentsCount > 0;
|
|
89
96
|
};
|
|
97
|
+
SplitsCacheInMemory.prototype.getNamesByFlagSets = function (flagSets) {
|
|
98
|
+
var _this = this;
|
|
99
|
+
var toReturn = new sets_1._Set([]);
|
|
100
|
+
flagSets.forEach(function (flagSet) {
|
|
101
|
+
var featureFlagNames = _this.flagSetsCache[flagSet];
|
|
102
|
+
if (featureFlagNames) {
|
|
103
|
+
toReturn = (0, sets_1.returnSetsUnion)(toReturn, featureFlagNames);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return toReturn;
|
|
107
|
+
};
|
|
108
|
+
SplitsCacheInMemory.prototype.addToFlagSets = function (featureFlag) {
|
|
109
|
+
var _this = this;
|
|
110
|
+
if (!featureFlag.sets)
|
|
111
|
+
return;
|
|
112
|
+
featureFlag.sets.forEach(function (featureFlagSet) {
|
|
113
|
+
if (_this.flagSetsFilter.length > 0 && !_this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === featureFlagSet; }))
|
|
114
|
+
return;
|
|
115
|
+
if (!_this.flagSetsCache[featureFlagSet])
|
|
116
|
+
_this.flagSetsCache[featureFlagSet] = new sets_1._Set([]);
|
|
117
|
+
_this.flagSetsCache[featureFlagSet].add(featureFlag.name);
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
SplitsCacheInMemory.prototype.removeFromFlagSets = function (featureFlagName, flagSets) {
|
|
121
|
+
var _this = this;
|
|
122
|
+
if (!flagSets)
|
|
123
|
+
return;
|
|
124
|
+
flagSets.forEach(function (flagSet) {
|
|
125
|
+
_this.removeNames(flagSet, featureFlagName);
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
SplitsCacheInMemory.prototype.removeNames = function (flagSetName, featureFlagName) {
|
|
129
|
+
if (!this.flagSetsCache[flagSetName])
|
|
130
|
+
return;
|
|
131
|
+
this.flagSetsCache[flagSetName].delete(featureFlagName);
|
|
132
|
+
if (this.flagSetsCache[flagSetName].size === 0)
|
|
133
|
+
delete this.flagSetsCache[flagSetName];
|
|
134
|
+
};
|
|
90
135
|
return SplitsCacheInMemory;
|
|
91
136
|
}(AbstractSplitsCacheSync_1.AbstractSplitsCacheSync));
|
|
92
137
|
exports.SplitsCacheInMemory = SplitsCacheInMemory;
|
|
@@ -5,6 +5,7 @@ var tslib_1 = require("tslib");
|
|
|
5
5
|
var lang_1 = require("../../utils/lang");
|
|
6
6
|
var constants_1 = require("./constants");
|
|
7
7
|
var AbstractSplitsCacheAsync_1 = require("../AbstractSplitsCacheAsync");
|
|
8
|
+
var sets_1 = require("../../utils/lang/sets");
|
|
8
9
|
/**
|
|
9
10
|
* Discard errors for an answer of multiple operations.
|
|
10
11
|
*/
|
|
@@ -171,6 +172,16 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
|
|
|
171
172
|
var _this = this;
|
|
172
173
|
return this.redis.keys(this.keys.searchPatternForSplitKeys()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
|
|
173
174
|
};
|
|
175
|
+
/**
|
|
176
|
+
* Get list of split names related to a given flag set names list.
|
|
177
|
+
* The returned promise is resolved with the list of split names,
|
|
178
|
+
* or rejected if wrapper operation fails.
|
|
179
|
+
* @todo this is a no-op method to be implemented
|
|
180
|
+
*/
|
|
181
|
+
SplitsCacheInRedis.prototype.getNamesByFlagSets = function () {
|
|
182
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
183
|
+
return new Promise(function (flagSets) { return new sets_1._Set([]); });
|
|
184
|
+
};
|
|
174
185
|
/**
|
|
175
186
|
* Check traffic type existence.
|
|
176
187
|
* The returned promise is resolved with a boolean indicating whether the TT exist or not.
|
|
@@ -5,6 +5,7 @@ var tslib_1 = require("tslib");
|
|
|
5
5
|
var lang_1 = require("../../utils/lang");
|
|
6
6
|
var constants_1 = require("./constants");
|
|
7
7
|
var AbstractSplitsCacheAsync_1 = require("../AbstractSplitsCacheAsync");
|
|
8
|
+
var sets_1 = require("../../utils/lang/sets");
|
|
8
9
|
/**
|
|
9
10
|
* ISplitsCacheAsync implementation for pluggable storages.
|
|
10
11
|
*/
|
|
@@ -145,6 +146,16 @@ var SplitsCachePluggable = /** @class */ (function (_super) {
|
|
|
145
146
|
var _this = this;
|
|
146
147
|
return this.wrapper.getKeysByPrefix(this.keys.buildSplitKeyPrefix()).then(function (listOfKeys) { return listOfKeys.map(_this.keys.extractKey); });
|
|
147
148
|
};
|
|
149
|
+
/**
|
|
150
|
+
* Get list of split names related to a given flag set names list.
|
|
151
|
+
* The returned promise is resolved with the list of split names,
|
|
152
|
+
* or rejected if wrapper operation fails.
|
|
153
|
+
* @todo this is a no-op method to be implemented
|
|
154
|
+
*/
|
|
155
|
+
SplitsCachePluggable.prototype.getNamesByFlagSets = function () {
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
157
|
+
return new Promise(function (flagSets) { return new sets_1._Set([]); });
|
|
158
|
+
};
|
|
148
159
|
/**
|
|
149
160
|
* Check traffic type existence.
|
|
150
161
|
* The returned promise is resolved with a boolean indicating whether the TT exist or not.
|
|
@@ -8,6 +8,6 @@ var splitChangesUpdater_1 = require("../updaters/splitChangesUpdater");
|
|
|
8
8
|
* Creates a sync task that periodically executes a `splitChangesUpdater` task
|
|
9
9
|
*/
|
|
10
10
|
function splitsSyncTaskFactory(fetchSplitChanges, storage, readiness, settings, isClientSide) {
|
|
11
|
-
return (0, syncTask_1.syncTaskFactory)(settings.log, (0, splitChangesUpdater_1.splitChangesUpdaterFactory)(settings.log, (0, splitChangesFetcher_1.splitChangesFetcherFactory)(fetchSplitChanges), storage.splits, storage.segments, readiness.splits, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, isClientSide), settings.scheduler.featuresRefreshRate, 'splitChangesUpdater');
|
|
11
|
+
return (0, syncTask_1.syncTaskFactory)(settings.log, (0, splitChangesUpdater_1.splitChangesUpdaterFactory)(settings.log, (0, splitChangesFetcher_1.splitChangesFetcherFactory)(fetchSplitChanges), storage.splits, storage.segments, settings.sync.__splitFiltersValidation, readiness.splits, settings.startup.requestTimeoutBeforeReady, settings.startup.retriesOnFailureBeforeReady, isClientSide), settings.scheduler.featuresRefreshRate, 'splitChangesUpdater');
|
|
12
12
|
}
|
|
13
13
|
exports.splitsSyncTaskFactory = splitsSyncTaskFactory;
|
|
@@ -5,6 +5,7 @@ var sets_1 = require("../../../utils/lang/sets");
|
|
|
5
5
|
var timeout_1 = require("../../../utils/promise/timeout");
|
|
6
6
|
var constants_1 = require("../../../readiness/constants");
|
|
7
7
|
var constants_2 = require("../../../logger/constants");
|
|
8
|
+
var lang_1 = require("../../../utils/lang");
|
|
8
9
|
// Checks that all registered segments have been fetched (changeNumber !== -1 for every segment).
|
|
9
10
|
// Returns a promise that could be rejected.
|
|
10
11
|
// @TODO review together with Segments and MySegments storage APIs
|
|
@@ -32,15 +33,34 @@ function parseSegments(_a) {
|
|
|
32
33
|
return segments;
|
|
33
34
|
}
|
|
34
35
|
exports.parseSegments = parseSegments;
|
|
36
|
+
/**
|
|
37
|
+
* If there are defined filters and one feature flag doesn't match with them, its status is changed to 'ARCHIVE' to avoid storing it
|
|
38
|
+
* If there are set filter defined, names filter is ignored
|
|
39
|
+
*
|
|
40
|
+
* @param featureFlag feature flag to be evaluated
|
|
41
|
+
* @param filters splitFiltersValidation bySet | byName
|
|
42
|
+
*/
|
|
43
|
+
function matchFilters(featureFlag, filters) {
|
|
44
|
+
var _a = filters.groupedFilters, setsFilter = _a.bySet, namesFilter = _a.byName, prefixFilter = _a.byPrefix;
|
|
45
|
+
if (setsFilter.length > 0)
|
|
46
|
+
return featureFlag.sets && featureFlag.sets.some(function (featureFlagSet) { return setsFilter.indexOf(featureFlagSet) > -1; });
|
|
47
|
+
var namesFilterConfigured = namesFilter.length > 0;
|
|
48
|
+
var prefixFilterConfigured = prefixFilter.length > 0;
|
|
49
|
+
if (!namesFilterConfigured && !prefixFilterConfigured)
|
|
50
|
+
return true;
|
|
51
|
+
var matchNames = namesFilterConfigured && namesFilter.indexOf(featureFlag.name) > -1;
|
|
52
|
+
var matchPrefix = prefixFilterConfigured && prefixFilter.some(function (prefix) { return (0, lang_1.startsWith)(featureFlag.name, prefix); });
|
|
53
|
+
return matchNames || matchPrefix;
|
|
54
|
+
}
|
|
35
55
|
/**
|
|
36
56
|
* Given the list of splits from /splitChanges endpoint, it returns the mutations,
|
|
37
57
|
* i.e., an object with added splits, removed splits and used segments.
|
|
38
58
|
* Exported for testing purposes.
|
|
39
59
|
*/
|
|
40
|
-
function computeSplitsMutation(entries) {
|
|
60
|
+
function computeSplitsMutation(entries, filters) {
|
|
41
61
|
var segments = new sets_1._Set();
|
|
42
62
|
var computed = entries.reduce(function (accum, split) {
|
|
43
|
-
if (split.status === 'ACTIVE') {
|
|
63
|
+
if (split.status === 'ACTIVE' && matchFilters(split, filters)) {
|
|
44
64
|
accum.added.push([split.name, split]);
|
|
45
65
|
parseSegments(split).forEach(function (segmentName) {
|
|
46
66
|
segments.add(segmentName);
|
|
@@ -69,7 +89,7 @@ exports.computeSplitsMutation = computeSplitsMutation;
|
|
|
69
89
|
* @param requestTimeoutBeforeReady How long the updater will wait for the request to timeout. Default 0, i.e., never timeout.
|
|
70
90
|
* @param retriesOnFailureBeforeReady How many retries on `/splitChanges` we the updater do in case of failure or timeout. Default 0, i.e., no retries.
|
|
71
91
|
*/
|
|
72
|
-
function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, segments, splitsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, isClientSide) {
|
|
92
|
+
function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, segments, splitFiltersValidation, splitsEventEmitter, requestTimeoutBeforeReady, retriesOnFailureBeforeReady, isClientSide) {
|
|
73
93
|
if (requestTimeoutBeforeReady === void 0) { requestTimeoutBeforeReady = 0; }
|
|
74
94
|
if (retriesOnFailureBeforeReady === void 0) { retriesOnFailureBeforeReady = 0; }
|
|
75
95
|
var startingUp = true;
|
|
@@ -79,6 +99,17 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, segments,
|
|
|
79
99
|
promise = (0, timeout_1.timeout)(requestTimeoutBeforeReady, promise);
|
|
80
100
|
return promise;
|
|
81
101
|
}
|
|
102
|
+
/** Returns true if at least one split was updated */
|
|
103
|
+
function update(flagsChange) {
|
|
104
|
+
var added = flagsChange[1], removed = flagsChange[2];
|
|
105
|
+
// There is at least one added or modified feature flag
|
|
106
|
+
if (added && added.some(function (update) { return update; }))
|
|
107
|
+
return true;
|
|
108
|
+
// There is at least one removed feature flag
|
|
109
|
+
if (removed && removed.some(function (update) { return update; }))
|
|
110
|
+
return true;
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
82
113
|
/**
|
|
83
114
|
* SplitChanges updater returns a promise that resolves with a `false` boolean value if it fails to fetch splits or synchronize them with the storage.
|
|
84
115
|
* Returned promise will not be rejected.
|
|
@@ -99,7 +130,7 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, segments,
|
|
|
99
130
|
splitChangesFetcher(since, noCache, till, _promiseDecorator))
|
|
100
131
|
.then(function (splitChanges) {
|
|
101
132
|
startingUp = false;
|
|
102
|
-
var mutation = computeSplitsMutation(splitChanges.splits);
|
|
133
|
+
var mutation = computeSplitsMutation(splitChanges.splits, splitFiltersValidation);
|
|
103
134
|
log.debug(constants_2.SYNC_SPLITS_NEW, [mutation.added.length]);
|
|
104
135
|
log.debug(constants_2.SYNC_SPLITS_REMOVED, [mutation.removed.length]);
|
|
105
136
|
log.debug(constants_2.SYNC_SPLITS_SEGMENTS, [mutation.segments.length]);
|
|
@@ -111,7 +142,10 @@ function splitChangesUpdaterFactory(log, splitChangesFetcher, splits, segments,
|
|
|
111
142
|
splits.addSplits(mutation.added),
|
|
112
143
|
splits.removeSplits(mutation.removed),
|
|
113
144
|
segments.registerSegments(mutation.segments)
|
|
114
|
-
]).then(function () {
|
|
145
|
+
]).then(function (flagsChange) {
|
|
146
|
+
var triggerSdkUpdate = update(flagsChange);
|
|
147
|
+
if (!triggerSdkUpdate)
|
|
148
|
+
return true;
|
|
115
149
|
if (splitsEventEmitter) {
|
|
116
150
|
// To emit SDK_SPLITS_ARRIVED for server-side SDK, we must check that all registered segments have been fetched
|
|
117
151
|
return Promise.resolve(!splitsEventEmitter.splitsArrived || (since !== splitChanges.till && (isClientSide || checkAllSegmentsExist(segments))))
|
|
@@ -32,6 +32,17 @@ function getRedundantActiveFactories() {
|
|
|
32
32
|
return acum + apiKey_1.usedKeysMap[sdkKey] - 1;
|
|
33
33
|
}, 0);
|
|
34
34
|
}
|
|
35
|
+
function getTelemetryFlagSetsStats(splitFiltersValidation) {
|
|
36
|
+
// Group every configured flag set in an unique array called originalSets
|
|
37
|
+
var flagSetsTotal = 0;
|
|
38
|
+
splitFiltersValidation.validFilters.forEach(function (filter) {
|
|
39
|
+
if (filter.type === 'bySet')
|
|
40
|
+
flagSetsTotal += filter.values.length;
|
|
41
|
+
});
|
|
42
|
+
var flagSetsValid = splitFiltersValidation.groupedFilters.bySet.length;
|
|
43
|
+
var flagSetsIgnored = flagSetsTotal - flagSetsValid;
|
|
44
|
+
return { flagSetsTotal: flagSetsTotal, flagSetsIgnored: flagSetsIgnored };
|
|
45
|
+
}
|
|
35
46
|
function getTelemetryConfigStats(mode, storageType) {
|
|
36
47
|
return {
|
|
37
48
|
oM: OPERATION_MODE_MAP[mode],
|
|
@@ -51,6 +62,7 @@ function telemetryCacheConfigAdapter(telemetry, settings) {
|
|
|
51
62
|
pop: function () {
|
|
52
63
|
var urls = settings.urls, scheduler = settings.scheduler;
|
|
53
64
|
var isClientSide = settings.core.key !== undefined;
|
|
65
|
+
var _a = getTelemetryFlagSetsStats(settings.sync.__splitFiltersValidation), flagSetsTotal = _a.flagSetsTotal, flagSetsIgnored = _a.flagSetsIgnored;
|
|
54
66
|
return (0, objectAssign_1.objectAssign)(getTelemetryConfigStats(settings.mode, settings.storage.type), {
|
|
55
67
|
sE: settings.streamingEnabled,
|
|
56
68
|
rR: {
|
|
@@ -78,7 +90,9 @@ function telemetryCacheConfigAdapter(telemetry, settings) {
|
|
|
78
90
|
nR: telemetry.getNonReadyUsage(),
|
|
79
91
|
t: telemetry.popTags(),
|
|
80
92
|
i: settings.integrations && settings.integrations.map(function (int) { return int.type; }),
|
|
81
|
-
uC: settings.userConsent ? USER_CONSENT_MAP[settings.userConsent] : 0
|
|
93
|
+
uC: settings.userConsent ? USER_CONSENT_MAP[settings.userConsent] : 0,
|
|
94
|
+
fsT: flagSetsTotal,
|
|
95
|
+
fsI: flagSetsIgnored
|
|
82
96
|
});
|
|
83
97
|
}
|
|
84
98
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.PAUSED = exports.ENABLED = exports.DISABLED = exports.NON_REQUESTED = exports.REQUESTED = exports.POLLING = exports.STREAMING = exports.AUTH_REJECTION = exports.SYNC_MODE_UPDATE = void 0;
|
|
3
|
+
exports.OCCUPANCY_SEC = exports.OCCUPANCY_PRI = exports.CONNECTION_ESTABLISHED = exports.TRACK = exports.TREATMENTS_WITH_CONFIG_BY_FLAGSETS = exports.TREATMENTS_WITH_CONFIG_BY_FLAGSET = exports.TREATMENTS_BY_FLAGSETS = exports.TREATMENTS_BY_FLAGSET = exports.TREATMENTS_WITH_CONFIG = exports.TREATMENT_WITH_CONFIG = exports.TREATMENTS = exports.TREATMENT = exports.MY_SEGMENT = exports.SEGMENT = exports.TOKEN = exports.TELEMETRY = exports.EVENTS = exports.IMPRESSIONS_COUNT = exports.IMPRESSIONS = exports.SPLITS = exports.NONE_ENUM = exports.DEBUG_ENUM = exports.OPTIMIZED_ENUM = exports.CONSUMER_PARTIAL_ENUM = exports.CONSUMER_ENUM = exports.STANDALONE_ENUM = exports.DEDUPED = exports.DROPPED = exports.QUEUED = exports.CONSENT_UNKNOWN = exports.CONSENT_DECLINED = exports.CONSENT_GRANTED = exports.STORAGE_PLUGGABLE = exports.STORAGE_REDIS = exports.STORAGE_LOCALSTORAGE = exports.STORAGE_MEMORY = exports.CONSUMER_PARTIAL_MODE = exports.CONSUMER_MODE = exports.PRODUCER_MODE = exports.STANDALONE_MODE = exports.LOCALHOST_MODE = exports.NONE = exports.OPTIMIZED = exports.DEBUG = exports.SPLIT_EVENT = exports.SPLIT_IMPRESSION = exports.NA = exports.UNKNOWN = exports.CONTROL_WITH_CONFIG = exports.CONTROL = void 0;
|
|
4
|
+
exports.PAUSED = exports.ENABLED = exports.DISABLED = exports.NON_REQUESTED = exports.REQUESTED = exports.POLLING = exports.STREAMING = exports.AUTH_REJECTION = exports.SYNC_MODE_UPDATE = exports.ABLY_ERROR = exports.TOKEN_REFRESH = exports.SSE_CONNECTION_ERROR = exports.STREAMING_STATUS = void 0;
|
|
5
5
|
// Special treatments
|
|
6
6
|
exports.CONTROL = 'control';
|
|
7
7
|
exports.CONTROL_WITH_CONFIG = {
|
|
@@ -55,6 +55,10 @@ exports.TREATMENT = 't';
|
|
|
55
55
|
exports.TREATMENTS = 'ts';
|
|
56
56
|
exports.TREATMENT_WITH_CONFIG = 'tc';
|
|
57
57
|
exports.TREATMENTS_WITH_CONFIG = 'tcs';
|
|
58
|
+
exports.TREATMENTS_BY_FLAGSET = 'tf';
|
|
59
|
+
exports.TREATMENTS_BY_FLAGSETS = 'tfs';
|
|
60
|
+
exports.TREATMENTS_WITH_CONFIG_BY_FLAGSET = 'tcf';
|
|
61
|
+
exports.TREATMENTS_WITH_CONFIG_BY_FLAGSETS = 'tcfs';
|
|
58
62
|
exports.TRACK = 'tr';
|
|
59
63
|
exports.CONNECTION_ESTABLISHED = 0;
|
|
60
64
|
exports.OCCUPANCY_PRI = 10;
|
package/cjs/utils/lang/sets.js
CHANGED
|
@@ -24,7 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
24
24
|
THE SOFTWARE.
|
|
25
25
|
**/
|
|
26
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
-
exports._Set = exports.__getSetConstructor = exports.setToArray = exports.SetPoly = void 0;
|
|
27
|
+
exports.returnSetsUnion = exports._Set = exports.__getSetConstructor = exports.setToArray = exports.SetPoly = void 0;
|
|
28
28
|
var SetPoly = /** @class */ (function () {
|
|
29
29
|
// unlike ES6 `Set`, it only accepts an array as first argument iterable
|
|
30
30
|
function SetPoly(values) {
|
|
@@ -100,3 +100,11 @@ function __getSetConstructor() {
|
|
|
100
100
|
}
|
|
101
101
|
exports.__getSetConstructor = __getSetConstructor;
|
|
102
102
|
exports._Set = __getSetConstructor();
|
|
103
|
+
function returnSetsUnion(set, set2) {
|
|
104
|
+
var result = new exports._Set(setToArray(set));
|
|
105
|
+
set2.forEach(function (value) {
|
|
106
|
+
result.add(value);
|
|
107
|
+
});
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
exports.returnSetsUnion = returnSetsUnion;
|
|
@@ -179,7 +179,7 @@ function settingsValidation(config, validationParams) {
|
|
|
179
179
|
withDefaults.sync.enabled = true;
|
|
180
180
|
}
|
|
181
181
|
// validate the `splitFilters` settings and parse splits query
|
|
182
|
-
var splitFiltersValidation = (0, splitFilters_1.validateSplitFilters)(log, withDefaults.sync.splitFilters
|
|
182
|
+
var splitFiltersValidation = (0, splitFilters_1.validateSplitFilters)(log, withDefaults.sync.splitFilters);
|
|
183
183
|
withDefaults.sync.splitFilters = splitFiltersValidation.validFilters;
|
|
184
184
|
withDefaults.sync.__splitFiltersValidation = splitFiltersValidation;
|
|
185
185
|
// ensure a valid user consent value
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateSplitFilters = void 0;
|
|
4
|
-
var constants_1 = require("../constants");
|
|
3
|
+
exports.flagSetsAreValid = exports.validateSplitFilters = void 0;
|
|
5
4
|
var splits_1 = require("../inputValidation/splits");
|
|
6
|
-
var
|
|
5
|
+
var constants_1 = require("../../logger/constants");
|
|
6
|
+
var objectAssign_1 = require("../lang/objectAssign");
|
|
7
|
+
var lang_1 = require("../lang");
|
|
7
8
|
// Split filters metadata.
|
|
8
9
|
// Ordered according to their precedency when forming the filter query string: `&names=<values>&prefixes=<values>`
|
|
9
10
|
var FILTERS_METADATA = [
|
|
11
|
+
{
|
|
12
|
+
type: 'bySet',
|
|
13
|
+
maxLength: 50,
|
|
14
|
+
queryParam: 'sets='
|
|
15
|
+
},
|
|
10
16
|
{
|
|
11
17
|
type: 'byName',
|
|
12
18
|
maxLength: 400,
|
|
@@ -18,6 +24,8 @@ var FILTERS_METADATA = [
|
|
|
18
24
|
queryParam: 'prefixes='
|
|
19
25
|
}
|
|
20
26
|
];
|
|
27
|
+
var VALID_FLAGSET_REGEX = /^[a-z0-9][_a-z0-9]{0,49}$/;
|
|
28
|
+
var CAPITAL_LETTERS_REGEX = /[A-Z]/;
|
|
21
29
|
/**
|
|
22
30
|
* Validates that the given value is a valid filter type
|
|
23
31
|
*/
|
|
@@ -36,8 +44,11 @@ function validateFilterType(maybeFilterType) {
|
|
|
36
44
|
*/
|
|
37
45
|
function validateSplitFilter(log, type, values, maxLength) {
|
|
38
46
|
// validate and remove invalid and duplicated values
|
|
39
|
-
var result = (0, splits_1.validateSplits)(log, values,
|
|
47
|
+
var result = (0, splits_1.validateSplits)(log, values, constants_1.LOG_PREFIX_SETTINGS, type + " filter", type + " filter value");
|
|
40
48
|
if (result) {
|
|
49
|
+
if (type === 'bySet') {
|
|
50
|
+
result = sanitizeFlagSets(log, result);
|
|
51
|
+
}
|
|
41
52
|
// check max length
|
|
42
53
|
if (result.length > maxLength)
|
|
43
54
|
throw new Error(maxLength + " unique values can be specified at most for '" + type + "' filter. You passed " + result.length + ". Please consider reducing the amount or using other filter.");
|
|
@@ -68,12 +79,47 @@ function queryStringBuilder(groupedFilters) {
|
|
|
68
79
|
});
|
|
69
80
|
return queryParams.length > 0 ? '&' + queryParams.join('&') : null;
|
|
70
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Sanitizes set names list taking in account:
|
|
84
|
+
* - It should be lowercase
|
|
85
|
+
* - Must adhere the following regular expression /^[a-z0-9][_a-z0-9]{0,49}$/ that means
|
|
86
|
+
* - must start with a letter or number
|
|
87
|
+
* - Be in lowercase
|
|
88
|
+
* - Be alphanumeric
|
|
89
|
+
* - have a max length of 50 characters
|
|
90
|
+
*
|
|
91
|
+
* @param {ILogger} log
|
|
92
|
+
* @param {string[]} flagSets
|
|
93
|
+
* @returns sanitized list of set names
|
|
94
|
+
*/
|
|
95
|
+
function sanitizeFlagSets(log, flagSets) {
|
|
96
|
+
var sanitizedSets = flagSets
|
|
97
|
+
.map(function (flagSet) {
|
|
98
|
+
if (CAPITAL_LETTERS_REGEX.test(flagSet)) {
|
|
99
|
+
log.warn(constants_1.WARN_SPLITS_FILTER_LOWERCASE_SET, [flagSet]);
|
|
100
|
+
flagSet = flagSet.toLowerCase();
|
|
101
|
+
}
|
|
102
|
+
return flagSet;
|
|
103
|
+
})
|
|
104
|
+
.filter(function (flagSet) {
|
|
105
|
+
if (!VALID_FLAGSET_REGEX.test(flagSet)) {
|
|
106
|
+
log.warn(constants_1.WARN_SPLITS_FILTER_INVALID_SET, [flagSet, VALID_FLAGSET_REGEX, flagSet]);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
if (typeof flagSet !== 'string')
|
|
110
|
+
return false;
|
|
111
|
+
return true;
|
|
112
|
+
});
|
|
113
|
+
return (0, lang_1.uniq)(sanitizedSets);
|
|
114
|
+
}
|
|
115
|
+
function configuredFilter(validFilters, filterType) {
|
|
116
|
+
return (0, lang_1.find)(validFilters, function (filter) { return filter.type === filterType && filter.values.length > 0; });
|
|
117
|
+
}
|
|
71
118
|
/**
|
|
72
119
|
* Validates `splitFilters` configuration object and parses it into a query string for filtering splits on `/splitChanges` fetch.
|
|
73
120
|
*
|
|
74
121
|
* @param {ILogger} log logger
|
|
75
122
|
* @param {any} maybeSplitFilters split filters configuration param provided by the user
|
|
76
|
-
* @param {string} mode settings mode
|
|
77
123
|
* @returns it returns an object with the following properties:
|
|
78
124
|
* - `validFilters`: the validated `splitFilters` configuration object defined by the user.
|
|
79
125
|
* - `queryString`: the parsed split filter query. it is null if all filters are invalid or all values in filters are invalid.
|
|
@@ -81,24 +127,19 @@ function queryStringBuilder(groupedFilters) {
|
|
|
81
127
|
*
|
|
82
128
|
* @throws Error if the some of the grouped list of values per filter exceeds the max allowed length
|
|
83
129
|
*/
|
|
84
|
-
function validateSplitFilters(log, maybeSplitFilters
|
|
130
|
+
function validateSplitFilters(log, maybeSplitFilters) {
|
|
85
131
|
// Validation result schema
|
|
86
132
|
var res = {
|
|
87
133
|
validFilters: [],
|
|
88
134
|
queryString: null,
|
|
89
|
-
groupedFilters: { byName: [], byPrefix: [] }
|
|
135
|
+
groupedFilters: { bySet: [], byName: [], byPrefix: [] },
|
|
90
136
|
};
|
|
91
137
|
// do nothing if `splitFilters` param is not a non-empty array or mode is not STANDALONE
|
|
92
138
|
if (!maybeSplitFilters)
|
|
93
139
|
return res;
|
|
94
|
-
// Warn depending on the mode
|
|
95
|
-
if (mode !== constants_1.STANDALONE_MODE) {
|
|
96
|
-
log.warn(constants_2.WARN_SPLITS_FILTER_IGNORED, [constants_1.STANDALONE_MODE]);
|
|
97
|
-
return res;
|
|
98
|
-
}
|
|
99
140
|
// Check collection type
|
|
100
141
|
if (!Array.isArray(maybeSplitFilters) || maybeSplitFilters.length === 0) {
|
|
101
|
-
log.warn(
|
|
142
|
+
log.warn(constants_1.WARN_SPLITS_FILTER_EMPTY);
|
|
102
143
|
return res;
|
|
103
144
|
}
|
|
104
145
|
// Validate filters and group their values by filter type inside `groupedFilters` object
|
|
@@ -108,7 +149,7 @@ function validateSplitFilters(log, maybeSplitFilters, mode) {
|
|
|
108
149
|
return true;
|
|
109
150
|
}
|
|
110
151
|
else {
|
|
111
|
-
log.warn(
|
|
152
|
+
log.warn(constants_1.WARN_SPLITS_FILTER_INVALID, [index]);
|
|
112
153
|
}
|
|
113
154
|
return false;
|
|
114
155
|
});
|
|
@@ -118,9 +159,31 @@ function validateSplitFilters(log, maybeSplitFilters, mode) {
|
|
|
118
159
|
if (res.groupedFilters[type].length > 0)
|
|
119
160
|
res.groupedFilters[type] = validateSplitFilter(log, type, res.groupedFilters[type], maxLength);
|
|
120
161
|
});
|
|
162
|
+
var setFilter = configuredFilter(res.validFilters, 'bySet');
|
|
163
|
+
// Clean all filters if set filter is present
|
|
164
|
+
if (setFilter) {
|
|
165
|
+
if (configuredFilter(res.validFilters, 'byName') || configuredFilter(res.validFilters, 'byPrefix'))
|
|
166
|
+
log.error(constants_1.ERROR_SETS_FILTER_EXCLUSIVE);
|
|
167
|
+
(0, objectAssign_1.objectAssign)(res.groupedFilters, { byName: [], byPrefix: [] });
|
|
168
|
+
}
|
|
121
169
|
// build query string
|
|
122
170
|
res.queryString = queryStringBuilder(res.groupedFilters);
|
|
123
|
-
log.debug(
|
|
171
|
+
log.debug(constants_1.SETTINGS_SPLITS_FILTER, [res.queryString]);
|
|
124
172
|
return res;
|
|
125
173
|
}
|
|
126
174
|
exports.validateSplitFilters = validateSplitFilters;
|
|
175
|
+
function flagSetsAreValid(log, method, flagSets, flagSetsInConfig) {
|
|
176
|
+
var sets = (0, splits_1.validateSplits)(log, flagSets, method, 'flag sets', 'flag set');
|
|
177
|
+
var toReturn = sets ? sanitizeFlagSets(log, sets) : [];
|
|
178
|
+
if (flagSetsInConfig.length > 0) {
|
|
179
|
+
toReturn = toReturn.filter(function (flagSet) {
|
|
180
|
+
if (flagSetsInConfig.indexOf(flagSet) > -1) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
log.warn(constants_1.WARN_FLAGSET_NOT_CONFIGURED, [method, flagSet]);
|
|
184
|
+
return false;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return toReturn;
|
|
188
|
+
}
|
|
189
|
+
exports.flagSetsAreValid = flagSetsAreValid;
|
package/esm/evaluator/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Engine } from './Engine';
|
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
3
|
import * as LabelsConstants from '../utils/labels';
|
|
4
4
|
import { CONTROL } from '../utils/constants';
|
|
5
|
+
import { setToArray } from '../utils/lang/sets';
|
|
5
6
|
var treatmentException = {
|
|
6
7
|
treatment: CONTROL,
|
|
7
8
|
label: LabelsConstants.EXCEPTION,
|
|
@@ -49,6 +50,21 @@ export function evaluateFeatures(log, key, splitNames, attributes, storage) {
|
|
|
49
50
|
}) :
|
|
50
51
|
getEvaluations(log, splitNames, parsedSplits, key, attributes, storage);
|
|
51
52
|
}
|
|
53
|
+
export function evaluateFeaturesByFlagSets(log, key, flagSets, attributes, storage) {
|
|
54
|
+
var storedFlagNames;
|
|
55
|
+
// get features by flag sets
|
|
56
|
+
try {
|
|
57
|
+
storedFlagNames = storage.splits.getNamesByFlagSets(flagSets);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
// return empty evaluations
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
// evaluate related features
|
|
64
|
+
return thenable(storedFlagNames) ?
|
|
65
|
+
storedFlagNames.then(function (splitNames) { return evaluateFeatures(log, key, setToArray(splitNames), attributes, storage); }) :
|
|
66
|
+
evaluateFeatures(log, key, setToArray(storedFlagNames), attributes, storage);
|
|
67
|
+
}
|
|
52
68
|
function getEvaluation(log, splitJSON, key, attributes, storage) {
|
|
53
69
|
var evaluation = {
|
|
54
70
|
treatment: CONTROL,
|
package/esm/logger/constants.js
CHANGED
|
@@ -89,12 +89,14 @@ export var WARN_NOT_EXISTENT_SPLIT = 215;
|
|
|
89
89
|
export var WARN_LOWERCASE_TRAFFIC_TYPE = 216;
|
|
90
90
|
export var WARN_NOT_EXISTENT_TT = 217;
|
|
91
91
|
export var WARN_INTEGRATION_INVALID = 218;
|
|
92
|
-
export var WARN_SPLITS_FILTER_IGNORED = 219;
|
|
93
92
|
export var WARN_SPLITS_FILTER_INVALID = 220;
|
|
94
93
|
export var WARN_SPLITS_FILTER_EMPTY = 221;
|
|
95
94
|
export var WARN_SDK_KEY = 222;
|
|
96
95
|
export var STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 223;
|
|
97
96
|
export var STREAMING_PARSING_SPLIT_UPDATE = 224;
|
|
97
|
+
export var WARN_SPLITS_FILTER_INVALID_SET = 225;
|
|
98
|
+
export var WARN_SPLITS_FILTER_LOWERCASE_SET = 226;
|
|
99
|
+
export var WARN_FLAGSET_NOT_CONFIGURED = 227;
|
|
98
100
|
export var ERROR_ENGINE_COMBINER_IFELSEIF = 300;
|
|
99
101
|
export var ERROR_LOGLEVEL_INVALID = 301;
|
|
100
102
|
export var ERROR_CLIENT_LISTENER = 302;
|
|
@@ -122,6 +124,8 @@ export var ERROR_LOCALHOST_MODULE_REQUIRED = 323;
|
|
|
122
124
|
export var ERROR_STORAGE_INVALID = 324;
|
|
123
125
|
export var ERROR_NOT_BOOLEAN = 325;
|
|
124
126
|
export var ERROR_MIN_CONFIG_PARAM = 326;
|
|
127
|
+
export var ERROR_TOO_MANY_SETS = 327;
|
|
128
|
+
export var ERROR_SETS_FILTER_EXCLUSIVE = 328;
|
|
125
129
|
// Log prefixes (a.k.a. tags or categories)
|
|
126
130
|
export var LOG_PREFIX_SETTINGS = 'settings';
|
|
127
131
|
export var LOG_PREFIX_INSTANTIATION = 'Factory instantiation';
|
|
@@ -33,4 +33,6 @@ export var codesError = [
|
|
|
33
33
|
[c.ERROR_LOCALHOST_MODULE_REQUIRED, c.LOG_PREFIX_SETTINGS + ': an invalid value was received for "sync.localhostMode" config. A valid entity should be provided for localhost mode.'],
|
|
34
34
|
[c.ERROR_STORAGE_INVALID, c.LOG_PREFIX_SETTINGS + ': the provided storage is invalid.%s Falling back into default MEMORY storage'],
|
|
35
35
|
[c.ERROR_MIN_CONFIG_PARAM, c.LOG_PREFIX_SETTINGS + ': the provided "%s" config param is lower than allowed. Setting to the minimum value %s seconds'],
|
|
36
|
+
[c.ERROR_TOO_MANY_SETS, c.LOG_PREFIX_SETTINGS + ': the amount of flag sets provided are big causing uri length error.'],
|
|
37
|
+
[c.ERROR_SETS_FILTER_EXCLUSIVE, c.LOG_PREFIX_SETTINGS + ': the Set filter is exclusive and cannot be used simultaneously with names or prefix filters. Ignoring names and prefixes.'],
|
|
36
38
|
];
|
|
@@ -23,12 +23,14 @@ export var codesWarn = codesError.concat([
|
|
|
23
23
|
[c.WARN_NOT_EXISTENT_SPLIT, '%s: feature flag "%s" does not exist in this environment. Please double check what feature flags exist in the Split user interface.'],
|
|
24
24
|
[c.WARN_LOWERCASE_TRAFFIC_TYPE, '%s: traffic_type_name should be all lowercase - converting string to lowercase.'],
|
|
25
25
|
[c.WARN_NOT_EXISTENT_TT, '%s: traffic type "%s" does not have any corresponding feature flag in this environment, make sure you\'re tracking your events to a valid traffic type defined in the Split user interface.'],
|
|
26
|
+
[c.WARN_FLAGSET_NOT_CONFIGURED, '%s: : you passed %s wich is not part of the configured FlagSetsFilter, ignoring Flag Set.'],
|
|
26
27
|
// initialization / settings validation
|
|
27
28
|
[c.WARN_INTEGRATION_INVALID, c.LOG_PREFIX_SETTINGS + ': %s integration item(s) at settings is invalid. %s'],
|
|
28
|
-
[c.
|
|
29
|
-
[c.WARN_SPLITS_FILTER_INVALID, c.LOG_PREFIX_SETTINGS + ': feature flag filter at position %s is invalid. It must be an object with a valid filter type ("byName" or "byPrefix") and a list of "values".'],
|
|
29
|
+
[c.WARN_SPLITS_FILTER_INVALID, c.LOG_PREFIX_SETTINGS + ': feature flag filter at position %s is invalid. It must be an object with a valid filter type ("bySet", "byName" or "byPrefix") and a list of "values".'],
|
|
30
30
|
[c.WARN_SPLITS_FILTER_EMPTY, c.LOG_PREFIX_SETTINGS + ': feature flag filter configuration must be a non-empty array of filter objects.'],
|
|
31
31
|
[c.WARN_SDK_KEY, c.LOG_PREFIX_SETTINGS + ': You already have %s. We recommend keeping only one instance of the factory at all times (Singleton pattern) and reusing it throughout your application'],
|
|
32
32
|
[c.STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching MySegments due to an error processing %s notification: %s'],
|
|
33
33
|
[c.STREAMING_PARSING_SPLIT_UPDATE, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching SplitChanges due to an error processing SPLIT_UPDATE notification: %s'],
|
|
34
|
+
[c.WARN_SPLITS_FILTER_INVALID_SET, c.LOG_PREFIX_SETTINGS + ': you passed %s, flag set must adhere to the regular expressions %s. This means a flag set must start with a letter or number, be in lowercase, alphanumeric and have a max length of 50 characters. %s was discarded.'],
|
|
35
|
+
[c.WARN_SPLITS_FILTER_LOWERCASE_SET, c.LOG_PREFIX_SETTINGS + ': flag set %s should be all lowercase - converting string to lowercase.'],
|
|
34
36
|
]);
|
package/esm/sdkClient/client.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { evaluateFeature, evaluateFeatures } from '../evaluator';
|
|
1
|
+
import { evaluateFeature, evaluateFeatures, evaluateFeaturesByFlagSets } from '../evaluator';
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
3
|
import { getMatching, getBucketing } from '../utils/key';
|
|
4
4
|
import { validateSplitExistance } from '../utils/inputValidation/splitExistance';
|
|
5
5
|
import { validateTrafficTypeExistance } from '../utils/inputValidation/trafficTypeExistance';
|
|
6
6
|
import { SDK_NOT_READY } from '../utils/labels';
|
|
7
|
-
import { CONTROL, TREATMENT, TREATMENTS, TREATMENT_WITH_CONFIG, TREATMENTS_WITH_CONFIG, TRACK } from '../utils/constants';
|
|
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';
|
|
9
9
|
import { isStorageSync } from '../trackers/impressionObserver/utils';
|
|
10
10
|
var treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
|
|
@@ -64,6 +64,35 @@ export function clientFactory(params) {
|
|
|
64
64
|
function getTreatmentsWithConfig(key, featureFlagNames, attributes) {
|
|
65
65
|
return getTreatments(key, featureFlagNames, attributes, true);
|
|
66
66
|
}
|
|
67
|
+
function getTreatmentsByFlagSets(key, flagSetNames, attributes, withConfig, method) {
|
|
68
|
+
if (withConfig === void 0) { withConfig = false; }
|
|
69
|
+
if (method === void 0) { method = TREATMENTS_BY_FLAGSETS; }
|
|
70
|
+
var stopTelemetryTracker = telemetryTracker.trackEval(method);
|
|
71
|
+
var wrapUp = function (evaluationResults) {
|
|
72
|
+
var queue = [];
|
|
73
|
+
var treatments = {};
|
|
74
|
+
var evaluations = evaluationResults;
|
|
75
|
+
Object.keys(evaluations).forEach(function (featureFlagName) {
|
|
76
|
+
treatments[featureFlagName] = processEvaluation(evaluations[featureFlagName], featureFlagName, key, attributes, withConfig, "getTreatmentsByFlagSets" + (withConfig ? 'WithConfig' : ''), queue);
|
|
77
|
+
});
|
|
78
|
+
impressionsTracker.track(queue, attributes);
|
|
79
|
+
stopTelemetryTracker(queue[0] && queue[0].label);
|
|
80
|
+
return treatments;
|
|
81
|
+
};
|
|
82
|
+
var evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
|
|
83
|
+
evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage) :
|
|
84
|
+
isStorageSync(settings) ? {} : Promise.resolve({}); // Promisify if async
|
|
85
|
+
return thenable(evaluations) ? evaluations.then(function (res) { return wrapUp(res); }) : wrapUp(evaluations);
|
|
86
|
+
}
|
|
87
|
+
function getTreatmentsWithConfigByFlagSets(key, flagSetNames, attributes) {
|
|
88
|
+
return getTreatmentsByFlagSets(key, flagSetNames, attributes, true, TREATMENTS_WITH_CONFIG_BY_FLAGSETS);
|
|
89
|
+
}
|
|
90
|
+
function getTreatmentsByFlagSet(key, flagSetName, attributes) {
|
|
91
|
+
return getTreatmentsByFlagSets(key, [flagSetName], attributes, false, TREATMENTS_BY_FLAGSET);
|
|
92
|
+
}
|
|
93
|
+
function getTreatmentsWithConfigByFlagSet(key, flagSetName, attributes) {
|
|
94
|
+
return getTreatmentsByFlagSets(key, [flagSetName], attributes, true, TREATMENTS_WITH_CONFIG_BY_FLAGSET);
|
|
95
|
+
}
|
|
67
96
|
// Internal function
|
|
68
97
|
function processEvaluation(evaluation, featureFlagName, key, attributes, withConfig, invokingMethodName, queue) {
|
|
69
98
|
var matchingKey = getMatching(key);
|
|
@@ -122,6 +151,10 @@ export function clientFactory(params) {
|
|
|
122
151
|
getTreatmentWithConfig: getTreatmentWithConfig,
|
|
123
152
|
getTreatments: getTreatments,
|
|
124
153
|
getTreatmentsWithConfig: getTreatmentsWithConfig,
|
|
154
|
+
getTreatmentsByFlagSets: getTreatmentsByFlagSets,
|
|
155
|
+
getTreatmentsWithConfigByFlagSets: getTreatmentsWithConfigByFlagSets,
|
|
156
|
+
getTreatmentsByFlagSet: getTreatmentsByFlagSet,
|
|
157
|
+
getTreatmentsWithConfigByFlagSet: getTreatmentsWithConfigByFlagSet,
|
|
125
158
|
track: track,
|
|
126
159
|
isClientSide: false
|
|
127
160
|
};
|