Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v1.26.0 #804

Merged
merged 26 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7bd7ade
Update E2E tests
EmilianoSanchez Apr 3, 2024
440ac15
Update query param name and value
EmilianoSanchez Apr 8, 2024
ff12f34
Add flags spec query param to auth requests
EmilianoSanchez Apr 9, 2024
0980301
Fix typo
EmilianoSanchez Apr 10, 2024
a678e26
Fix some typos
EmilianoSanchez Apr 11, 2024
282b560
Merge pull request #800 from splitio/fix_typos
EmilianoSanchez Apr 12, 2024
451255a
Update JS-commons and add E2E tests for Semver matchers
EmilianoSanchez Apr 12, 2024
525024e
Merge branch 'development' into baseline_semver
EmilianoSanchez Apr 12, 2024
c2ea719
Merge branch 'baseline_semver' into SDKS_8266_semver_matchers
EmilianoSanchez Apr 12, 2024
09f6102
Handling invalid semver values
EmilianoSanchez Apr 15, 2024
061344c
Prepare rc
EmilianoSanchez Apr 15, 2024
f5d11af
Update version
EmilianoSanchez Apr 15, 2024
e9acdeb
Assert some corner cases
EmilianoSanchez Apr 16, 2024
9812395
Upgrade JS-commons
EmilianoSanchez Apr 16, 2024
8ecfc78
Prepare rc
EmilianoSanchez Apr 16, 2024
03324e1
Add changelog entry
EmilianoSanchez Apr 18, 2024
8e8b8b0
Merge pull request #801 from splitio/SDKS_8266_semver_matchers
EmilianoSanchez Apr 18, 2024
85ad78c
Merge branch 'baseline_semver' into unsupported_matcher_type
EmilianoSanchez Apr 18, 2024
e22f56f
rc
EmilianoSanchez Apr 18, 2024
328c811
fix tests adding new query param
EmilianoSanchez Apr 18, 2024
3965467
rollback ci-cd
EmilianoSanchez Apr 18, 2024
6f130c9
Merge pull request #798 from splitio/unsupported_matcher_type
NicoZelaya Apr 30, 2024
98a966c
Update JS-commons
EmilianoSanchez May 3, 2024
f5fc4ad
rc
EmilianoSanchez May 3, 2024
83fa5e6
stable version
EmilianoSanchez May 6, 2024
0dfd83a
Merge pull request #802 from splitio/baseline_semver
EmilianoSanchez May 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
10.26.0 (May 6, 2024)
- Updated @splitsoftware/splitio-commons package to version 1.14.0 that includes minor updates:
- Added support for targeting rules based on semantic versions (https://semver.org/).
- Added special impression label "targeting rule type unsupported by sdk" when the matcher type is not supported by the SDK, which returns 'control' treatment.
- Updated Split API client to include the flags spec version query parameter for the `splitChanges` and `auth` endpoints.

10.25.2 (March 26, 2024)
- Updated some transitive dependencies for vulnerability fixes.
- 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).
Expand Down Expand Up @@ -107,7 +113,7 @@
- Bugfixing - Fixed an issue with `connectionTimeout` options params of Redis storage, that was being ignored and not passed down to the underlying ioredis client.
- Bugfixing - Updated the validation of some SDK configuration params to log errors and throw exceptions with clear descriptions of the invalid setup:
- 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'.".
- 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.".
- 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.".
- 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.".

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

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

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

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

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

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

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

factory.settings.features.my_feature = 'off'; // Apply this cache programatically
factory.settings.features.my_feature = 'off'; // Apply this cache programmatically

