Skip to content

Commit e06c342

Browse files
Merge pull request #804 from splitio/development
Release v1.26.0
2 parents 127c3c3 + 0dfd83a commit e06c342

File tree

59 files changed

+1091
-345
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1091
-345
lines changed

CHANGES.txt

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
10.26.0 (May 6, 2024)
2+
- Updated @splitsoftware/splitio-commons package to version 1.14.0 that includes minor updates:
3+
- Added support for targeting rules based on semantic versions (https://semver.org/).
4+
- Added special impression label "targeting rule type unsupported by sdk" when the matcher type is not supported by the SDK, which returns 'control' treatment.
5+
- Updated Split API client to include the flags spec version query parameter for the `splitChanges` and `auth` endpoints.
6+
17
10.25.2 (March 26, 2024)
28
- Updated some transitive dependencies for vulnerability fixes.
39
- 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 +113,7 @@
107113
- Bugfixing - Fixed an issue with `connectionTimeout` options params of Redis storage, that was being ignored and not passed down to the underlying ioredis client.
108114
- Bugfixing - Updated the validation of some SDK configuration params to log errors and throw exceptions with clear descriptions of the invalid setup:
109115
- 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.".
116+
- 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.".
111117
- 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.".
112118

113119
- 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 +181,7 @@
175181
10.15.4 (Mar 17, 2021)
176182
- Updated Streaming logic with some improvements and fixes, including:
177183
- Updated SSE error handling.
178-
- Extended publishers tracking to support multiregion infrastructure.
184+
- Extended publishers tracking to support multi-region infrastructure.
179185
- Enforced revalidation for requests stored in local caches, like proxies or browsers.
180186
- Bugfixing - In NodeJS, fetch new segments captured due to streaming notifications.
181187
- Updated some dependencies, including a vulnerability fix.
@@ -300,11 +306,11 @@
300306
- Added Block Until Ready functionality support for consumer clients (Redis mode on Node) to make integration code work the same between modes.
301307
- Added more Input and Usage Validation rules, including an extra label for impressions when the SDK is not ready.
302308
- 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.
309+
- 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.
304310

305311
10.7.0 (Apr 30, 2019)
306312
- 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.
313+
- Added Dynamic Configurations support through two new methods that mimic the regular ones, changing the type of what is returned.
308314
- getTreatmentWithConfig: Same as getTreatment, but instead of a string it returns a map with treatment and config as a stringified JSON.
309315
- 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.
310316
- Added configs to SplitViews returned by the manager module.
@@ -318,7 +324,7 @@
318324

319325
10.6.0 (Feb 12, 2019)
320326
- 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,
327+
- 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,
322328
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.
323329
- Updated getTreatments to have it's own latency metric for the whole operation, instead of one per each feature evaluation.
324330
- Updated default values on configuration for NodeJS.
@@ -385,13 +391,13 @@
385391
- Migrated source code to es modules.
386392
- Localhost mode uses fewer dependencies now.
387393
- Removed flowtype since it was not used anymore.
388-
- Udpated to last node LTS.
394+
- Updated to last node LTS.
389395
- Added package-lock.json.
390396
- Fixed eslint configuration.
391397

392398
9.4.0 (Jan 12, 2018)
393399
- 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.
400+
- Adding trafficType as an optional core setting. If provided on the browser it will be bound to the client as the key.
395401
- TypeScript declarations polishing.
396402
- Updated SDK labels.
397403
- Bugfixing - Shared clients (browser) were ready even if the main client was not.
@@ -593,10 +599,10 @@ var treatmentsMap = client.getTreatments('CUSTOMER_KEY', ['Feature_flag_1', 'Fea
593599
const client = SplitFactory(config);
594600

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

598604
// or just use the returned promise
599-
client.getTreatment('my-feature-comming-from-localstorage').then(treatment => {
605+
client.getTreatment('my-feature-coming-from-localstorage').then(treatment => {
600606
// do something with the treatment
601607
});
602608
```
@@ -652,7 +658,7 @@ var treatmentsMap = client.getTreatments('CUSTOMER_KEY', ['Feature_flag_1', 'Fea
652658

653659
client.getTreatment('my_feature') === 'on'; // true
654660

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

657663
client.getTreatment('my_feature') === 'off'; // Some time after you will be able to verify this
658664
```
@@ -728,7 +734,7 @@ var treatmentsMap = client.getTreatments('CUSTOMER_KEY', ['Feature_flag_1', 'Fea
728734
```html
729735
<script src="//cdn.split.io/sdk/split-5.0.0.min.js"></script>
730736
<script>
731-
// instanciation
737+
// instantiation
732738
var dynamic1 = splitio({
733739
core: {
734740
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.26.0",
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.1",
43+
"@splitsoftware/splitio-commons": "1.14.0",
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?s=1.1&since=-1', { status: 200, body: splitChangesMock1 });
29+
fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&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, 'targeting rule type unsupported by sdk', '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)