@splitsoftware/splitio-commons 1.13.2-rc.6 → 1.13.2-rc.7
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 +2 -0
- package/cjs/evaluator/index.js +1 -1
- package/cjs/evaluator/matchers/semver_inlist.js +1 -1
- package/cjs/evaluator/matchersTransform/index.js +1 -0
- package/cjs/evaluator/matchersTransform/whitelist.js +1 -1
- package/cjs/evaluator/parser/index.js +7 -12
- package/cjs/logger/messages/debug.js +2 -2
- package/cjs/services/splitApi.js +5 -5
- package/cjs/storages/KeyBuilder.js +3 -2
- package/cjs/utils/constants/index.js +2 -1
- package/esm/evaluator/index.js +1 -1
- package/esm/evaluator/matchers/semver_inlist.js +1 -1
- package/esm/evaluator/matchersTransform/index.js +1 -0
- package/esm/evaluator/matchersTransform/whitelist.js +1 -1
- package/esm/evaluator/parser/index.js +7 -12
- package/esm/logger/messages/debug.js +2 -2
- package/esm/services/splitApi.js +6 -6
- package/esm/storages/KeyBuilder.js +3 -2
- package/esm/utils/constants/index.js +1 -0
- package/package.json +1 -1
- package/src/evaluator/index.ts +1 -1
- package/src/evaluator/matchers/semver_inlist.ts +1 -1
- package/src/evaluator/matchersTransform/index.ts +2 -1
- package/src/evaluator/matchersTransform/whitelist.ts +1 -1
- package/src/evaluator/parser/index.ts +4 -5
- package/src/evaluator/types.ts +1 -0
- package/src/logger/messages/debug.ts +2 -2
- package/src/services/splitApi.ts +5 -6
- package/src/storages/KeyBuilder.ts +3 -2
- package/src/utils/constants/index.ts +2 -0
- package/types/evaluator/matchersTransform/whitelist.d.ts +1 -1
- package/types/evaluator/types.d.ts +1 -0
- package/types/storages/KeyBuilder.d.ts +1 -1
- package/types/utils/constants/index.d.ts +1 -0
package/CHANGES.txt
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
1.14.0 (April XX, 2024)
|
|
2
|
+
- Added support for Semver matchers.
|
|
2
3
|
- Updated impression label to 'unsupported matcher type' when the matcher type is not supported by the SDK.
|
|
4
|
+
- Updated Split API client to include the flags spec version query parameter for the `splitChanges` and `auth` endpoints.
|
|
3
5
|
|
|
4
6
|
1.13.1 (January 10, 2024)
|
|
5
7
|
- Updated client `destroy` method to release SDK key immediately and avoid unexpected warning logs when a factory is created with the same SDK key after the previous one was destroyed.
|
package/cjs/evaluator/index.js
CHANGED
|
@@ -99,7 +99,7 @@ function getEvaluation(log, splitJSON, key, attributes, storage) {
|
|
|
99
99
|
if (splitJSON) {
|
|
100
100
|
var split_1 = Engine_1.Engine.parse(log, splitJSON, storage);
|
|
101
101
|
evaluation = split_1.getTreatment(key, attributes, evaluateFeature);
|
|
102
|
-
// If the storage is async and the evaluated
|
|
102
|
+
// If the storage is async and the evaluated flag uses segments or dependencies, evaluation is thenable
|
|
103
103
|
if ((0, thenable_1.thenable)(evaluation)) {
|
|
104
104
|
return evaluation.then(function (result) {
|
|
105
105
|
result.changeNumber = split_1.getChangeNumber();
|
|
@@ -4,7 +4,7 @@ exports.inListSemverMatcherContext = void 0;
|
|
|
4
4
|
var sets_1 = require("../../utils/lang/sets");
|
|
5
5
|
var Semver_1 = require("../../utils/Semver");
|
|
6
6
|
function inListSemverMatcherContext(ruleAttr) {
|
|
7
|
-
// @TODO
|
|
7
|
+
// @TODO ruleAttr validation should be done at the `parser` or `matchersTransform` level to reuse for all matchers
|
|
8
8
|
if (!ruleAttr || ruleAttr.length === 0)
|
|
9
9
|
throw new Error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type');
|
|
10
10
|
var listOfSemvers = new sets_1._Set(ruleAttr.map(function (version) { return new Semver_1.Semver(version).version; }));
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.whitelistTransform = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* Extract whitelist
|
|
5
|
+
* Extract whitelist array.
|
|
6
6
|
*/
|
|
7
7
|
function whitelistTransform(whitelistObject) {
|
|
8
8
|
return whitelistObject && whitelistObject.whitelist;
|
|
@@ -12,18 +12,18 @@ var thenable_1 = require("../../utils/promise/thenable");
|
|
|
12
12
|
var constants_1 = require("../../logger/constants");
|
|
13
13
|
function parser(log, conditions, storage) {
|
|
14
14
|
var predicates = [];
|
|
15
|
-
var
|
|
15
|
+
for (var i = 0; i < conditions.length; i++) {
|
|
16
16
|
var _a = conditions[i], matcherGroup = _a.matcherGroup, partitions = _a.partitions, label = _a.label, conditionType = _a.conditionType;
|
|
17
17
|
// transform data structure
|
|
18
18
|
var matchers = (0, matchersTransform_1.matchersTransform)(matcherGroup.matchers);
|
|
19
19
|
// create a set of pure functions from the matcher's dto
|
|
20
|
-
var expressions = matchers.map(function (matcherDto
|
|
20
|
+
var expressions = matchers.map(function (matcherDto) {
|
|
21
21
|
var matcher;
|
|
22
22
|
try {
|
|
23
23
|
matcher = (0, matchers_1.matcherFactory)(log, matcherDto, storage);
|
|
24
24
|
}
|
|
25
25
|
catch (error) {
|
|
26
|
-
log.error(constants_1.ENGINE_MATCHER_ERROR, [
|
|
26
|
+
log.error(constants_1.ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
|
|
27
27
|
}
|
|
28
28
|
// Evaluator function.
|
|
29
29
|
return function (key, attributes, splitEvaluator) {
|
|
@@ -34,12 +34,11 @@ function parser(log, conditions, storage) {
|
|
|
34
34
|
result = matcher(value, splitEvaluator);
|
|
35
35
|
}
|
|
36
36
|
catch (error) {
|
|
37
|
-
|
|
38
|
-
log.error(constants_1.ENGINE_MATCHER_ERROR, [matcherGroup.matchers[index].matcherType, error]);
|
|
37
|
+
log.error(constants_1.ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
function handleResult(result) {
|
|
42
|
-
log.debug(constants_1.ENGINE_MATCHER_RESULT, [
|
|
41
|
+
log.debug(constants_1.ENGINE_MATCHER_RESULT, [matcherDto.name, result, matcherDto.value, value]); // @ts-ignore
|
|
43
42
|
return Boolean(result ^ matcherDto.negate);
|
|
44
43
|
}
|
|
45
44
|
return (0, thenable_1.thenable)(result) ?
|
|
@@ -51,14 +50,10 @@ function parser(log, conditions, storage) {
|
|
|
51
50
|
if (expressions.length === 0) {
|
|
52
51
|
// reset any data collected during parsing
|
|
53
52
|
predicates = [];
|
|
54
|
-
|
|
53
|
+
// and break the loop
|
|
54
|
+
break;
|
|
55
55
|
}
|
|
56
56
|
predicates.push((0, condition_1.conditionContext)(log, (0, and_1.andCombinerContext)(log, expressions), treatments_1.Treatments.parse(partitions), label, conditionType));
|
|
57
|
-
};
|
|
58
|
-
for (var i = 0; i < conditions.length; i++) {
|
|
59
|
-
var state_1 = _loop_1(i);
|
|
60
|
-
if (state_1 === "break")
|
|
61
|
-
break;
|
|
62
57
|
}
|
|
63
58
|
// Instanciate evaluator given the set of conditions using if else if logic
|
|
64
59
|
return (0, ifelseif_1.ifElseIfCombinerContext)(log, predicates);
|
|
@@ -10,8 +10,8 @@ exports.codesDebug = info_1.codesInfo.concat([
|
|
|
10
10
|
[c.ENGINE_COMBINER_IFELSEIF, c.LOG_PREFIX_ENGINE_COMBINER + 'Treatment found: %s'],
|
|
11
11
|
[c.ENGINE_COMBINER_IFELSEIF_NO_TREATMENT, c.LOG_PREFIX_ENGINE_COMBINER + 'All predicates evaluated, no treatment found.'],
|
|
12
12
|
[c.ENGINE_BUCKET, c.LOG_PREFIX_ENGINE + ': using algo "murmur" bucket %s for key %s using seed %s - treatment %s'],
|
|
13
|
-
[c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[
|
|
14
|
-
[c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[
|
|
13
|
+
[c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] parent feature flag "%s" evaluated to "%s" with label "%s". %s evaluated treatment is part of %s ? %s.'],
|
|
14
|
+
[c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] will evaluate parent feature flag: "%s" with key: %s %s'],
|
|
15
15
|
[c.ENGINE_VALUE, c.LOG_PREFIX_ENGINE_VALUE + 'Extracted attribute `%s`. %s will be used for matching.'],
|
|
16
16
|
[c.ENGINE_SANITIZE, c.LOG_PREFIX_ENGINE + ':sanitize: Attempted to sanitize %s which should be of type %s. Sanitized and processed value => %s'],
|
|
17
17
|
[c.ENGINE_MATCHER_RESULT, c.LOG_PREFIX_ENGINE_MATCHER + '[%s] Result: %s. Rule value: %s. Evaluation value: %s'],
|
package/cjs/services/splitApi.js
CHANGED
|
@@ -32,16 +32,16 @@ function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
32
32
|
return splitHttpClient(url).then(function () { return true; }).catch(function () { return false; });
|
|
33
33
|
},
|
|
34
34
|
fetchAuth: function (userMatchingKeys) {
|
|
35
|
-
var url = urls.auth + "/v2/auth";
|
|
36
|
-
if (userMatchingKeys) { //
|
|
35
|
+
var url = urls.auth + "/v2/auth?s=" + constants_1.FLAGS_SPEC;
|
|
36
|
+
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
|
|
37
37
|
var queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
|
|
38
|
-
if (queryParams)
|
|
39
|
-
url += '
|
|
38
|
+
if (queryParams)
|
|
39
|
+
url += '&' + queryParams;
|
|
40
40
|
}
|
|
41
41
|
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(constants_1.TOKEN));
|
|
42
42
|
},
|
|
43
43
|
fetchSplitChanges: function (since, noCache, till) {
|
|
44
|
-
var url = urls.sdk + "/splitChanges?since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
|
|
44
|
+
var url = urls.sdk + "/splitChanges?s=" + constants_1.FLAGS_SPEC + "&since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
|
|
45
45
|
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(constants_1.SPLITS))
|
|
46
46
|
.catch(function (err) {
|
|
47
47
|
if (err.statusCode === 414)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getStorageHash = exports.KeyBuilder = exports.validatePrefix = void 0;
|
|
4
|
+
var constants_1 = require("../utils/constants");
|
|
4
5
|
var lang_1 = require("../utils/lang");
|
|
5
6
|
var murmur3_1 = require("../utils/murmur3/murmur3");
|
|
6
7
|
var everythingAtTheEnd = /[^.]+$/;
|
|
@@ -66,10 +67,10 @@ var KeyBuilder = /** @class */ (function () {
|
|
|
66
67
|
}());
|
|
67
68
|
exports.KeyBuilder = KeyBuilder;
|
|
68
69
|
/**
|
|
69
|
-
* Generates a murmur32 hash based on the authorization key
|
|
70
|
+
* Generates a murmur32 hash based on the authorization key, the feature flags filter query, and version of SplitChanges API.
|
|
70
71
|
* The hash is in hexadecimal format (8 characters max, 32 bits).
|
|
71
72
|
*/
|
|
72
73
|
function getStorageHash(settings) {
|
|
73
|
-
return (0, murmur3_1.hash)(settings.core.authorizationKey + "::" + settings.sync.__splitFiltersValidation.queryString).toString(16);
|
|
74
|
+
return (0, murmur3_1.hash)(settings.core.authorizationKey + "::" + settings.sync.__splitFiltersValidation.queryString + "::" + constants_1.FLAGS_SPEC).toString(16);
|
|
74
75
|
}
|
|
75
76
|
exports.getStorageHash = getStorageHash;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
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.NAMES_FN_LABEL = exports.SPLITS_FN_LABEL = exports.SPLIT_FN_LABEL = exports.TRACK_FN_LABEL = exports.GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS = exports.GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET = exports.GET_TREATMENTS_BY_FLAG_SETS = exports.GET_TREATMENTS_BY_FLAG_SET = exports.GET_TREATMENTS_WITH_CONFIG = exports.GET_TREATMENT_WITH_CONFIG = exports.GET_TREATMENTS = exports.GET_TREATMENT = 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 = 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 = void 0;
|
|
4
|
+
exports.FLAGS_SPEC = 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 = 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 = void 0;
|
|
5
5
|
// Special treatments
|
|
6
6
|
exports.CONTROL = 'control';
|
|
7
7
|
exports.CONTROL_WITH_CONFIG = {
|
|
@@ -90,3 +90,4 @@ exports.NON_REQUESTED = 1;
|
|
|
90
90
|
exports.DISABLED = 0;
|
|
91
91
|
exports.ENABLED = 1;
|
|
92
92
|
exports.PAUSED = 2;
|
|
93
|
+
exports.FLAGS_SPEC = '1.1';
|
package/esm/evaluator/index.js
CHANGED
|
@@ -93,7 +93,7 @@ function getEvaluation(log, splitJSON, key, attributes, storage) {
|
|
|
93
93
|
if (splitJSON) {
|
|
94
94
|
var split_1 = Engine.parse(log, splitJSON, storage);
|
|
95
95
|
evaluation = split_1.getTreatment(key, attributes, evaluateFeature);
|
|
96
|
-
// If the storage is async and the evaluated
|
|
96
|
+
// If the storage is async and the evaluated flag uses segments or dependencies, evaluation is thenable
|
|
97
97
|
if (thenable(evaluation)) {
|
|
98
98
|
return evaluation.then(function (result) {
|
|
99
99
|
result.changeNumber = split_1.getChangeNumber();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { _Set } from '../../utils/lang/sets';
|
|
2
2
|
import { Semver } from '../../utils/Semver';
|
|
3
3
|
export function inListSemverMatcherContext(ruleAttr) {
|
|
4
|
-
// @TODO
|
|
4
|
+
// @TODO ruleAttr validation should be done at the `parser` or `matchersTransform` level to reuse for all matchers
|
|
5
5
|
if (!ruleAttr || ruleAttr.length === 0)
|
|
6
6
|
throw new Error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type');
|
|
7
7
|
var listOfSemvers = new _Set(ruleAttr.map(function (version) { return new Semver(version).version; }));
|
|
@@ -9,18 +9,18 @@ import { thenable } from '../../utils/promise/thenable';
|
|
|
9
9
|
import { ENGINE_MATCHER_ERROR, ENGINE_MATCHER_RESULT } from '../../logger/constants';
|
|
10
10
|
export function parser(log, conditions, storage) {
|
|
11
11
|
var predicates = [];
|
|
12
|
-
var
|
|
12
|
+
for (var i = 0; i < conditions.length; i++) {
|
|
13
13
|
var _a = conditions[i], matcherGroup = _a.matcherGroup, partitions = _a.partitions, label = _a.label, conditionType = _a.conditionType;
|
|
14
14
|
// transform data structure
|
|
15
15
|
var matchers = matchersTransform(matcherGroup.matchers);
|
|
16
16
|
// create a set of pure functions from the matcher's dto
|
|
17
|
-
var expressions = matchers.map(function (matcherDto
|
|
17
|
+
var expressions = matchers.map(function (matcherDto) {
|
|
18
18
|
var matcher;
|
|
19
19
|
try {
|
|
20
20
|
matcher = matcherFactory(log, matcherDto, storage);
|
|
21
21
|
}
|
|
22
22
|
catch (error) {
|
|
23
|
-
log.error(ENGINE_MATCHER_ERROR, [
|
|
23
|
+
log.error(ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
|
|
24
24
|
}
|
|
25
25
|
// Evaluator function.
|
|
26
26
|
return function (key, attributes, splitEvaluator) {
|
|
@@ -31,12 +31,11 @@ export function parser(log, conditions, storage) {
|
|
|
31
31
|
result = matcher(value, splitEvaluator);
|
|
32
32
|
}
|
|
33
33
|
catch (error) {
|
|
34
|
-
|
|
35
|
-
log.error(ENGINE_MATCHER_ERROR, [matcherGroup.matchers[index].matcherType, error]);
|
|
34
|
+
log.error(ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
|
|
36
35
|
}
|
|
37
36
|
}
|
|
38
37
|
function handleResult(result) {
|
|
39
|
-
log.debug(ENGINE_MATCHER_RESULT, [
|
|
38
|
+
log.debug(ENGINE_MATCHER_RESULT, [matcherDto.name, result, matcherDto.value, value]); // @ts-ignore
|
|
40
39
|
return Boolean(result ^ matcherDto.negate);
|
|
41
40
|
}
|
|
42
41
|
return thenable(result) ?
|
|
@@ -48,14 +47,10 @@ export function parser(log, conditions, storage) {
|
|
|
48
47
|
if (expressions.length === 0) {
|
|
49
48
|
// reset any data collected during parsing
|
|
50
49
|
predicates = [];
|
|
51
|
-
|
|
50
|
+
// and break the loop
|
|
51
|
+
break;
|
|
52
52
|
}
|
|
53
53
|
predicates.push(conditionContext(log, andCombinerContext(log, expressions), Treatments.parse(partitions), label, conditionType));
|
|
54
|
-
};
|
|
55
|
-
for (var i = 0; i < conditions.length; i++) {
|
|
56
|
-
var state_1 = _loop_1(i);
|
|
57
|
-
if (state_1 === "break")
|
|
58
|
-
break;
|
|
59
54
|
}
|
|
60
55
|
// Instanciate evaluator given the set of conditions using if else if logic
|
|
61
56
|
return ifElseIfCombinerContext(log, predicates);
|
|
@@ -6,8 +6,8 @@ export var codesDebug = codesInfo.concat([
|
|
|
6
6
|
[c.ENGINE_COMBINER_IFELSEIF, c.LOG_PREFIX_ENGINE_COMBINER + 'Treatment found: %s'],
|
|
7
7
|
[c.ENGINE_COMBINER_IFELSEIF_NO_TREATMENT, c.LOG_PREFIX_ENGINE_COMBINER + 'All predicates evaluated, no treatment found.'],
|
|
8
8
|
[c.ENGINE_BUCKET, c.LOG_PREFIX_ENGINE + ': using algo "murmur" bucket %s for key %s using seed %s - treatment %s'],
|
|
9
|
-
[c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[
|
|
10
|
-
[c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[
|
|
9
|
+
[c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] parent feature flag "%s" evaluated to "%s" with label "%s". %s evaluated treatment is part of %s ? %s.'],
|
|
10
|
+
[c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] will evaluate parent feature flag: "%s" with key: %s %s'],
|
|
11
11
|
[c.ENGINE_VALUE, c.LOG_PREFIX_ENGINE_VALUE + 'Extracted attribute `%s`. %s will be used for matching.'],
|
|
12
12
|
[c.ENGINE_SANITIZE, c.LOG_PREFIX_ENGINE + ':sanitize: Attempted to sanitize %s which should be of type %s. Sanitized and processed value => %s'],
|
|
13
13
|
[c.ENGINE_MATCHER_RESULT, c.LOG_PREFIX_ENGINE_MATCHER + '[%s] Result: %s. Rule value: %s. Evaluation value: %s'],
|
package/esm/services/splitApi.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { splitHttpClientFactory } from './splitHttpClient';
|
|
2
2
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
3
|
-
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT } from '../utils/constants';
|
|
3
|
+
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT, FLAGS_SPEC } from '../utils/constants';
|
|
4
4
|
import { ERROR_TOO_MANY_SETS } from '../logger/constants';
|
|
5
5
|
var noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
|
|
6
6
|
function userKeyToQueryParam(userKey) {
|
|
@@ -29,16 +29,16 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
29
29
|
return splitHttpClient(url).then(function () { return true; }).catch(function () { return false; });
|
|
30
30
|
},
|
|
31
31
|
fetchAuth: function (userMatchingKeys) {
|
|
32
|
-
var url = urls.auth + "/v2/auth";
|
|
33
|
-
if (userMatchingKeys) { //
|
|
32
|
+
var url = urls.auth + "/v2/auth?s=" + FLAGS_SPEC;
|
|
33
|
+
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
|
|
34
34
|
var queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
|
|
35
|
-
if (queryParams)
|
|
36
|
-
url += '
|
|
35
|
+
if (queryParams)
|
|
36
|
+
url += '&' + queryParams;
|
|
37
37
|
}
|
|
38
38
|
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
|
|
39
39
|
},
|
|
40
40
|
fetchSplitChanges: function (since, noCache, till) {
|
|
41
|
-
var url = urls.sdk + "/splitChanges?since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
|
|
41
|
+
var url = urls.sdk + "/splitChanges?s=" + FLAGS_SPEC + "&since=" + since + (till ? '&till=' + till : '') + (filterQueryString || '');
|
|
42
42
|
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
|
|
43
43
|
.catch(function (err) {
|
|
44
44
|
if (err.statusCode === 414)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FLAGS_SPEC } from '../utils/constants';
|
|
1
2
|
import { startsWith } from '../utils/lang';
|
|
2
3
|
import { hash } from '../utils/murmur3/murmur3';
|
|
3
4
|
var everythingAtTheEnd = /[^.]+$/;
|
|
@@ -62,9 +63,9 @@ var KeyBuilder = /** @class */ (function () {
|
|
|
62
63
|
}());
|
|
63
64
|
export { KeyBuilder };
|
|
64
65
|
/**
|
|
65
|
-
* Generates a murmur32 hash based on the authorization key
|
|
66
|
+
* Generates a murmur32 hash based on the authorization key, the feature flags filter query, and version of SplitChanges API.
|
|
66
67
|
* The hash is in hexadecimal format (8 characters max, 32 bits).
|
|
67
68
|
*/
|
|
68
69
|
export function getStorageHash(settings) {
|
|
69
|
-
return hash(settings.core.authorizationKey + "::" + settings.sync.__splitFiltersValidation.queryString).toString(16);
|
|
70
|
+
return hash(settings.core.authorizationKey + "::" + settings.sync.__splitFiltersValidation.queryString + "::" + FLAGS_SPEC).toString(16);
|
|
70
71
|
}
|
package/package.json
CHANGED
package/src/evaluator/index.ts
CHANGED
|
@@ -151,7 +151,7 @@ function getEvaluation(
|
|
|
151
151
|
const split = Engine.parse(log, splitJSON, storage);
|
|
152
152
|
evaluation = split.getTreatment(key, attributes, evaluateFeature);
|
|
153
153
|
|
|
154
|
-
// If the storage is async and the evaluated
|
|
154
|
+
// If the storage is async and the evaluated flag uses segments or dependencies, evaluation is thenable
|
|
155
155
|
if (thenable(evaluation)) {
|
|
156
156
|
return evaluation.then(result => {
|
|
157
157
|
result.changeNumber = split.getChangeNumber();
|
|
@@ -2,7 +2,7 @@ import { _Set } from '../../utils/lang/sets';
|
|
|
2
2
|
import { Semver } from '../../utils/Semver';
|
|
3
3
|
|
|
4
4
|
export function inListSemverMatcherContext(ruleAttr: string[]) {
|
|
5
|
-
// @TODO
|
|
5
|
+
// @TODO ruleAttr validation should be done at the `parser` or `matchersTransform` level to reuse for all matchers
|
|
6
6
|
if (!ruleAttr || ruleAttr.length === 0) throw new Error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type');
|
|
7
7
|
|
|
8
8
|
const listOfSemvers = new _Set(ruleAttr.map((version) => new Semver(version).version));
|
|
@@ -31,7 +31,7 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
|
|
|
31
31
|
let type = matcherTypesMapper(matcherType);
|
|
32
32
|
// As default input data type we use string (even for ALL_KEYS)
|
|
33
33
|
let dataType = matcherDataTypes.STRING;
|
|
34
|
-
let value
|
|
34
|
+
let value = undefined;
|
|
35
35
|
|
|
36
36
|
if (type === matcherTypes.IN_SEGMENT) {
|
|
37
37
|
value = segmentTransform(userDefinedSegmentMatcherData as IInSegmentMatcherData);
|
|
@@ -98,6 +98,7 @@ export function matchersTransform(matchers: ISplitMatcher[]): IMatcherDto[] {
|
|
|
98
98
|
attribute, // attribute over we should do the matching, undefined means 'use the key'
|
|
99
99
|
negate, // should we negate the result?
|
|
100
100
|
type, // which kind of matcher we should evaluate
|
|
101
|
+
name: matcherType,// name of the matcher for logging purposes
|
|
101
102
|
value, // metadata used for the matching
|
|
102
103
|
dataType // runtime input data type
|
|
103
104
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ISplitMatcher } from '../../dtos/types';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Extract whitelist
|
|
4
|
+
* Extract whitelist array.
|
|
5
5
|
*/
|
|
6
6
|
export function whitelistTransform(whitelistObject: ISplitMatcher['whitelistMatcherData']) {
|
|
7
7
|
return whitelistObject && whitelistObject.whitelist;
|
|
@@ -28,12 +28,12 @@ export function parser(log: ILogger, conditions: ISplitCondition[], storage: ISt
|
|
|
28
28
|
const matchers = matchersTransform(matcherGroup.matchers);
|
|
29
29
|
|
|
30
30
|
// create a set of pure functions from the matcher's dto
|
|
31
|
-
const expressions = matchers.map((matcherDto: IMatcherDto
|
|
31
|
+
const expressions = matchers.map((matcherDto: IMatcherDto) => {
|
|
32
32
|
let matcher: ReturnType<typeof matcherFactory>;
|
|
33
33
|
try {
|
|
34
34
|
matcher = matcherFactory(log, matcherDto, storage);
|
|
35
35
|
} catch (error) {
|
|
36
|
-
log.error(ENGINE_MATCHER_ERROR, [
|
|
36
|
+
log.error(ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// Evaluator function.
|
|
@@ -45,13 +45,12 @@ export function parser(log: ILogger, conditions: ISplitCondition[], storage: ISt
|
|
|
45
45
|
try {
|
|
46
46
|
result = matcher(value, splitEvaluator);
|
|
47
47
|
} catch (error) {
|
|
48
|
-
|
|
49
|
-
log.error(ENGINE_MATCHER_ERROR, [matcherGroup.matchers[index].matcherType, error]);
|
|
48
|
+
log.error(ENGINE_MATCHER_ERROR, [matcherDto.name, error]);
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
function handleResult(result: boolean) {
|
|
54
|
-
log.debug(ENGINE_MATCHER_RESULT, [
|
|
53
|
+
log.debug(ENGINE_MATCHER_RESULT, [matcherDto.name, result, matcherDto.value, value]); // @ts-ignore
|
|
55
54
|
return Boolean(result ^ matcherDto.negate);
|
|
56
55
|
}
|
|
57
56
|
|
package/src/evaluator/types.ts
CHANGED
|
@@ -10,6 +10,7 @@ export interface IDependencyMatcherValue {
|
|
|
10
10
|
|
|
11
11
|
export interface IMatcherDto {
|
|
12
12
|
type: number
|
|
13
|
+
name: string
|
|
13
14
|
value?: string | number | boolean | string[] | IDependencyMatcherData | IBetweenMatcherData | IBetweenStringMatcherData | null
|
|
14
15
|
|
|
15
16
|
attribute: string | null
|
|
@@ -7,8 +7,8 @@ export const codesDebug: [number, string][] = codesInfo.concat([
|
|
|
7
7
|
[c.ENGINE_COMBINER_IFELSEIF, c.LOG_PREFIX_ENGINE_COMBINER + 'Treatment found: %s'],
|
|
8
8
|
[c.ENGINE_COMBINER_IFELSEIF_NO_TREATMENT, c.LOG_PREFIX_ENGINE_COMBINER + 'All predicates evaluated, no treatment found.'],
|
|
9
9
|
[c.ENGINE_BUCKET, c.LOG_PREFIX_ENGINE + ': using algo "murmur" bucket %s for key %s using seed %s - treatment %s'],
|
|
10
|
-
[c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[
|
|
11
|
-
[c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[
|
|
10
|
+
[c.ENGINE_MATCHER_DEPENDENCY, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] parent feature flag "%s" evaluated to "%s" with label "%s". %s evaluated treatment is part of %s ? %s.'],
|
|
11
|
+
[c.ENGINE_MATCHER_DEPENDENCY_PRE, c.LOG_PREFIX_ENGINE_MATCHER + '[IN_SPLIT_TREATMENT] will evaluate parent feature flag: "%s" with key: %s %s'],
|
|
12
12
|
[c.ENGINE_VALUE, c.LOG_PREFIX_ENGINE_VALUE + 'Extracted attribute `%s`. %s will be used for matching.'],
|
|
13
13
|
[c.ENGINE_SANITIZE, c.LOG_PREFIX_ENGINE + ':sanitize: Attempted to sanitize %s which should be of type %s. Sanitized and processed value => %s'],
|
|
14
14
|
[c.ENGINE_MATCHER_RESULT, c.LOG_PREFIX_ENGINE_MATCHER + '[%s] Result: %s. Rule value: %s. Evaluation value: %s'],
|
package/src/services/splitApi.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { splitHttpClientFactory } from './splitHttpClient';
|
|
|
4
4
|
import { ISplitApi } from './types';
|
|
5
5
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
6
6
|
import { ITelemetryTracker } from '../trackers/types';
|
|
7
|
-
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT } from '../utils/constants';
|
|
7
|
+
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT, FLAGS_SPEC } from '../utils/constants';
|
|
8
8
|
import { ERROR_TOO_MANY_SETS } from '../logger/constants';
|
|
9
9
|
|
|
10
10
|
const noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
|
|
@@ -44,17 +44,16 @@ export function splitApiFactory(
|
|
|
44
44
|
},
|
|
45
45
|
|
|
46
46
|
fetchAuth(userMatchingKeys?: string[]) {
|
|
47
|
-
let url = `${urls.auth}/v2/auth`;
|
|
48
|
-
if (userMatchingKeys) { //
|
|
47
|
+
let url = `${urls.auth}/v2/auth?s=${FLAGS_SPEC}`;
|
|
48
|
+
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
|
|
49
49
|
const queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
|
|
50
|
-
if (queryParams)
|
|
51
|
-
url += '?' + queryParams;
|
|
50
|
+
if (queryParams) url += '&' + queryParams;
|
|
52
51
|
}
|
|
53
52
|
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
|
|
54
53
|
},
|
|
55
54
|
|
|
56
55
|
fetchSplitChanges(since: number, noCache?: boolean, till?: number) {
|
|
57
|
-
const url = `${urls.sdk}/splitChanges?since=${since}${till ? '&till=' + till : ''}${filterQueryString || ''}`;
|
|
56
|
+
const url = `${urls.sdk}/splitChanges?s=${FLAGS_SPEC}&since=${since}${till ? '&till=' + till : ''}${filterQueryString || ''}`;
|
|
58
57
|
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
|
|
59
58
|
.catch((err) => {
|
|
60
59
|
if (err.statusCode === 414) settings.log.error(ERROR_TOO_MANY_SETS);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ISettings } from '../types';
|
|
2
|
+
import { FLAGS_SPEC } from '../utils/constants';
|
|
2
3
|
import { startsWith } from '../utils/lang';
|
|
3
4
|
import { hash } from '../utils/murmur3/murmur3';
|
|
4
5
|
|
|
@@ -81,9 +82,9 @@ export class KeyBuilder {
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
/**
|
|
84
|
-
* Generates a murmur32 hash based on the authorization key
|
|
85
|
+
* Generates a murmur32 hash based on the authorization key, the feature flags filter query, and version of SplitChanges API.
|
|
85
86
|
* The hash is in hexadecimal format (8 characters max, 32 bits).
|
|
86
87
|
*/
|
|
87
88
|
export function getStorageHash(settings: ISettings) {
|
|
88
|
-
return hash(`${settings.core.authorizationKey}::${settings.sync.__splitFiltersValidation.queryString}`).toString(16);
|
|
89
|
+
return hash(`${settings.core.authorizationKey}::${settings.sync.__splitFiltersValidation.queryString}::${FLAGS_SPEC}`).toString(16);
|
|
89
90
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ISplitMatcher } from '../../dtos/types';
|
|
2
2
|
/**
|
|
3
|
-
* Extract whitelist
|
|
3
|
+
* Extract whitelist array.
|
|
4
4
|
*/
|
|
5
5
|
export declare function whitelistTransform(whitelistObject: ISplitMatcher['whitelistMatcherData']): string[] | null | undefined;
|
|
@@ -8,6 +8,7 @@ export interface IDependencyMatcherValue {
|
|
|
8
8
|
}
|
|
9
9
|
export interface IMatcherDto {
|
|
10
10
|
type: number;
|
|
11
|
+
name: string;
|
|
11
12
|
value?: string | number | boolean | string[] | IDependencyMatcherData | IBetweenMatcherData | IBetweenStringMatcherData | null;
|
|
12
13
|
attribute: string | null;
|
|
13
14
|
negate: boolean;
|
|
@@ -16,7 +16,7 @@ export declare class KeyBuilder {
|
|
|
16
16
|
buildHashKey(): string;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* Generates a murmur32 hash based on the authorization key
|
|
19
|
+
* Generates a murmur32 hash based on the authorization key, the feature flags filter query, and version of SplitChanges API.
|
|
20
20
|
* The hash is in hexadecimal format (8 characters max, 32 bits).
|
|
21
21
|
*/
|
|
22
22
|
export declare function getStorageHash(settings: ISettings): string;
|