Skip to content

Commit 85ad78c

Browse files
Merge branch 'baseline_semver' into unsupported_matcher_type
2 parents 0980301 + 8e8b8b0 commit 85ad78c

File tree

15 files changed

+787
-43
lines changed

15 files changed

+787
-43
lines changed

CHANGES.txt

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
10.26.0 (April XX, 2024)
2+
- Updated @splitsoftware/splitio-commons package to version 1.14.0 that includes minor updates:
3+
- Added support for Semver matchers.
4+
15
10.25.2 (March 26, 2024)
26
- Updated some transitive dependencies for vulnerability fixes.
37
- Bugfixing - Added tslib as an explicit dependency to avoid issues with some package managers that don't resolve it automatically as a transitive dependency from @splitsoftware/splitio-commons (Related to issue https://github.com/splitio/javascript-client/issues/795).
@@ -107,7 +111,7 @@
107111
- Bugfixing - Fixed an issue with `connectionTimeout` options params of Redis storage, that was being ignored and not passed down to the underlying ioredis client.
108112
- Bugfixing - Updated the validation of some SDK configuration params to log errors and throw exceptions with clear descriptions of the invalid setup:
109113
- If passing a non-string value to `sync.impressionsMode`, the SDK logs the error: "you passed an invalid impressionsMode config param. It should be one of the following values: 'OPTIMIZED', 'DEBUG'. Defaulting to 'OPTIMIZED'.".
110-
- If passing 'REDIS' storage type without setting `mode` to 'consumer', the SDK logs the error: "The provided REDIS storage is invalid for this mode. It requires 'consumer' mode. Fallbacking into default MEMORY storage.".
114+
- If passing 'REDIS' storage type without setting `mode` to 'consumer', the SDK logs the error: "The provided REDIS storage is invalid for this mode. It requires 'consumer' mode. Fallback into default MEMORY storage.".
111115
- If passing 'consumer' mode without setting `storage.type` to 'REDIS', the SDK throws an exception with message: "A REDIS storage is required on consumer mode.".
112116

113117
- NOTABLE CHANGE: since version 10.18.0, the SDK has been refactored to use @splitsoftware/splitio-commons package in order to reuse core modules shared across all JavaScript-based SDKs. Most internal modules have been moved and renamed,
@@ -175,7 +179,7 @@
175179
10.15.4 (Mar 17, 2021)
176180
- Updated Streaming logic with some improvements and fixes, including:
177181
- Updated SSE error handling.
178-
- Extended publishers tracking to support multiregion infrastructure.
182+
- Extended publishers tracking to support multi-region infrastructure.
179183
- Enforced revalidation for requests stored in local caches, like proxies or browsers.
180184
- Bugfixing - In NodeJS, fetch new segments captured due to streaming notifications.
181185
- Updated some dependencies, including a vulnerability fix.
@@ -300,11 +304,11 @@
300304
- Added Block Until Ready functionality support for consumer clients (Redis mode on Node) to make integration code work the same between modes.
301305
- Added more Input and Usage Validation rules, including an extra label for impressions when the SDK is not ready.
302306
- Updated the SDK Redis adapter to handle pending commands when disconnecting from the Redis server.
303-
- Bugfixing - Clearing up readyTimeout after we don't need it anymore. It also fixes the missleading SDK_READY_TIMED_OUT error log when using Redis.
307+
- Bugfixing - Clearing up readyTimeout after we don't need it anymore. It also fixes the misleading SDK_READY_TIMED_OUT error log when using Redis.
304308

305309
10.7.0 (Apr 30, 2019)
306310
- Added Block Until Ready functionality to the manager, shared with the main client. Now you can subscribe to SDK events or use the .ready() promise from the manager as well.
307-
- Added Dynamic Configurations support through two new methods that mimick the regular ones, changing the type of what is returned.
311+
- Added Dynamic Configurations support through two new methods that mimic the regular ones, changing the type of what is returned.
308312
- getTreatmentWithConfig: Same as getTreatment, but instead of a string it returns a map with treatment and config as a stringified JSON.
309313
- getTreatmentWithConfig: Same as getTreatments, but instead of a map of string it returns a map of objects with treatment and config as a stringified JSON.
310314
- Added configs to SplitViews returned by the manager module.
@@ -318,7 +322,7 @@
318322

