@splitsoftware/splitio-commons 1.9.1-rc.0 → 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.
- package/CHANGES.txt +5 -2
- package/cjs/evaluator/index.js +18 -1
- package/cjs/integrations/ga/GaToSplit.js +1 -1
- package/cjs/listeners/browser.js +3 -13
- package/cjs/logger/constants.js +7 -2
- package/cjs/logger/messages/error.js +2 -0
- package/cjs/logger/messages/warn.js +3 -1
- package/cjs/myLogger.js +34 -0
- package/cjs/sdkClient/client.js +33 -0
- package/cjs/sdkClient/clientAttributesDecoration.js +20 -0
- package/cjs/sdkClient/clientCS.js +5 -4
- package/cjs/sdkClient/clientInputValidation.js +52 -3
- package/cjs/sdkManager/index.js +2 -1
- 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 +24 -4
- package/cjs/sync/streaming/parseUtils.js +1 -0
- 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/splitFilters.js +77 -2
- package/esm/evaluator/index.js +16 -0
- package/esm/integrations/ga/GaToSplit.js +1 -1
- package/esm/listeners/browser.js +3 -13
- package/esm/logger/constants.js +5 -0
- package/esm/logger/messages/error.js +2 -0
- package/esm/logger/messages/warn.js +3 -1
- package/esm/myLogger.js +31 -0
- package/esm/sdkClient/client.js +35 -2
- package/esm/sdkClient/clientAttributesDecoration.js +20 -0
- package/esm/sdkClient/clientCS.js +5 -4
- package/esm/sdkClient/clientInputValidation.js +52 -3
- package/esm/sdkManager/index.js +2 -1
- 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 +24 -4
- package/esm/sync/streaming/parseUtils.js +1 -0
- 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/splitFilters.js +76 -2
- package/package.json +6 -6
- package/src/dtos/types.ts +3 -2
- package/src/evaluator/index.ts +24 -0
- package/src/integrations/ga/GaToSplit.ts +1 -1
- package/src/listeners/browser.ts +3 -13
- package/src/logger/constants.ts +5 -0
- package/src/logger/messages/error.ts +2 -0
- package/src/logger/messages/warn.ts +3 -1
- package/src/myLogger.ts +36 -0
- package/src/sdkClient/client.ts +42 -2
- package/src/sdkClient/clientAttributesDecoration.ts +24 -0
- package/src/sdkClient/clientCS.ts +5 -4
- package/src/sdkClient/clientInputValidation.ts +56 -4
- package/src/sdkManager/index.ts +2 -1
- 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/inMemory/TelemetryCacheInMemory.ts +1 -1
- package/src/storages/inRedis/RedisAdapter.ts +1 -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 +27 -4
- package/src/sync/streaming/parseUtils.ts +1 -0
- package/src/sync/submitters/telemetrySubmitter.ts +19 -2
- package/src/sync/submitters/types.ts +7 -1
- package/src/types.ts +118 -1
- package/src/utils/constants/index.ts +4 -0
- package/src/utils/lang/sets.ts +8 -0
- package/src/utils/redis/RedisMock.ts +1 -1
- package/src/utils/settingsValidation/splitFilters.ts +82 -2
- package/types/dtos/types.d.ts +1 -0
- package/types/evaluator/index.d.ts +1 -0
- package/types/listeners/browser.d.ts +0 -1
- package/types/logger/constants.d.ts +5 -0
- 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 +118 -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 +1 -0
- 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
|
@@ -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;
|
package/esm/utils/lang/sets.js
CHANGED
|
@@ -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
|
+
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { STANDALONE_MODE } from '../constants';
|
|
2
2
|
import { validateSplits } from '../inputValidation/splits';
|
|
3
|
-
import { WARN_SPLITS_FILTER_IGNORED, WARN_SPLITS_FILTER_EMPTY, WARN_SPLITS_FILTER_INVALID, SETTINGS_SPLITS_FILTER, LOG_PREFIX_SETTINGS } from '../../logger/constants';
|
|
3
|
+
import { WARN_SPLITS_FILTER_IGNORED, WARN_SPLITS_FILTER_EMPTY, WARN_SPLITS_FILTER_INVALID, SETTINGS_SPLITS_FILTER, LOG_PREFIX_SETTINGS, ERROR_SETS_FILTER_EXCLUSIVE, WARN_SPLITS_FILTER_LOWERCASE_SET, WARN_SPLITS_FILTER_INVALID_SET, ERROR_EMPTY_ARRAY, WARN_FLAGSET_NOT_CONFIGURED } from '../../logger/constants';
|
|
4
|
+
import { objectAssign } from '../lang/objectAssign';
|
|
5
|
+
import { find, uniq } from '../lang';
|
|
4
6
|
// Split filters metadata.
|
|
5
7
|
// Ordered according to their precedency when forming the filter query string: `&names=<values>&prefixes=<values>`
|
|
6
8
|
var FILTERS_METADATA = [
|
|
9
|
+
{
|
|
10
|
+
type: 'bySet',
|
|
11
|
+
maxLength: 50,
|
|
12
|
+
queryParam: 'sets='
|
|
13
|
+
},
|
|
7
14
|
{
|
|
8
15
|
type: 'byName',
|
|
9
16
|
maxLength: 400,
|
|
@@ -15,6 +22,8 @@ var FILTERS_METADATA = [
|
|
|
15
22
|
queryParam: 'prefixes='
|
|
16
23
|
}
|
|
17
24
|
];
|
|
25
|
+
var VALID_FLAGSET_REGEX = /^[a-z0-9][_a-z0-9]{0,49}$/;
|
|
26
|
+
var CAPITAL_LETTERS_REGEX = /[A-Z]/;
|
|
18
27
|
/**
|
|
19
28
|
* Validates that the given value is a valid filter type
|
|
20
29
|
*/
|
|
@@ -35,6 +44,9 @@ function validateSplitFilter(log, type, values, maxLength) {
|
|
|
35
44
|
// validate and remove invalid and duplicated values
|
|
36
45
|
var result = validateSplits(log, values, LOG_PREFIX_SETTINGS, type + " filter", type + " filter value");
|
|
37
46
|
if (result) {
|
|
47
|
+
if (type === 'bySet') {
|
|
48
|
+
result = sanitizeFlagSets(log, result);
|
|
49
|
+
}
|
|
38
50
|
// check max length
|
|
39
51
|
if (result.length > maxLength)
|
|
40
52
|
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.");
|
|
@@ -65,6 +77,42 @@ function queryStringBuilder(groupedFilters) {
|
|
|
65
77
|
});
|
|
66
78
|
return queryParams.length > 0 ? '&' + queryParams.join('&') : null;
|
|
67
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Sanitizes set names list taking in account:
|
|
82
|
+
* - It should be lowercase
|
|
83
|
+
* - Must adhere the following regular expression /^[a-z0-9][_a-z0-9]{0,49}$/ that means
|
|
84
|
+
* - must start with a letter or number
|
|
85
|
+
* - Be in lowercase
|
|
86
|
+
* - Be alphanumeric
|
|
87
|
+
* - have a max length of 50 characters
|
|
88
|
+
*
|
|
89
|
+
* @param {ILogger} log
|
|
90
|
+
* @param {string[]} flagSets
|
|
91
|
+
* @returns sanitized list of set names
|
|
92
|
+
*/
|
|
93
|
+
function sanitizeFlagSets(log, flagSets) {
|
|
94
|
+
var sanitizedSets = flagSets
|
|
95
|
+
.map(function (flagSet) {
|
|
96
|
+
if (CAPITAL_LETTERS_REGEX.test(flagSet)) {
|
|
97
|
+
log.warn(WARN_SPLITS_FILTER_LOWERCASE_SET, [flagSet]);
|
|
98
|
+
flagSet = flagSet.toLowerCase();
|
|
99
|
+
}
|
|
100
|
+
return flagSet;
|
|
101
|
+
})
|
|
102
|
+
.filter(function (flagSet) {
|
|
103
|
+
if (!VALID_FLAGSET_REGEX.test(flagSet)) {
|
|
104
|
+
log.warn(WARN_SPLITS_FILTER_INVALID_SET, [flagSet, VALID_FLAGSET_REGEX, flagSet]);
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
if (typeof flagSet !== 'string')
|
|
108
|
+
return false;
|
|
109
|
+
return true;
|
|
110
|
+
});
|
|
111
|
+
return uniq(sanitizedSets);
|
|
112
|
+
}
|
|
113
|
+
function configuredFilter(validFilters, filterType) {
|
|
114
|
+
return find(validFilters, function (filter) { return filter.type === filterType && filter.values.length > 0; });
|
|
115
|
+
}
|
|
68
116
|
/**
|
|
69
117
|
* Validates `splitFilters` configuration object and parses it into a query string for filtering splits on `/splitChanges` fetch.
|
|
70
118
|
*
|
|
@@ -83,7 +131,7 @@ export function validateSplitFilters(log, maybeSplitFilters, mode) {
|
|
|
83
131
|
var res = {
|
|
84
132
|
validFilters: [],
|
|
85
133
|
queryString: null,
|
|
86
|
-
groupedFilters: { byName: [], byPrefix: [] }
|
|
134
|
+
groupedFilters: { bySet: [], byName: [], byPrefix: [] },
|
|
87
135
|
};
|
|
88
136
|
// do nothing if `splitFilters` param is not a non-empty array or mode is not STANDALONE
|
|
89
137
|
if (!maybeSplitFilters)
|
|
@@ -115,8 +163,34 @@ export function validateSplitFilters(log, maybeSplitFilters, mode) {
|
|
|
115
163
|
if (res.groupedFilters[type].length > 0)
|
|
116
164
|
res.groupedFilters[type] = validateSplitFilter(log, type, res.groupedFilters[type], maxLength);
|
|
117
165
|
});
|
|
166
|
+
var setFilter = configuredFilter(res.validFilters, 'bySet');
|
|
167
|
+
// Clean all filters if set filter is present
|
|
168
|
+
if (setFilter) {
|
|
169
|
+
if (configuredFilter(res.validFilters, 'byName') || configuredFilter(res.validFilters, 'byPrefix'))
|
|
170
|
+
log.error(ERROR_SETS_FILTER_EXCLUSIVE);
|
|
171
|
+
objectAssign(res.groupedFilters, { byName: [], byPrefix: [] });
|
|
172
|
+
}
|
|
118
173
|
// build query string
|
|
119
174
|
res.queryString = queryStringBuilder(res.groupedFilters);
|
|
120
175
|
log.debug(SETTINGS_SPLITS_FILTER, [res.queryString]);
|
|
121
176
|
return res;
|
|
122
177
|
}
|
|
178
|
+
export function flagSetsAreValid(log, method, flagSets, flagSetsInConfig) {
|
|
179
|
+
var toReturn = [];
|
|
180
|
+
if (flagSets.length === 0) {
|
|
181
|
+
log.error(ERROR_EMPTY_ARRAY, [method, 'flagSets']);
|
|
182
|
+
return toReturn;
|
|
183
|
+
}
|
|
184
|
+
var sets = validateSplits(log, flagSets, method, 'flag sets', 'flag set');
|
|
185
|
+
toReturn = sets ? sanitizeFlagSets(log, sets) : [];
|
|
186
|
+
if (flagSetsInConfig.length > 0) {
|
|
187
|
+
toReturn = toReturn.filter(function (flagSet) {
|
|
188
|
+
if (flagSetsInConfig.indexOf(flagSet) > -1) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
log.warn(WARN_FLAGSET_NOT_CONFIGURED, [flagSet]);
|
|
192
|
+
return false;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return toReturn;
|
|
196
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitsoftware/splitio-commons",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.2-rc.0",
|
|
4
4
|
"description": "Split Javascript SDK common components",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"build": "npm run build:cjs && npm run build:esm",
|
|
23
23
|
"build:esm": "rimraf esm && tsc -m es2015 --outDir esm -d true --declarationDir types",
|
|
24
24
|
"build:cjs": "rimraf cjs && tsc -m CommonJS --outDir cjs",
|
|
25
|
-
"test": "jest
|
|
25
|
+
"test": "jest",
|
|
26
26
|
"test:coverage": "jest --coverage",
|
|
27
27
|
"all": "npm run check && npm run build && npm run test",
|
|
28
28
|
"publish:rc": "npm run check && npm run test && npm run build && npm publish --tag rc",
|
|
@@ -60,12 +60,12 @@
|
|
|
60
60
|
"@types/ioredis": "^4.28.0",
|
|
61
61
|
"@types/jest": "^27.0.0",
|
|
62
62
|
"@types/lodash": "^4.14.162",
|
|
63
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
64
|
-
"@typescript-eslint/parser": "^
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "^6.6.0",
|
|
64
|
+
"@typescript-eslint/parser": "^6.6.0",
|
|
65
65
|
"cross-env": "^7.0.2",
|
|
66
66
|
"csv-streamify": "^4.0.0",
|
|
67
|
-
"eslint": "^
|
|
68
|
-
"eslint-plugin-compat": "
|
|
67
|
+
"eslint": "^8.48.0",
|
|
68
|
+
"eslint-plugin-compat": "^4.2.0",
|
|
69
69
|
"eslint-plugin-import": "^2.25.3",
|
|
70
70
|
"fetch-mock": "^9.11.0",
|
|
71
71
|
"ioredis": "^4.28.0",
|
package/src/dtos/types.ts
CHANGED
|
@@ -163,7 +163,8 @@ export interface ISplit {
|
|
|
163
163
|
trafficAllocationSeed?: number
|
|
164
164
|
configurations?: {
|
|
165
165
|
[treatmentName: string]: string
|
|
166
|
-
}
|
|
166
|
+
},
|
|
167
|
+
sets?: string[]
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
// Split definition used in offline mode
|
|
@@ -208,5 +209,5 @@ export interface IMetadata {
|
|
|
208
209
|
export type ISplitFiltersValidation = {
|
|
209
210
|
queryString: string | null,
|
|
210
211
|
groupedFilters: Record<SplitIO.SplitFilterType, string[]>,
|
|
211
|
-
validFilters: SplitIO.SplitFilter[]
|
|
212
|
+
validFilters: SplitIO.SplitFilter[],
|
|
212
213
|
};
|
package/src/evaluator/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { IStorageAsync, IStorageSync } from '../storages/types';
|
|
|
7
7
|
import { IEvaluationResult } from './types';
|
|
8
8
|
import { SplitIO } from '../types';
|
|
9
9
|
import { ILogger } from '../logger/types';
|
|
10
|
+
import { ISet, setToArray } from '../utils/lang/sets';
|
|
10
11
|
|
|
11
12
|
const treatmentException = {
|
|
12
13
|
treatment: CONTROL,
|
|
@@ -87,6 +88,29 @@ export function evaluateFeatures(
|
|
|
87
88
|
getEvaluations(log, splitNames, parsedSplits, key, attributes, storage);
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
export function evaluateFeaturesByFlagSets(
|
|
92
|
+
log: ILogger,
|
|
93
|
+
key: SplitIO.SplitKey,
|
|
94
|
+
flagSets: string[],
|
|
95
|
+
attributes: SplitIO.Attributes | undefined,
|
|
96
|
+
storage: IStorageSync | IStorageAsync,
|
|
97
|
+
): MaybeThenable<Record<string,IEvaluationResult>> {
|
|
98
|
+
let storedFlagNames: MaybeThenable<ISet<string>>;
|
|
99
|
+
|
|
100
|
+
// get features by flag sets
|
|
101
|
+
try {
|
|
102
|
+
storedFlagNames = storage.splits.getNamesByFlagSets(flagSets);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
// return empty evaluations
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// evaluate related features
|
|
109
|
+
return thenable(storedFlagNames) ?
|
|
110
|
+
storedFlagNames.then((splitNames) => evaluateFeatures(log, key, setToArray(splitNames), attributes, storage)) :
|
|
111
|
+
evaluateFeatures(log, key, setToArray(storedFlagNames), attributes, storage);
|
|
112
|
+
}
|
|
113
|
+
|
|
90
114
|
function getEvaluation(
|
|
91
115
|
log: ILogger,
|
|
92
116
|
splitJSON: ISplit | null,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-undef */
|
|
2
1
|
import { objectAssign } from '../../utils/lang/objectAssign';
|
|
3
2
|
import { isString, isFiniteNumber, uniqAsStrings } from '../../utils/lang';
|
|
4
3
|
import {
|
|
@@ -295,5 +294,6 @@ export function GaToSplit(sdkOptions: GoogleAnalyticsToSplitOptions, params: IIn
|
|
|
295
294
|
}
|
|
296
295
|
|
|
297
296
|
// Register the plugin, even if config is invalid, since, if not provided, it will block `ga` command queue.
|
|
297
|
+
// eslint-disable-next-line no-undef
|
|
298
298
|
providePlugin(window, 'splitTracker', SplitTracker, log, sdkOptions.autoRequire === true, telemetryTracker);
|
|
299
299
|
}
|
package/src/listeners/browser.ts
CHANGED
|
@@ -15,8 +15,7 @@ import { isConsentGranted } from '../consent';
|
|
|
15
15
|
|
|
16
16
|
const VISIBILITYCHANGE_EVENT = 'visibilitychange';
|
|
17
17
|
const PAGEHIDE_EVENT = 'pagehide';
|
|
18
|
-
const
|
|
19
|
-
const EVENT_NAME = 'for unload page event.';
|
|
18
|
+
const EVENT_NAME = 'for visibilitychange and pagehide events.';
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* We'll listen for events over the window object.
|
|
@@ -33,7 +32,6 @@ export class BrowserSignalListener implements ISignalListener {
|
|
|
33
32
|
) {
|
|
34
33
|
this.flushData = this.flushData.bind(this);
|
|
35
34
|
this.flushDataIfHidden = this.flushDataIfHidden.bind(this);
|
|
36
|
-
this.stopSync = this.stopSync.bind(this);
|
|
37
35
|
this.fromImpressionsCollector = fromImpressionsCollector.bind(undefined, settings.core.labelsEnabled);
|
|
38
36
|
}
|
|
39
37
|
|
|
@@ -48,11 +46,9 @@ export class BrowserSignalListener implements ISignalListener {
|
|
|
48
46
|
document.addEventListener(VISIBILITYCHANGE_EVENT, this.flushDataIfHidden);
|
|
49
47
|
}
|
|
50
48
|
if (typeof window !== 'undefined' && window.addEventListener) {
|
|
51
|
-
// Some browsers like Safari does not fire the `visibilitychange` event when the page is being unloaded.
|
|
52
|
-
// If both events are triggered, the
|
|
49
|
+
// Some browsers, like Safari, does not fire the `visibilitychange` event when the page is being unloaded. Therefore, we also flush data in the `pagehide` event.
|
|
50
|
+
// If both events are triggered, the latter will find the storage empty, so no duplicate data will be submitted.
|
|
53
51
|
window.addEventListener(PAGEHIDE_EVENT, this.flushData);
|
|
54
|
-
// Stop streaming on 'unload' event. Used instead of 'beforeunload', because 'unload' is not a cancelable event, so no other listeners can stop the event from occurring.
|
|
55
|
-
window.addEventListener(UNLOAD_EVENT, this.stopSync);
|
|
56
52
|
}
|
|
57
53
|
}
|
|
58
54
|
|
|
@@ -67,15 +63,9 @@ export class BrowserSignalListener implements ISignalListener {
|
|
|
67
63
|
}
|
|
68
64
|
if (typeof window !== 'undefined' && window.removeEventListener) {
|
|
69
65
|
window.removeEventListener(PAGEHIDE_EVENT, this.flushData);
|
|
70
|
-
window.removeEventListener(UNLOAD_EVENT, this.stopSync);
|
|
71
66
|
}
|
|
72
67
|
}
|
|
73
68
|
|
|
74
|
-
stopSync() {
|
|
75
|
-
// Close streaming connection
|
|
76
|
-
if (this.syncManager && this.syncManager.pushManager) this.syncManager.pushManager.stop();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
69
|
/**
|
|
80
70
|
* flushData method.
|
|
81
71
|
* Called when pagehide event is triggered. It flushed remaining impressions and events to the backend,
|
package/src/logger/constants.ts
CHANGED
|
@@ -97,6 +97,9 @@ export const WARN_SPLITS_FILTER_EMPTY = 221;
|
|
|
97
97
|
export const WARN_SDK_KEY = 222;
|
|
98
98
|
export const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 223;
|
|
99
99
|
export const STREAMING_PARSING_SPLIT_UPDATE = 224;
|
|
100
|
+
export const WARN_SPLITS_FILTER_INVALID_SET = 225;
|
|
101
|
+
export const WARN_SPLITS_FILTER_LOWERCASE_SET = 226;
|
|
102
|
+
export const WARN_FLAGSET_NOT_CONFIGURED = 227;
|
|
100
103
|
|
|
101
104
|
export const ERROR_ENGINE_COMBINER_IFELSEIF = 300;
|
|
102
105
|
export const ERROR_LOGLEVEL_INVALID = 301;
|
|
@@ -125,6 +128,8 @@ export const ERROR_LOCALHOST_MODULE_REQUIRED = 323;
|
|
|
125
128
|
export const ERROR_STORAGE_INVALID = 324;
|
|
126
129
|
export const ERROR_NOT_BOOLEAN = 325;
|
|
127
130
|
export const ERROR_MIN_CONFIG_PARAM = 326;
|
|
131
|
+
export const ERROR_TOO_MANY_SETS = 327;
|
|
132
|
+
export const ERROR_SETS_FILTER_EXCLUSIVE = 328;
|
|
128
133
|
|
|
129
134
|
// Log prefixes (a.k.a. tags or categories)
|
|
130
135
|
export const LOG_PREFIX_SETTINGS = 'settings';
|
|
@@ -34,4 +34,6 @@ export const codesError: [number, string][] = [
|
|
|
34
34
|
[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.'],
|
|
35
35
|
[c.ERROR_STORAGE_INVALID, c.LOG_PREFIX_SETTINGS+': the provided storage is invalid.%s Falling back into default MEMORY storage'],
|
|
36
36
|
[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'],
|
|
37
|
+
[c.ERROR_TOO_MANY_SETS, c.LOG_PREFIX_SETTINGS + ': the amount of flag sets provided are big causing uri length error.'],
|
|
38
|
+
[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.'],
|
|
37
39
|
];
|
|
@@ -27,10 +27,12 @@ export const codesWarn: [number, string][] = codesError.concat([
|
|
|
27
27
|
// initialization / settings validation
|
|
28
28
|
[c.WARN_INTEGRATION_INVALID, c.LOG_PREFIX_SETTINGS+': %s integration item(s) at settings is invalid. %s'],
|
|
29
29
|
[c.WARN_SPLITS_FILTER_IGNORED, c.LOG_PREFIX_SETTINGS+': feature flag filters have been configured but will have no effect if mode is not "%s", since synchronization is being deferred to an external tool.'],
|
|
30
|
-
[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".'],
|
|
30
|
+
[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".'],
|
|
31
31
|
[c.WARN_SPLITS_FILTER_EMPTY, c.LOG_PREFIX_SETTINGS+': feature flag filter configuration must be a non-empty array of filter objects.'],
|
|
32
32
|
[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'],
|
|
33
33
|
|
|
34
34
|
[c.STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching MySegments due to an error processing %s notification: %s'],
|
|
35
35
|
[c.STREAMING_PARSING_SPLIT_UPDATE, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching SplitChanges due to an error processing SPLIT_UPDATE notification: %s'],
|
|
36
|
+
[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.'],
|
|
37
|
+
[c.WARN_SPLITS_FILTER_LOWERCASE_SET, c.LOG_PREFIX_SETTINGS+': flag set %s should be all lowercase - converting string to lowercase.'],
|
|
36
38
|
]);
|
package/src/myLogger.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// get the Console class
|
|
2
|
+
// const { Console } = require('console');
|
|
3
|
+
// get fs module for creating write streams
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
var pjson = require('../package.json');
|
|
6
|
+
|
|
7
|
+
//@ts-ignore
|
|
8
|
+
const pad = (value) => ('00'+value).slice(-2);
|
|
9
|
+
const getDate = () => {
|
|
10
|
+
const date = new Date();
|
|
11
|
+
return `${date.getFullYear()}-${pad(date.getMonth()+1)}-${pad(date.getDate())}_${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
//@ts-ignore
|
|
15
|
+
|
|
16
|
+
const logToFile = (file, msg) => {
|
|
17
|
+
//@ts-ignore
|
|
18
|
+
fs.appendFileSync(file, `${msg}\n`, (err) => {
|
|
19
|
+
if (err) {
|
|
20
|
+
return console.log(err);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// make a new logger
|
|
26
|
+
const version = pjson.version.replace('.','').replace('.','');
|
|
27
|
+
const filePath = `logs/${version}_${getDate()}`;
|
|
28
|
+
export const myLogger = {
|
|
29
|
+
//@ts-ignore
|
|
30
|
+
log: (msg) => logToFile(filePath, msg),
|
|
31
|
+
//@ts-ignore
|
|
32
|
+
logCsv: (msg) => logToFile(`${filePath}.csv`, msg),
|
|
33
|
+
//@ts-ignore
|
|
34
|
+
logToFile: (file, msg) => logToFile(`logs/${file}`, msg),
|
|
35
|
+
};
|
|
36
|
+
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
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 { IEvaluationResult } from '../evaluator/types';
|
|
9
9
|
import { SplitIO, ImpressionDTO } from '../types';
|
|
10
10
|
import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
|
|
11
11
|
import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
12
12
|
import { isStorageSync } from '../trackers/impressionObserver/utils';
|
|
13
|
+
import { Method } from '../sync/submitters/types';
|
|
13
14
|
|
|
14
15
|
const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
|
|
15
16
|
|
|
@@ -81,6 +82,41 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
81
82
|
return getTreatments(key, featureFlagNames, attributes, true);
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
function getTreatmentsByFlagSets(key: SplitIO.SplitKey, flagSetNames: string[], attributes: SplitIO.Attributes | undefined, withConfig = false, method: Method = TREATMENTS_BY_FLAGSETS) {
|
|
86
|
+
const stopTelemetryTracker = telemetryTracker.trackEval(method);
|
|
87
|
+
|
|
88
|
+
const wrapUp = (evaluationResults: Record<string,IEvaluationResult>) => {
|
|
89
|
+
const queue: ImpressionDTO[] = [];
|
|
90
|
+
const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
|
|
91
|
+
const evaluations = evaluationResults;
|
|
92
|
+
Object.keys(evaluations).forEach(featureFlagName => {
|
|
93
|
+
treatments[featureFlagName] = processEvaluation(evaluations[featureFlagName], featureFlagName, key, attributes, withConfig, `getTreatmentsByFlagSets${withConfig ? 'WithConfig' : ''}`, queue);
|
|
94
|
+
});
|
|
95
|
+
impressionsTracker.track(queue, attributes);
|
|
96
|
+
|
|
97
|
+
stopTelemetryTracker(queue[0] && queue[0].label);
|
|
98
|
+
return treatments;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
|
|
102
|
+
evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage) :
|
|
103
|
+
isStorageSync(settings) ? {} : Promise.resolve({}); // Promisify if async
|
|
104
|
+
|
|
105
|
+
return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getTreatmentsWithConfigByFlagSets(key: SplitIO.SplitKey, flagSetNames: string[], attributes: SplitIO.Attributes | undefined) {
|
|
109
|
+
return getTreatmentsByFlagSets(key, flagSetNames, attributes, true, TREATMENTS_WITH_CONFIG_BY_FLAGSETS);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getTreatmentsByFlagSet(key: SplitIO.SplitKey, flagSetName: string, attributes: SplitIO.Attributes | undefined) {
|
|
113
|
+
return getTreatmentsByFlagSets(key, [flagSetName], attributes, false, TREATMENTS_BY_FLAGSET);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getTreatmentsWithConfigByFlagSet(key: SplitIO.SplitKey, flagSetName: string, attributes: SplitIO.Attributes | undefined) {
|
|
117
|
+
return getTreatmentsByFlagSets(key, [flagSetName], attributes, true, TREATMENTS_WITH_CONFIG_BY_FLAGSET);
|
|
118
|
+
}
|
|
119
|
+
|
|
84
120
|
// Internal function
|
|
85
121
|
function processEvaluation(
|
|
86
122
|
evaluation: IEvaluationResult,
|
|
@@ -155,6 +191,10 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
155
191
|
getTreatmentWithConfig,
|
|
156
192
|
getTreatments,
|
|
157
193
|
getTreatmentsWithConfig,
|
|
194
|
+
getTreatmentsByFlagSets,
|
|
195
|
+
getTreatmentsWithConfigByFlagSets,
|
|
196
|
+
getTreatmentsByFlagSet,
|
|
197
|
+
getTreatmentsWithConfigByFlagSet,
|
|
158
198
|
track,
|
|
159
199
|
isClientSide: false
|
|
160
200
|
} as SplitIO.IClient | SplitIO.IAsyncClient;
|
|
@@ -16,6 +16,10 @@ export function clientAttributesDecoration<TClient extends SplitIO.IClient | Spl
|
|
|
16
16
|
const clientGetTreatmentWithConfig = client.getTreatmentWithConfig;
|
|
17
17
|
const clientGetTreatments = client.getTreatments;
|
|
18
18
|
const clientGetTreatmentsWithConfig = client.getTreatmentsWithConfig;
|
|
19
|
+
const clientGetTreatmentsByFlagSets = client.getTreatmentsByFlagSets;
|
|
20
|
+
const clientGetTreatmentsWithConfigByFlagSets = client.getTreatmentsWithConfigByFlagSets;
|
|
21
|
+
const clientGetTreatmentsByFlagSet = client.getTreatmentsByFlagSet;
|
|
22
|
+
const clientGetTreatmentsWithConfigByFlagSet = client.getTreatmentsWithConfigByFlagSet;
|
|
19
23
|
const clientTrack = client.track;
|
|
20
24
|
|
|
21
25
|
function getTreatment(maybeKey: SplitIO.SplitKey, maybeFeatureFlagName: string, maybeAttributes?: SplitIO.Attributes) {
|
|
@@ -34,6 +38,22 @@ export function clientAttributesDecoration<TClient extends SplitIO.IClient | Spl
|
|
|
34
38
|
return clientGetTreatmentsWithConfig(maybeKey, maybeFeatureFlagNames, combineAttributes(maybeAttributes));
|
|
35
39
|
}
|
|
36
40
|
|
|
41
|
+
function getTreatmentsByFlagSets(maybeKey: SplitIO.SplitKey, maybeFlagSets: string[], maybeAttributes?: SplitIO.Attributes) {
|
|
42
|
+
return clientGetTreatmentsByFlagSets(maybeKey, maybeFlagSets, combineAttributes(maybeAttributes));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getTreatmentsWithConfigByFlagSets(maybeKey: SplitIO.SplitKey, maybeFlagSets: string[], maybeAttributes?: SplitIO.Attributes) {
|
|
46
|
+
return clientGetTreatmentsWithConfigByFlagSets(maybeKey, maybeFlagSets, combineAttributes(maybeAttributes));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getTreatmentsByFlagSet(maybeKey: SplitIO.SplitKey, maybeFlagSet: string, maybeAttributes?: SplitIO.Attributes) {
|
|
50
|
+
return clientGetTreatmentsByFlagSet(maybeKey, maybeFlagSet, combineAttributes(maybeAttributes));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getTreatmentsWithConfigByFlagSet(maybeKey: SplitIO.SplitKey, maybeFlagSet: string, maybeAttributes?: SplitIO.Attributes) {
|
|
54
|
+
return clientGetTreatmentsWithConfigByFlagSet(maybeKey, maybeFlagSet, combineAttributes(maybeAttributes));
|
|
55
|
+
}
|
|
56
|
+
|
|
37
57
|
function track(maybeKey: SplitIO.SplitKey, maybeTT: string, maybeEvent: string, maybeEventValue?: number, maybeProperties?: SplitIO.Properties) {
|
|
38
58
|
return clientTrack(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties);
|
|
39
59
|
}
|
|
@@ -51,6 +71,10 @@ export function clientAttributesDecoration<TClient extends SplitIO.IClient | Spl
|
|
|
51
71
|
getTreatmentWithConfig: getTreatmentWithConfig,
|
|
52
72
|
getTreatments: getTreatments,
|
|
53
73
|
getTreatmentsWithConfig: getTreatmentsWithConfig,
|
|
74
|
+
getTreatmentsByFlagSets: getTreatmentsByFlagSets,
|
|
75
|
+
getTreatmentsWithConfigByFlagSets: getTreatmentsWithConfigByFlagSets,
|
|
76
|
+
getTreatmentsByFlagSet: getTreatmentsByFlagSet,
|
|
77
|
+
getTreatmentsWithConfigByFlagSet: getTreatmentsWithConfigByFlagSet,
|
|
54
78
|
track: track,
|
|
55
79
|
|
|
56
80
|
/**
|
|
@@ -21,13 +21,14 @@ export function clientCSDecorator(log: ILogger, client: SplitIO.IClient, key: Sp
|
|
|
21
21
|
getTreatmentWithConfig: clientCS.getTreatmentWithConfig.bind(clientCS, key),
|
|
22
22
|
getTreatments: clientCS.getTreatments.bind(clientCS, key),
|
|
23
23
|
getTreatmentsWithConfig: clientCS.getTreatmentsWithConfig.bind(clientCS, key),
|
|
24
|
+
getTreatmentsByFlagSets: clientCS.getTreatmentsByFlagSets.bind(clientCS, key),
|
|
25
|
+
getTreatmentsWithConfigByFlagSets: clientCS.getTreatmentsWithConfigByFlagSets.bind(clientCS, key),
|
|
26
|
+
getTreatmentsByFlagSet: clientCS.getTreatmentsByFlagSet.bind(clientCS, key),
|
|
27
|
+
getTreatmentsWithConfigByFlagSet: clientCS.getTreatmentsWithConfigByFlagSet.bind(clientCS, key),
|
|
24
28
|
|
|
25
29
|
// Key is bound to the `track` method. Same thing happens with trafficType but only if provided
|
|
26
30
|
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key),
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
isClientSide: true,
|
|
30
|
-
key,
|
|
31
|
-
trafficType
|
|
32
|
+
isClientSide: true
|
|
32
33
|
}) as SplitIO.ICsClient;
|
|
33
34
|
}
|