@splitsoftware/splitio-commons 1.12.1-rc.3 → 1.12.1-rc.5
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 +1 -1
- package/cjs/logger/constants.js +3 -3
- package/cjs/logger/messages/warn.js +2 -2
- package/cjs/sdkClient/client.js +27 -21
- package/cjs/sdkClient/clientInputValidation.js +21 -21
- package/cjs/sdkManager/index.js +13 -15
- package/cjs/storages/KeyBuilder.js +13 -1
- package/cjs/storages/KeyBuilderCS.js +1 -4
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +12 -16
- package/cjs/storages/inLocalStorage/index.js +1 -1
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +2 -2
- package/cjs/storages/inRedis/index.js +1 -1
- package/cjs/storages/pluggable/SplitsCachePluggable.js +2 -2
- package/cjs/storages/pluggable/index.js +14 -3
- package/cjs/trackers/eventTracker.js +4 -4
- package/cjs/utils/constants/index.js +16 -2
- package/cjs/utils/lang/sets.js +3 -3
- package/cjs/utils/settingsValidation/index.js +1 -1
- package/cjs/utils/settingsValidation/mode.js +10 -3
- package/cjs/utils/settingsValidation/splitFilters.js +20 -19
- package/esm/logger/constants.js +2 -2
- package/esm/logger/messages/warn.js +2 -2
- package/esm/sdkClient/client.js +28 -22
- package/esm/sdkClient/clientInputValidation.js +23 -23
- package/esm/sdkManager/index.js +7 -9
- package/esm/storages/KeyBuilder.js +11 -0
- package/esm/storages/KeyBuilderCS.js +1 -4
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +12 -16
- package/esm/storages/inLocalStorage/index.js +1 -1
- package/esm/storages/inRedis/SplitsCacheInRedis.js +3 -3
- package/esm/storages/inRedis/index.js +1 -1
- package/esm/storages/pluggable/SplitsCachePluggable.js +3 -3
- package/esm/storages/pluggable/index.js +15 -4
- package/esm/trackers/eventTracker.js +4 -4
- package/esm/utils/constants/index.js +14 -0
- package/esm/utils/lang/sets.js +1 -1
- package/esm/utils/settingsValidation/index.js +2 -2
- package/esm/utils/settingsValidation/mode.js +7 -1
- package/esm/utils/settingsValidation/splitFilters.js +11 -10
- package/package.json +1 -1
- package/src/logger/constants.ts +2 -2
- package/src/logger/messages/warn.ts +2 -2
- package/src/sdkClient/client.ts +26 -23
- package/src/sdkClient/clientInputValidation.ts +23 -23
- package/src/sdkManager/index.ts +7 -10
- package/src/storages/KeyBuilder.ts +14 -1
- package/src/storages/KeyBuilderCS.ts +1 -5
- package/src/storages/KeyBuilderSS.ts +4 -4
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +16 -14
- package/src/storages/inLocalStorage/index.ts +1 -1
- package/src/storages/inRedis/SplitsCacheInRedis.ts +3 -3
- package/src/storages/inRedis/index.ts +1 -1
- package/src/storages/pluggable/SplitsCachePluggable.ts +3 -3
- package/src/storages/pluggable/index.ts +15 -5
- package/src/storages/types.ts +3 -3
- package/src/trackers/eventTracker.ts +4 -4
- package/src/utils/constants/index.ts +16 -0
- package/src/utils/lang/sets.ts +1 -1
- package/src/utils/murmur3/murmur3.ts +0 -1
- package/src/utils/settingsValidation/index.ts +2 -2
- package/src/utils/settingsValidation/mode.ts +8 -1
- package/src/utils/settingsValidation/splitFilters.ts +11 -10
- package/types/logger/constants.d.ts +2 -2
- package/types/storages/AbstractSplitsCache.d.ts +46 -0
- package/types/storages/KeyBuilder.d.ts +8 -1
- package/types/storages/KeyBuilderCS.d.ts +0 -1
- package/types/storages/KeyBuilderSS.d.ts +4 -4
- package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +5 -5
- package/types/utils/constants/index.d.ts +12 -0
- package/types/utils/lang/sets.d.ts +1 -1
- package/types/utils/settingsValidation/mode.d.ts +5 -1
- package/types/utils/settingsValidation/splitFilters.d.ts +1 -1
- package/cjs/trackers/impressionObserver/utils.js +0 -11
- package/cjs/utils/redis/RedisMock.js +0 -31
- package/esm/trackers/impressionObserver/utils.js +0 -7
- package/esm/utils/redis/RedisMock.js +0 -28
- package/src/trackers/impressionObserver/utils.ts +0 -9
- package/src/utils/redis/RedisMock.ts +0 -31
|
@@ -3,6 +3,7 @@ import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSyn
|
|
|
3
3
|
import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang';
|
|
4
4
|
import { LOG_PREFIX } from './constants';
|
|
5
5
|
import { _Set, setToArray } from '../../utils/lang/sets';
|
|
6
|
+
import { getStorageHash } from '../KeyBuilder';
|
|
6
7
|
/**
|
|
7
8
|
* ISplitsCacheSync implementation that stores split definitions in browser LocalStorage.
|
|
8
9
|
*/
|
|
@@ -13,13 +14,12 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
13
14
|
* @param {number | undefined} expirationTimestamp
|
|
14
15
|
* @param {ISplitFiltersValidation} splitFiltersValidation
|
|
15
16
|
*/
|
|
16
|
-
function SplitsCacheInLocal(
|
|
17
|
-
if (splitFiltersValidation === void 0) { splitFiltersValidation = { queryString: null, groupedFilters: { bySet: [], byName: [], byPrefix: [] }, validFilters: [] }; }
|
|
17
|
+
function SplitsCacheInLocal(settings, keys, expirationTimestamp) {
|
|
18
18
|
var _this = _super.call(this) || this;
|
|
19
|
-
_this.log = log;
|
|
20
19
|
_this.keys = keys;
|
|
21
|
-
_this.
|
|
22
|
-
_this.
|
|
20
|
+
_this.log = settings.log;
|
|
21
|
+
_this.storageHash = getStorageHash(settings);
|
|
22
|
+
_this.flagSetsFilter = settings.sync.__splitFiltersValidation.groupedFilters.bySet;
|
|
23
23
|
_this._checkExpiration(expirationTimestamp);
|
|
24
24
|
_this._checkFilterQuery();
|
|
25
25
|
return _this;
|
|
@@ -123,14 +123,10 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
123
123
|
SplitsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
124
124
|
// when using a new split query, we must update it at the store
|
|
125
125
|
if (this.updateNewFilter) {
|
|
126
|
-
this.log.info(LOG_PREFIX + '
|
|
127
|
-
var
|
|
128
|
-
var queryString = this.splitFiltersValidation.queryString;
|
|
126
|
+
this.log.info(LOG_PREFIX + 'SDK key or feature flag filter criteria was modified. Updating cache.');
|
|
127
|
+
var storageHashKey = this.keys.buildHashKey();
|
|
129
128
|
try {
|
|
130
|
-
|
|
131
|
-
localStorage.setItem(queryKey, queryString);
|
|
132
|
-
else
|
|
133
|
-
localStorage.removeItem(queryKey);
|
|
129
|
+
localStorage.setItem(storageHashKey, this.storageHash);
|
|
134
130
|
}
|
|
135
131
|
catch (e) {
|
|
136
132
|
this.log.error(LOG_PREFIX + e);
|
|
@@ -208,11 +204,11 @@ var SplitsCacheInLocal = /** @class */ (function (_super) {
|
|
|
208
204
|
this.clear();
|
|
209
205
|
}
|
|
210
206
|
};
|
|
207
|
+
// @TODO eventually remove `_checkFilterQuery`. Cache should be cleared at the storage level, reusing same logic than PluggableStorage
|
|
211
208
|
SplitsCacheInLocal.prototype._checkFilterQuery = function () {
|
|
212
|
-
var
|
|
213
|
-
var
|
|
214
|
-
|
|
215
|
-
if (currentQueryString !== queryString) {
|
|
209
|
+
var storageHashKey = this.keys.buildHashKey();
|
|
210
|
+
var storageHash = localStorage.getItem(storageHashKey);
|
|
211
|
+
if (storageHash !== this.storageHash) {
|
|
216
212
|
try {
|
|
217
213
|
// mark cache to update the new query filter on first successful splits fetch
|
|
218
214
|
this.updateNewFilter = true;
|
|
@@ -31,7 +31,7 @@ export function InLocalStorage(options) {
|
|
|
31
31
|
var matchingKey = getMatching(settings.core.key);
|
|
32
32
|
var keys = new KeyBuilderCS(prefix, matchingKey);
|
|
33
33
|
var expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
|
|
34
|
-
var splits = new SplitsCacheInLocal(
|
|
34
|
+
var splits = new SplitsCacheInLocal(settings, keys, expirationTimestamp);
|
|
35
35
|
var segments = new MySegmentsCacheInLocal(log, keys);
|
|
36
36
|
return {
|
|
37
37
|
splits: splits,
|
|
@@ -2,7 +2,7 @@ import { __extends, __spreadArray } 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,
|
|
5
|
+
import { _Set, returnDifference } from '../../utils/lang/sets';
|
|
6
6
|
/**
|
|
7
7
|
* Discard errors for an answer of multiple operations.
|
|
8
8
|
*/
|
|
@@ -49,8 +49,8 @@ var SplitsCacheInRedis = /** @class */ (function (_super) {
|
|
|
49
49
|
};
|
|
50
50
|
SplitsCacheInRedis.prototype._updateFlagSets = function (featureFlagName, flagSetsOfRemovedFlag, flagSetsOfAddedFlag) {
|
|
51
51
|
var _this = this;
|
|
52
|
-
var removeFromFlagSets =
|
|
53
|
-
var addToFlagSets =
|
|
52
|
+
var removeFromFlagSets = returnDifference(flagSetsOfRemovedFlag, flagSetsOfAddedFlag);
|
|
53
|
+
var addToFlagSets = returnDifference(flagSetsOfAddedFlag, flagSetsOfRemovedFlag);
|
|
54
54
|
if (this.flagSetsFilter.length > 0) {
|
|
55
55
|
addToFlagSets = addToFlagSets.filter(function (flagSet) {
|
|
56
56
|
return _this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === flagSet; });
|
|
@@ -36,7 +36,7 @@ export function InRedisStorage(options) {
|
|
|
36
36
|
telemetry.recordConfig();
|
|
37
37
|
});
|
|
38
38
|
return {
|
|
39
|
-
splits: new SplitsCacheInRedis(log, keys, redisClient),
|
|
39
|
+
splits: new SplitsCacheInRedis(log, keys, redisClient, settings.sync.__splitFiltersValidation),
|
|
40
40
|
segments: new SegmentsCacheInRedis(log, keys, redisClient),
|
|
41
41
|
impressions: new ImpressionsCacheInRedis(log, keys.buildImpressionsKey(), redisClient, metadata),
|
|
42
42
|
impressionCounts: impressionCountsCache,
|
|
@@ -2,7 +2,7 @@ import { __extends, __spreadArray } 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,
|
|
5
|
+
import { _Set, returnDifference } from '../../utils/lang/sets';
|
|
6
6
|
/**
|
|
7
7
|
* ISplitsCacheAsync implementation for pluggable storages.
|
|
8
8
|
*/
|
|
@@ -36,8 +36,8 @@ var SplitsCachePluggable = /** @class */ (function (_super) {
|
|
|
36
36
|
};
|
|
37
37
|
SplitsCachePluggable.prototype._updateFlagSets = function (featureFlagName, flagSetsOfRemovedFlag, flagSetsOfAddedFlag) {
|
|
38
38
|
var _this = this;
|
|
39
|
-
var removeFromFlagSets =
|
|
40
|
-
var addToFlagSets =
|
|
39
|
+
var removeFromFlagSets = returnDifference(flagSetsOfRemovedFlag, flagSetsOfAddedFlag);
|
|
40
|
+
var addToFlagSets = returnDifference(flagSetsOfAddedFlag, flagSetsOfRemovedFlag);
|
|
41
41
|
if (this.flagSetsFilter.length > 0) {
|
|
42
42
|
addToFlagSets = addToFlagSets.filter(function (flagSet) {
|
|
43
43
|
return _this.flagSetsFilter.some(function (filterFlagSet) { return filterFlagSet === flagSet; });
|
|
@@ -6,7 +6,7 @@ import { ImpressionsCachePluggable } from './ImpressionsCachePluggable';
|
|
|
6
6
|
import { EventsCachePluggable } from './EventsCachePluggable';
|
|
7
7
|
import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter';
|
|
8
8
|
import { isObject } from '../../utils/lang';
|
|
9
|
-
import { validatePrefix } from '../KeyBuilder';
|
|
9
|
+
import { getStorageHash, validatePrefix } from '../KeyBuilder';
|
|
10
10
|
import { CONSUMER_PARTIAL_MODE, DEBUG, NONE, STORAGE_PLUGGABLE } from '../../utils/constants';
|
|
11
11
|
import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory';
|
|
12
12
|
import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
|
|
@@ -73,15 +73,26 @@ export function PluggableStorage(options) {
|
|
|
73
73
|
undefined;
|
|
74
74
|
// Connects to wrapper and emits SDK_READY event on main client
|
|
75
75
|
var connectPromise = wrapper.connect().then(function () {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
if (isSyncronizer) {
|
|
77
|
+
// In standalone or producer mode, clear storage if SDK key or feature flag filter has changed
|
|
78
|
+
return wrapper.get(keys.buildHashKey()).then(function (hash) {
|
|
79
|
+
var currentHash = getStorageHash(settings);
|
|
80
|
+
if (hash !== currentHash) {
|
|
81
|
+
return wrapper.getKeysByPrefix(keys.prefix + ".").then(function (storageKeys) {
|
|
82
|
+
return Promise.all(storageKeys.map(function (storageKey) { return wrapper.del(storageKey); }));
|
|
83
|
+
}).then(function () { return wrapper.set(keys.buildHashKey(), currentHash); });
|
|
84
|
+
}
|
|
85
|
+
}).then(onReadyCb);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Start periodic flush of async storages if not running synchronizer (producer mode)
|
|
79
89
|
if (impressionCountsCache && impressionCountsCache.start)
|
|
80
90
|
impressionCountsCache.start();
|
|
81
91
|
if (uniqueKeysCache && uniqueKeysCache.start)
|
|
82
92
|
uniqueKeysCache.start();
|
|
83
93
|
if (telemetry && telemetry.recordConfig)
|
|
84
94
|
telemetry.recordConfig();
|
|
95
|
+
onReadyCb();
|
|
85
96
|
}
|
|
86
97
|
}).catch(function (e) {
|
|
87
98
|
e = e || new Error('Error connecting wrapper');
|
|
@@ -2,7 +2,7 @@ import { objectAssign } from '../utils/lang/objectAssign';
|
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
3
|
import { EVENTS_TRACKER_SUCCESS, ERROR_EVENTS_TRACKER } from '../logger/constants';
|
|
4
4
|
import { CONSENT_DECLINED, DROPPED, QUEUED } from '../utils/constants';
|
|
5
|
-
import {
|
|
5
|
+
import { isConsumerMode } from '../utils/settingsValidation/mode';
|
|
6
6
|
/**
|
|
7
7
|
* Event tracker stores events in cache and pass them to the integrations manager if provided.
|
|
8
8
|
*
|
|
@@ -10,8 +10,8 @@ import { isStorageSync } from './impressionObserver/utils';
|
|
|
10
10
|
* @param integrationsManager optional event handler used for integrations
|
|
11
11
|
*/
|
|
12
12
|
export function eventTrackerFactory(settings, eventsCache, integrationsManager, telemetryCache) {
|
|
13
|
-
var log = settings.log;
|
|
14
|
-
var
|
|
13
|
+
var log = settings.log, mode = settings.mode;
|
|
14
|
+
var isAsync = isConsumerMode(mode);
|
|
15
15
|
function queueEventsCallback(eventData, tracked) {
|
|
16
16
|
var eventTypeId = eventData.eventTypeId, trafficTypeName = eventData.trafficTypeName, key = eventData.key, value = eventData.value, timestamp = eventData.timestamp, properties = eventData.properties;
|
|
17
17
|
// Logging every prop would be too much.
|
|
@@ -38,7 +38,7 @@ export function eventTrackerFactory(settings, eventsCache, integrationsManager,
|
|
|
38
38
|
return {
|
|
39
39
|
track: function (eventData, size) {
|
|
40
40
|
if (settings.userConsent === CONSENT_DECLINED) {
|
|
41
|
-
return
|
|
41
|
+
return isAsync ? Promise.resolve(false) : false;
|
|
42
42
|
}
|
|
43
43
|
var tracked = eventsCache.track(eventData, size);
|
|
44
44
|
if (thenable(tracked)) {
|
|
@@ -29,6 +29,20 @@ export var STORAGE_PLUGGABLE = 'PLUGGABLE';
|
|
|
29
29
|
export var CONSENT_GRANTED = 'GRANTED'; // The user has granted consent for tracking events and impressions
|
|
30
30
|
export var CONSENT_DECLINED = 'DECLINED'; // The user has declined consent for tracking events and impressions
|
|
31
31
|
export var CONSENT_UNKNOWN = 'UNKNOWN'; // The user has neither granted nor declined consent for tracking events and impressions
|
|
32
|
+
// Client method names
|
|
33
|
+
export var GET_TREATMENT = 'getTreatment';
|
|
34
|
+
export var GET_TREATMENTS = 'getTreatments';
|
|
35
|
+
export var GET_TREATMENT_WITH_CONFIG = 'getTreatmentWithConfig';
|
|
36
|
+
export var GET_TREATMENTS_WITH_CONFIG = 'getTreatmentsWithConfig';
|
|
37
|
+
export var GET_TREATMENTS_BY_FLAG_SET = 'getTreatmentsByFlagSet';
|
|
38
|
+
export var GET_TREATMENTS_BY_FLAG_SETS = 'getTreatmentsByFlagSets';
|
|
39
|
+
export var GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET = 'getTreatmentsWithConfigByFlagSet';
|
|
40
|
+
export var GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS = 'getTreatmentsWithConfigByFlagSets';
|
|
41
|
+
export var TRACK_FN_LABEL = 'track';
|
|
42
|
+
// Manager method names
|
|
43
|
+
export var SPLIT_FN_LABEL = 'split';
|
|
44
|
+
export var SPLITS_FN_LABEL = 'splits';
|
|
45
|
+
export var NAMES_FN_LABEL = 'names';
|
|
32
46
|
// Telemetry
|
|
33
47
|
export var QUEUED = 0;
|
|
34
48
|
export var DROPPED = 1;
|
package/esm/utils/lang/sets.js
CHANGED
|
@@ -102,7 +102,7 @@ export function returnSetsUnion(set, set2) {
|
|
|
102
102
|
});
|
|
103
103
|
return result;
|
|
104
104
|
}
|
|
105
|
-
export function
|
|
105
|
+
export function returnDifference(list, list2) {
|
|
106
106
|
if (list === void 0) { list = []; }
|
|
107
107
|
if (list2 === void 0) { list2 = []; }
|
|
108
108
|
var result = new _Set(list);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { merge, get } from '../lang';
|
|
2
|
-
import {
|
|
2
|
+
import { validateMode } from './mode';
|
|
3
3
|
import { validateSplitFilters } from './splitFilters';
|
|
4
4
|
import { STANDALONE_MODE, OPTIMIZED, LOCALHOST_MODE, DEBUG } from '../constants';
|
|
5
5
|
import { validImpressionsMode } from './impressionsMode';
|
|
@@ -123,7 +123,7 @@ export function settingsValidation(config, validationParams) {
|
|
|
123
123
|
startup.eventsFirstPushWindow = fromSecondsToMillis(startup.eventsFirstPushWindow);
|
|
124
124
|
// ensure a valid SDK mode
|
|
125
125
|
// @ts-ignore, modify readonly prop
|
|
126
|
-
withDefaults.mode =
|
|
126
|
+
withDefaults.mode = validateMode(withDefaults.core.authorizationKey, withDefaults.mode);
|
|
127
127
|
// ensure a valid Storage based on mode defined.
|
|
128
128
|
// @ts-ignore, modify readonly prop
|
|
129
129
|
if (storage)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LOCALHOST_MODE, STANDALONE_MODE, PRODUCER_MODE, CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../constants';
|
|
2
|
-
export function
|
|
2
|
+
export function validateMode(key, mode) {
|
|
3
3
|
// Leaving the comparison as is, in case we change the mode name but not the setting.
|
|
4
4
|
if (key === 'localhost')
|
|
5
5
|
return LOCALHOST_MODE;
|
|
@@ -7,3 +7,9 @@ export function mode(key, mode) {
|
|
|
7
7
|
throw Error('Invalid mode provided');
|
|
8
8
|
return mode;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Storage is async if mode is consumer or partial consumer
|
|
12
|
+
*/
|
|
13
|
+
export function isConsumerMode(mode) {
|
|
14
|
+
return CONSUMER_MODE === mode || CONSUMER_PARTIAL_MODE === mode;
|
|
15
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../constants';
|
|
2
1
|
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, ERROR_SETS_FILTER_EXCLUSIVE,
|
|
2
|
+
import { WARN_SPLITS_FILTER_IGNORED, WARN_SPLITS_FILTER_EMPTY, WARN_SPLITS_FILTER_INVALID, SETTINGS_SPLITS_FILTER, LOG_PREFIX_SETTINGS, ERROR_SETS_FILTER_EXCLUSIVE, WARN_LOWERCASE_FLAGSET, WARN_INVALID_FLAGSET, WARN_FLAGSET_NOT_CONFIGURED } from '../../logger/constants';
|
|
4
3
|
import { objectAssign } from '../lang/objectAssign';
|
|
5
4
|
import { find, uniq } from '../lang';
|
|
5
|
+
import { isConsumerMode } from './mode';
|
|
6
6
|
// Split filters metadata.
|
|
7
7
|
// Ordered according to their precedency when forming the filter query string: `&names=<values>&prefixes=<values>`
|
|
8
8
|
var FILTERS_METADATA = [
|
|
@@ -45,7 +45,7 @@ function validateSplitFilter(log, type, values, maxLength) {
|
|
|
45
45
|
var result = validateSplits(log, values, LOG_PREFIX_SETTINGS, type + " filter", type + " filter value");
|
|
46
46
|
if (result) {
|
|
47
47
|
if (type === 'bySet') {
|
|
48
|
-
result = sanitizeFlagSets(log, result);
|
|
48
|
+
result = sanitizeFlagSets(log, result, LOG_PREFIX_SETTINGS);
|
|
49
49
|
}
|
|
50
50
|
// check max length
|
|
51
51
|
if (result.length > maxLength)
|
|
@@ -78,7 +78,7 @@ function queryStringBuilder(groupedFilters) {
|
|
|
78
78
|
return queryParams.length > 0 ? '&' + queryParams.join('&') : null;
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
|
-
* Sanitizes set names list taking
|
|
81
|
+
* Sanitizes set names list taking into account:
|
|
82
82
|
* - It should be lowercase
|
|
83
83
|
* - Must adhere the following regular expression /^[a-z0-9][_a-z0-9]{0,49}$/ that means
|
|
84
84
|
* - must start with a letter or number
|
|
@@ -88,20 +88,21 @@ function queryStringBuilder(groupedFilters) {
|
|
|
88
88
|
*
|
|
89
89
|
* @param {ILogger} log
|
|
90
90
|
* @param {string[]} flagSets
|
|
91
|
+
* @param {string} method
|
|
91
92
|
* @returns sanitized list of set names
|
|
92
93
|
*/
|
|
93
|
-
function sanitizeFlagSets(log, flagSets) {
|
|
94
|
+
function sanitizeFlagSets(log, flagSets, method) {
|
|
94
95
|
var sanitizedSets = flagSets
|
|
95
96
|
.map(function (flagSet) {
|
|
96
97
|
if (CAPITAL_LETTERS_REGEX.test(flagSet)) {
|
|
97
|
-
log.warn(
|
|
98
|
+
log.warn(WARN_LOWERCASE_FLAGSET, [method, flagSet]);
|
|
98
99
|
flagSet = flagSet.toLowerCase();
|
|
99
100
|
}
|
|
100
101
|
return flagSet;
|
|
101
102
|
})
|
|
102
103
|
.filter(function (flagSet) {
|
|
103
104
|
if (!VALID_FLAGSET_REGEX.test(flagSet)) {
|
|
104
|
-
log.warn(
|
|
105
|
+
log.warn(WARN_INVALID_FLAGSET, [method, flagSet, VALID_FLAGSET_REGEX, flagSet]);
|
|
105
106
|
return false;
|
|
106
107
|
}
|
|
107
108
|
if (typeof flagSet !== 'string')
|
|
@@ -137,7 +138,7 @@ export function validateSplitFilters(log, maybeSplitFilters, mode) {
|
|
|
137
138
|
if (!maybeSplitFilters)
|
|
138
139
|
return res;
|
|
139
140
|
// Warn depending on the mode
|
|
140
|
-
if (mode
|
|
141
|
+
if (isConsumerMode(mode)) {
|
|
141
142
|
log.warn(WARN_SPLITS_FILTER_IGNORED);
|
|
142
143
|
return res;
|
|
143
144
|
}
|
|
@@ -175,9 +176,9 @@ export function validateSplitFilters(log, maybeSplitFilters, mode) {
|
|
|
175
176
|
log.debug(SETTINGS_SPLITS_FILTER, [res.queryString]);
|
|
176
177
|
return res;
|
|
177
178
|
}
|
|
178
|
-
export function
|
|
179
|
+
export function validateFlagSets(log, method, flagSets, flagSetsInConfig) {
|
|
179
180
|
var sets = validateSplits(log, flagSets, method, 'flag sets', 'flag set');
|
|
180
|
-
var toReturn = sets ? sanitizeFlagSets(log, sets) : [];
|
|
181
|
+
var toReturn = sets ? sanitizeFlagSets(log, sets, method) : [];
|
|
181
182
|
if (flagSetsInConfig.length > 0) {
|
|
182
183
|
toReturn = toReturn.filter(function (flagSet) {
|
|
183
184
|
if (flagSetsInConfig.indexOf(flagSet) > -1) {
|
package/package.json
CHANGED
package/src/logger/constants.ts
CHANGED
|
@@ -97,8 +97,8 @@ 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
|
|
101
|
-
export const
|
|
100
|
+
export const WARN_INVALID_FLAGSET = 225;
|
|
101
|
+
export const WARN_LOWERCASE_FLAGSET = 226;
|
|
102
102
|
export const WARN_FLAGSET_NOT_CONFIGURED = 227;
|
|
103
103
|
export const WARN_FLAGSET_WITHOUT_FLAGS = 228;
|
|
104
104
|
|
|
@@ -34,7 +34,7 @@ export const codesWarn: [number, string][] = codesError.concat([
|
|
|
34
34
|
|
|
35
35
|
[c.STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching MySegments due to an error processing %s notification: %s'],
|
|
36
36
|
[c.STREAMING_PARSING_SPLIT_UPDATE, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching SplitChanges due to an error processing SPLIT_UPDATE notification: %s'],
|
|
37
|
-
[c.
|
|
38
|
-
[c.
|
|
37
|
+
[c.WARN_INVALID_FLAGSET, '%s: 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.'],
|
|
38
|
+
[c.WARN_LOWERCASE_FLAGSET, '%s: flag set %s should be all lowercase - converting string to lowercase.'],
|
|
39
39
|
[c.WARN_FLAGSET_WITHOUT_FLAGS, '%s: you passed %s flag set that does not contain cached feature flag names. Please double check what flag sets are in use in the Split user interface.'],
|
|
40
40
|
]);
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -4,12 +4,12 @@ import { getMatching, getBucketing } from '../utils/key';
|
|
|
4
4
|
import { validateSplitExistence } from '../utils/inputValidation/splitExistence';
|
|
5
5
|
import { validateTrafficTypeExistence } from '../utils/inputValidation/trafficTypeExistence';
|
|
6
6
|
import { SDK_NOT_READY } from '../utils/labels';
|
|
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';
|
|
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, GET_TREATMENTS_WITH_CONFIG, GET_TREATMENTS_BY_FLAG_SETS, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, GET_TREATMENTS_BY_FLAG_SET, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, GET_TREATMENT_WITH_CONFIG, GET_TREATMENT, GET_TREATMENTS, TRACK_FN_LABEL } 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
|
-
import {
|
|
12
|
+
import { isConsumerMode } from '../utils/settingsValidation/mode';
|
|
13
13
|
import { Method } from '../sync/submitters/types';
|
|
14
14
|
|
|
15
15
|
const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
|
|
@@ -28,13 +28,14 @@ function treatmentsNotReady(featureFlagNames: string[]) {
|
|
|
28
28
|
export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | SplitIO.IAsyncClient {
|
|
29
29
|
const { sdkReadinessManager: { readinessManager }, storage, settings, impressionsTracker, eventTracker, telemetryTracker } = params;
|
|
30
30
|
const { log, mode } = settings;
|
|
31
|
+
const isAsync = isConsumerMode(mode);
|
|
31
32
|
|
|
32
|
-
function getTreatment(key: SplitIO.SplitKey, featureFlagName: string, attributes: SplitIO.Attributes | undefined, withConfig = false) {
|
|
33
|
+
function getTreatment(key: SplitIO.SplitKey, featureFlagName: string, attributes: SplitIO.Attributes | undefined, withConfig = false, methodName = GET_TREATMENT) {
|
|
33
34
|
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
|
|
34
35
|
|
|
35
36
|
const wrapUp = (evaluationResult: IEvaluationResult) => {
|
|
36
37
|
const queue: ImpressionDTO[] = [];
|
|
37
|
-
const treatment = processEvaluation(evaluationResult, featureFlagName, key, attributes, withConfig,
|
|
38
|
+
const treatment = processEvaluation(evaluationResult, featureFlagName, key, attributes, withConfig, methodName, queue);
|
|
38
39
|
impressionsTracker.track(queue, attributes);
|
|
39
40
|
|
|
40
41
|
stopTelemetryTracker(queue[0] && queue[0].label);
|
|
@@ -43,25 +44,25 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
43
44
|
|
|
44
45
|
const evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
|
|
45
46
|
evaluateFeature(log, key, featureFlagName, attributes, storage) :
|
|
46
|
-
|
|
47
|
-
treatmentNotReady :
|
|
48
|
-
|
|
47
|
+
isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
|
|
48
|
+
Promise.resolve(treatmentNotReady) :
|
|
49
|
+
treatmentNotReady;
|
|
49
50
|
|
|
50
51
|
return thenable(evaluation) ? evaluation.then((res) => wrapUp(res)) : wrapUp(evaluation);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
function getTreatmentWithConfig(key: SplitIO.SplitKey, featureFlagName: string, attributes: SplitIO.Attributes | undefined) {
|
|
54
|
-
return getTreatment(key, featureFlagName, attributes, true);
|
|
55
|
+
return getTreatment(key, featureFlagName, attributes, true, GET_TREATMENT_WITH_CONFIG);
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
function getTreatments(key: SplitIO.SplitKey, featureFlagNames: string[], attributes: SplitIO.Attributes | undefined, withConfig = false) {
|
|
58
|
+
function getTreatments(key: SplitIO.SplitKey, featureFlagNames: string[], attributes: SplitIO.Attributes | undefined, withConfig = false, methodName = GET_TREATMENTS) {
|
|
58
59
|
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENTS_WITH_CONFIG : TREATMENTS);
|
|
59
60
|
|
|
60
61
|
const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
|
|
61
62
|
const queue: ImpressionDTO[] = [];
|
|
62
63
|
const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
|
|
63
64
|
Object.keys(evaluationResults).forEach(featureFlagName => {
|
|
64
|
-
treatments[featureFlagName] = processEvaluation(evaluationResults[featureFlagName], featureFlagName, key, attributes, withConfig,
|
|
65
|
+
treatments[featureFlagName] = processEvaluation(evaluationResults[featureFlagName], featureFlagName, key, attributes, withConfig, methodName, queue);
|
|
65
66
|
});
|
|
66
67
|
impressionsTracker.track(queue, attributes);
|
|
67
68
|
|
|
@@ -71,26 +72,26 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
71
72
|
|
|
72
73
|
const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
|
|
73
74
|
evaluateFeatures(log, key, featureFlagNames, attributes, storage) :
|
|
74
|
-
|
|
75
|
-
treatmentsNotReady(featureFlagNames) :
|
|
76
|
-
|
|
75
|
+
isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
|
|
76
|
+
Promise.resolve(treatmentsNotReady(featureFlagNames)) :
|
|
77
|
+
treatmentsNotReady(featureFlagNames);
|
|
77
78
|
|
|
78
79
|
return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
function getTreatmentsWithConfig(key: SplitIO.SplitKey, featureFlagNames: string[], attributes: SplitIO.Attributes | undefined) {
|
|
82
|
-
return getTreatments(key, featureFlagNames, attributes, true);
|
|
83
|
+
return getTreatments(key, featureFlagNames, attributes, true, GET_TREATMENTS_WITH_CONFIG);
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
function getTreatmentsByFlagSets(key: SplitIO.SplitKey, flagSetNames: string[], attributes: SplitIO.Attributes | undefined, withConfig = false, method: Method = TREATMENTS_BY_FLAGSETS) {
|
|
86
|
+
function getTreatmentsByFlagSets(key: SplitIO.SplitKey, flagSetNames: string[], attributes: SplitIO.Attributes | undefined, withConfig = false, method: Method = TREATMENTS_BY_FLAGSETS, methodName = GET_TREATMENTS_BY_FLAG_SETS) {
|
|
86
87
|
const stopTelemetryTracker = telemetryTracker.trackEval(method);
|
|
87
88
|
|
|
88
|
-
const wrapUp = (evaluationResults: Record<string,IEvaluationResult>) => {
|
|
89
|
+
const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
|
|
89
90
|
const queue: ImpressionDTO[] = [];
|
|
90
91
|
const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
|
|
91
92
|
const evaluations = evaluationResults;
|
|
92
93
|
Object.keys(evaluations).forEach(featureFlagName => {
|
|
93
|
-
treatments[featureFlagName] = processEvaluation(evaluations[featureFlagName], featureFlagName, key, attributes, withConfig,
|
|
94
|
+
treatments[featureFlagName] = processEvaluation(evaluations[featureFlagName], featureFlagName, key, attributes, withConfig, methodName, queue);
|
|
94
95
|
});
|
|
95
96
|
impressionsTracker.track(queue, attributes);
|
|
96
97
|
|
|
@@ -99,22 +100,24 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
99
100
|
};
|
|
100
101
|
|
|
101
102
|
const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
|
|
102
|
-
evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage,
|
|
103
|
-
|
|
103
|
+
evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) :
|
|
104
|
+
isAsync ?
|
|
105
|
+
Promise.resolve({}) :
|
|
106
|
+
{};
|
|
104
107
|
|
|
105
108
|
return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
function getTreatmentsWithConfigByFlagSets(key: SplitIO.SplitKey, flagSetNames: string[], attributes: SplitIO.Attributes | undefined) {
|
|
109
|
-
return getTreatmentsByFlagSets(key, flagSetNames, attributes, true, TREATMENTS_WITH_CONFIG_BY_FLAGSETS);
|
|
112
|
+
return getTreatmentsByFlagSets(key, flagSetNames, attributes, true, TREATMENTS_WITH_CONFIG_BY_FLAGSETS, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS);
|
|
110
113
|
}
|
|
111
114
|
|
|
112
115
|
function getTreatmentsByFlagSet(key: SplitIO.SplitKey, flagSetName: string, attributes: SplitIO.Attributes | undefined) {
|
|
113
|
-
return getTreatmentsByFlagSets(key, [flagSetName], attributes, false, TREATMENTS_BY_FLAGSET);
|
|
116
|
+
return getTreatmentsByFlagSets(key, [flagSetName], attributes, false, TREATMENTS_BY_FLAGSET, GET_TREATMENTS_BY_FLAG_SET);
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
function getTreatmentsWithConfigByFlagSet(key: SplitIO.SplitKey, flagSetName: string, attributes: SplitIO.Attributes | undefined) {
|
|
117
|
-
return getTreatmentsByFlagSets(key, [flagSetName], attributes, true, TREATMENTS_WITH_CONFIG_BY_FLAGSET);
|
|
120
|
+
return getTreatmentsByFlagSets(key, [flagSetName], attributes, true, TREATMENTS_WITH_CONFIG_BY_FLAGSET, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET);
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
// Internal function
|
|
@@ -171,7 +174,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
171
174
|
};
|
|
172
175
|
|
|
173
176
|
// This may be async but we only warn, we don't actually care if it is valid or not in terms of queueing the event.
|
|
174
|
-
validateTrafficTypeExistence(log, readinessManager, storage.splits, mode, trafficTypeName,
|
|
177
|
+
validateTrafficTypeExistence(log, readinessManager, storage.splits, mode, trafficTypeName, TRACK_FN_LABEL);
|
|
175
178
|
|
|
176
179
|
const result = eventTracker.track(eventData, size);
|
|
177
180
|
|