319323
10.6.0 (Feb 12, 2019)
320324
- BREAKING CHANGE: Updated impressions cache for Redis storage to reduce the amount of Redis operations by using a single queue (Must use Synchronizer 2.x or above with this or newer SDK versions).
321-
- Added stricter validations to the input of the SDK api to provide better and faster feedback in case of missuse. We want our users to be able to diagnose issues sooner,
325+
- Added stricter validations to the input of the SDK api to provide better and faster feedback in case of misuse. We want our users to be able to diagnose issues sooner,
322326
instead of when you can't find the data you're looking for. As part of this, some error logs (just logs) will be visible even with the SDK Logger disabled.
323327
- Updated getTreatments to have it's own latency metric for the whole operation, instead of one per each feature evaluation.
324328
- Updated default values on configuration for NodeJS.
@@ -385,13 +389,13 @@
385389
- Migrated source code to es modules.
386390
- Localhost mode uses fewer dependencies now.
387391
- Removed flowtype since it was not used anymore.
388-
- Udpated to last node LTS.
392+
- Updated to last node LTS.
389393
- Added package-lock.json.
390394
- Fixed eslint configuration.
391395

392396
9.4.0 (Jan 12, 2018)
393397
- Adding support for client.track method, for tracking custom events.
394-
- Adding trafficType as an optional core setting. If provided on the browser it will be binded to the client as the key.
398+
- Adding trafficType as an optional core setting. If provided on the browser it will be bound to the client as the key.
395399
- TypeScript declarations polishing.
396400
- Updated SDK labels.
397401
- Bugfixing - Shared clients (browser) were ready even if the main client was not.
@@ -593,10 +597,10 @@ var treatmentsMap = client.getTreatments('CUSTOMER_KEY', ['Feature_flag_1', 'Fea
593597
const client = SplitFactory(config);
594598

595599
// Redis in NodeJS is async so we can use async/await syntax
596-
const treatment = await client.getTreatment('my-feature-comming-from-localstorage');
600+
const treatment = await client.getTreatment('my-feature-coming-from-localstorage');
597601

598602
// or just use the returned promise
599-
client.getTreatment('my-feature-comming-from-localstorage').then(treatment => {
603+
client.getTreatment('my-feature-coming-from-localstorage').then(treatment => {
600604
// do something with the treatment
601605
});
602606
```
@@ -652,7 +656,7 @@ var treatmentsMap = client.getTreatments('CUSTOMER_KEY', ['Feature_flag_1', 'Fea
652656

653657
client.getTreatment('my_feature') === 'on'; // true
654658

655-
factory.settings.features.my_feature = 'off'; // Apply this cache programatically
659+
factory.settings.features.my_feature = 'off'; // Apply this cache programmatically
656660

657661
client.getTreatment('my_feature') === 'off'; // Some time after you will be able to verify this
658662
```
@@ -728,7 +732,7 @@ var treatmentsMap = client.getTreatments('CUSTOMER_KEY', ['Feature_flag_1', 'Fea
728732
```html
729733
<script src="//cdn.split.io/sdk/split-5.0.0.min.js"></script>
730734
<script>
731-
// instanciation
735+
// instantiation
732736
var dynamic1 = splitio({
733737
core: {
734738
authorizationKey: '<your-token>',

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@splitsoftware/splitio",
3-
"version": "10.25.2",
3+
"version": "10.25.3-rc.1",
44
"description": "Split SDK",
55
"files": [
66
"README.md",
@@ -40,7 +40,7 @@
4040
"node": ">=6"
4141
},
4242
"dependencies": {
43-
"@splitsoftware/splitio-commons": "1.13.2-rc.2",
43+
"@splitsoftware/splitio-commons": "1.13.2-rc.6",
4444
"@types/google.analytics": "0.0.40",
4545
"@types/ioredis": "^4.28.0",
4646
"bloom-filters": "^3.0.0",
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import sinon from 'sinon';
2+
import splitChangesMock1 from '../mocks/splitchanges.since.-1.semver.json';
3+
4+
import { SplitFactory } from '../../';
5+
6+
const listener = {
7+
logImpression: sinon.stub()
8+
};
9+
10+
const config = {
11+
core: {
12+
authorizationKey: '<fake-token>',
13+
key: 'emi@split.io'
14+
},
15+
urls: {
16+
sdk: 'https://sdk.evaluation-semver/api',
17+
events: 'https://events.evaluation-semver/api'
18+
},
19+
sync: {
20+
impressionsMode: 'DEBUG'
21+
},
22+
impressionListener: listener,
23+
streamingEnabled: false
24+
};
25+
26+
export default async function (fetchMock, assert) {
27+
28+
fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=-1', { status: 200, body: splitChangesMock1 });
29+
fetchMock.getOnce(config.urls.sdk + '/splitChanges?since=1675259356568', { status: 200, body: { splits: [], since: 1675259356568, till: 1675259356568 } });
30+
fetchMock.getOnce(config.urls.sdk + '/mySegments/emi%40split.io', { status: 200, body: { mySegments: [] } });
31+
fetchMock.getOnce(config.urls.sdk + '/mySegments/2nd', { status: 200, body: { mySegments: [] } });
32+
33+
const splitio = SplitFactory(config);
34+
const client = splitio.client();
35+
36+
await client.ready();
37+
38+
// EQUAL_TO_SEMVER matcher
39+
assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is equal to `1.22.9`');
40+
assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored');
41+
assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not equal to `1.22.9`');
42+
assert.equal(client.getTreatment('semver_equalto', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type');
43+
assert.equal(client.getTreatment('semver_equalto'), 'off', 'the rule will return `off` if attribute `version` is not provided');
44+
45+
// IN_LIST_SEMVER matcher
46+
assert.equal(client.getTreatment('semver_inlist', { 'version': '2.1.0' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)');
47+
assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9' }), 'on', 'the rule will return `on` if attribute `version` is in list (`1.22.9`, `2.1.0`)');
48+
assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored');
49+
assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9-rc.0' }), 'off', 'the rule will return `off` if attribute `version` is not in list (`1.22.9`, `2.1.0`)');
50+
assert.equal(client.getTreatment('semver_inlist', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type');
51+
52+
// GREATER_THAN_OR_EQUAL_TO_SEMVER matcher
53+
assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.23.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`');
54+
assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9' }).semver_greater_or_equalto, 'on', 'the rule will return `on` if attribute `version` is greater than or equal to `1.22.9`');
55+
assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9+build' }).semver_greater_or_equalto, 'on', 'build metadata is ignored');
56+
assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9-rc.0' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`');
57+
assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.21.9' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is not greater than or equal to `1.22.9`');
58+
assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': 'invalid' }).semver_greater_or_equalto, 'off', 'the rule will return `off` if attribute `version` is an invalid semver value');
59+
60+
// LESS_THAN_OR_EQUAL_TO_SEMVER matcher
61+
assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.11' }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not less than or equal to `1.22.9`');
62+
assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`');
63+
assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9+build' }), { treatment: 'on', config: null }, 'build metadata is ignored');
64+
assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9-rc.0' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`');
65+
assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.21.9' }), { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is less than or equal to `1.22.9`');
66+
assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': {} }), { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not the expected type');
67+
68+
const client2 = splitio.client('2nd');
69+
await client2.ready();
70+
71+
// BETWEEN_SEMVER matcher
72+
assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '2.1.1' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`');
73+
assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '2.1.0+build' }).semver_between, { treatment: 'on', config: null }, 'build metadata is ignored');
74+
assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.25.0' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`');
75+
assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.22.9' }).semver_between, { treatment: 'on', config: null }, 'the rule will return `on` if attribute `version` is between `1.22.9` and `2.1.0`');
76+
assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '1.22.9-rc.0' }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not between `1.22.9` and `2.1.0`');
77+
assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': [] }).semver_between, { treatment: 'off', config: null }, 'the rule will return `off` if attribute `version` is not the expected type');
78+
79+
// Evaluation of a flag with unsupported matcher
80+
assert.equal(client2.getTreatment('flag_with_unsupported_matcher'), 'control', 'evaluation of a flag with an unsupported matcher should return control');
81+
82+
let POSTED_IMPRESSIONS_COUNT;
83+
84+
fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', (_, opts) => {
85+
86+
const payload = JSON.parse(opts.body);
87+
88+
function validateImpressionData(featureFlagName, expectedImpressionCount, expectedOnCount, expectedLabel, expectedTreatment = 'on') {
89+
const impressions = payload.find(e => e.f === featureFlagName).i;
90+
91+
assert.equal(impressions.length, expectedImpressionCount, `We should have ${expectedImpressionCount} impressions for the feature flag ${featureFlagName}`);
92+
assert.equal(impressions.filter((imp) => imp.r === expectedLabel && imp.t === expectedTreatment).length, expectedOnCount, `${expectedOnCount} impression with 'on' treatment and label ${expectedLabel}`);
93+
}
94+
95+
validateImpressionData('semver_equalto', 5, 1, 'equal to semver');
96+
validateImpressionData('semver_inlist', 5, 2, 'in list semver');
97+
validateImpressionData('semver_greater_or_equalto', 6, 3, 'greater than or equal to semver');
98+
validateImpressionData('semver_less_or_equalto', 6, 4, 'less than or equal to semver');
99+
validateImpressionData('semver_between', 6, 3, 'between semver');
100+
validateImpressionData('flag_with_unsupported_matcher', 1, 1, 'unsupported matcher type', 'control');
101+
102+
POSTED_IMPRESSIONS_COUNT = payload.reduce((acc, curr) => acc + curr.i.length, 0);
103+
104+
return 200;
105+
});
106+
107+
await Promise.all([client.destroy(), client2.destroy()]);
108+
109+
setTimeout(() => {
110+
assert.equal(listener.logImpression.callCount, POSTED_IMPRESSIONS_COUNT, 'Impression listener should be called once per each impression generated.');
111+
112+
assert.end();
113+
}, 0);
114+
}

0 commit comments

Comments
 (0)