client.getTreatment('my_feature') === 'off'; // Some time after you will be able to verify this
```
Expand Down Expand Up @@ -728,7 +734,7 @@ var treatmentsMap = client.getTreatments('CUSTOMER_KEY', ['Feature_flag_1', 'Fea
```html
<script src="//cdn.split.io/sdk/split-5.0.0.min.js"></script>
<script>
// instanciation
// instantiation
var dynamic1 = splitio({
core: {
authorizationKey: '<your-token>',
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@splitsoftware/splitio",
"version": "10.25.2",
"version": "10.26.0",
"description": "Split SDK",
"files": [
"README.md",
Expand Down Expand Up @@ -40,7 +40,7 @@
"node": ">=6"
},
"dependencies": {
"@splitsoftware/splitio-commons": "1.13.1",
"@splitsoftware/splitio-commons": "1.14.0",
"@types/google.analytics": "0.0.40",
"@types/ioredis": "^4.28.0",
"bloom-filters": "^3.0.0",
Expand Down
114 changes: 114 additions & 0 deletions src/__tests__/browserSuites/evaluations-semver.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import sinon from 'sinon';
import splitChangesMock1 from '../mocks/splitchanges.since.-1.semver.json';

import { SplitFactory } from '../../';

const listener = {
logImpression: sinon.stub()
};

const config = {
core: {
authorizationKey: '<fake-token>',
key: 'emi@split.io'
},
urls: {
sdk: 'https://sdk.evaluation-semver/api',
events: 'https://events.evaluation-semver/api'
},
sync: {
impressionsMode: 'DEBUG'
},
impressionListener: listener,
streamingEnabled: false
};

export default async function (fetchMock, assert) {

fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=-1', { status: 200, body: splitChangesMock1 });
fetchMock.getOnce(config.urls.sdk + '/splitChanges?s=1.1&since=1675259356568', { status: 200, body: { splits: [], since: 1675259356568, till: 1675259356568 } });
fetchMock.getOnce(config.urls.sdk + '/mySegments/emi%40split.io', { status: 200, body: { mySegments: [] } });
fetchMock.getOnce(config.urls.sdk + '/mySegments/2nd', { status: 200, body: { mySegments: [] } });

const splitio = SplitFactory(config);
const client = splitio.client();

await client.ready();

// EQUAL_TO_SEMVER matcher
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`');
assert.equal(client.getTreatment('semver_equalto', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored');
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`');
assert.equal(client.getTreatment('semver_equalto', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type');
assert.equal(client.getTreatment('semver_equalto'), 'off', 'the rule will return `off` if attribute `version` is not provided');

// IN_LIST_SEMVER matcher
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`)');
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`)');
assert.equal(client.getTreatment('semver_inlist', { 'version': '1.22.9+build' }), 'off', 'build metadata is not ignored');
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`)');
assert.equal(client.getTreatment('semver_inlist', { 'version': null }), 'off', 'the rule will return `off` if attribute `version` is not the expected type');

// GREATER_THAN_OR_EQUAL_TO_SEMVER matcher
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`');
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`');
assert.equal(client.getTreatments(['semver_greater_or_equalto'], { 'version': '1.22.9+build' }).semver_greater_or_equalto, 'on', 'build metadata is ignored');
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`');
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`');
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');

// LESS_THAN_OR_EQUAL_TO_SEMVER matcher
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`');
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`');
assert.deepEqual(client.getTreatmentWithConfig('semver_less_or_equalto', { 'version': '1.22.9+build' }), { treatment: 'on', config: null }, 'build metadata is ignored');
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`');
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`');
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');

const client2 = splitio.client('2nd');
await client2.ready();

// BETWEEN_SEMVER matcher
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`');
assert.deepEqual(client2.getTreatmentsWithConfig(['semver_between'], { 'version': '2.1.0+build' }).semver_between, { treatment: 'on', config: null }, 'build metadata is ignored');
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`');
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`');
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`');
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');

// Evaluation of a flag with unsupported matcher
assert.equal(client2.getTreatment('flag_with_unsupported_matcher'), 'control', 'evaluation of a flag with an unsupported matcher should return control');

let POSTED_IMPRESSIONS_COUNT;

fetchMock.postOnce(config.urls.events + '/testImpressions/bulk', (_, opts) => {

const payload = JSON.parse(opts.body);

function validateImpressionData(featureFlagName, expectedImpressionCount, expectedOnCount, expectedLabel, expectedTreatment = 'on') {
const impressions = payload.find(e => e.f === featureFlagName).i;

assert.equal(impressions.length, expectedImpressionCount, `We should have ${expectedImpressionCount} impressions for the feature flag ${featureFlagName}`);
assert.equal(impressions.filter((imp) => imp.r === expectedLabel && imp.t === expectedTreatment).length, expectedOnCount, `${expectedOnCount} impression with 'on' treatment and label ${expectedLabel}`);
}

validateImpressionData('semver_equalto', 5, 1, 'equal to semver');
validateImpressionData('semver_inlist', 5, 2, 'in list semver');
validateImpressionData('semver_greater_or_equalto', 6, 3, 'greater than or equal to semver');
validateImpressionData('semver_less_or_equalto', 6, 4, 'less than or equal to semver');
validateImpressionData('semver_between', 6, 3, 'between semver');
validateImpressionData('flag_with_unsupported_matcher', 1, 1, 'targeting rule type unsupported by sdk', 'control');

POSTED_IMPRESSIONS_COUNT = payload.reduce((acc, curr) => acc + curr.i.length, 0);

return 200;
});

await Promise.all([client.destroy(), client2.destroy()]);

setTimeout(() => {
assert.equal(listener.logImpression.callCount, POSTED_IMPRESSIONS_COUNT, 'Impression listener should be called once per each impression generated.');

assert.end();
}, 0);
}
Loading
Loading