Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
2.1.0 (April 1, 2025)
- Added a new optional `properties` argument to the options object of the `getTreatments` action creator, allowing to pass a map of properties to append to the generated impressions sent to Split backend. Read more in our docs.
- Updated @splitsoftware/splitio package to version 11.2.0 that includes some minor updates:
- Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on `SplitView` type objects. Read more in our docs.
- Added two new configuration options for the SDK's `LOCALSTORAGE` storage type to control the behavior of the persisted rollout plan cache in the browser:
- `storage.expirationDays` to specify the validity period of the rollout plan cache in days.
- `storage.clearOnInit` to clear the rollout plan cache on SDK initialization.
- Updated SDK_READY_FROM_CACHE event when using the `LOCALSTORAGE` storage type to be emitted alongside the SDK_READY event if it has not already been emitted.

2.0.1 (December 5, 2024)
- Updated @splitsoftware/splitio package to version 11.0.3 that includes some improvements and bugfixes.
- Bugfixing - Fixed issue where the `SplitIO` namespace from `@splitsoftware/splitio` was not accessible through the library, enabling users to use the namespace without additional imports (Related to https://github.com/splitio/redux-client/issues/130).
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright © 2024 Split Software, Inc.
Copyright © 2025 Split Software, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Split has built and maintains SDKs for:
* .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK)
* Android [Github](https://github.com/splitio/android-client) [Docs](https://help.split.io/hc/en-us/articles/360020343291-Android-SDK)
* Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/6495326064397-Angular-utilities)
* Elixir thin-client [Github](https://github.com/splitio/elixir-thin-client) [Docs](https://help.split.io/hc/en-us/articles/26988707417869-Elixir-Thin-Client-SDK)
* Flutter [Github](https://github.com/splitio/flutter-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/8096158017165-Flutter-plugin)
* GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK)
* iOS [Github](https://github.com/splitio/ios-client) [Docs](https://help.split.io/hc/en-us/articles/360020401491-iOS-SDK)
Expand All @@ -112,4 +113,4 @@ For a comprehensive list of open source projects visit our [Github page](https:/

**Learn more about Split:**

Visit [split.io/product](https://www.split.io/product) for an overview of Split, or visit our documentation at [help.split.io](http://help.split.io) for more detailed information.
Visit [split.io/product](https://www.split.io/product) for an overview of Split, or visit our documentation at [help.split.io](https://help.split.io) for more detailed information.
299 changes: 143 additions & 156 deletions package-lock.json

Large diffs are not rendered by default.

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-redux",
"version": "2.0.1",
"version": "2.1.0",
"description": "A library to easily use Split JS SDK with Redux and React Redux",
"main": "cjs/index.js",
"module": "esm/index.js",
Expand Down Expand Up @@ -59,7 +59,7 @@
},
"homepage": "https://github.com/splitio/redux-client#readme",
"dependencies": {
"@splitsoftware/splitio": "11.0.3",
"@splitsoftware/splitio": "11.2.0",
"tslib": "^2.3.1"
},
"devDependencies": {
Expand Down
40 changes: 21 additions & 19 deletions src/__tests__/asyncActions.browser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,11 @@ describe('getTreatments', () => {
});

// getting the evaluation result and validating it matches the results from SDK
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenCalledWith(['split1'], undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenCalledWith(['split1'], undefined, undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveReturnedWith(actions[0].payload.treatments);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenLastCalledWith(['split2'], undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenLastCalledWith(['split2'], undefined, undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(actions[1].payload.treatments);
expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toHaveBeenLastCalledWith(['set1'], undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toHaveBeenLastCalledWith(['set1'], undefined, undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toHaveLastReturnedWith(actions[2].payload.treatments);

expect(getClient(splitSdk).evalOnUpdate).toEqual({});
Expand Down Expand Up @@ -258,7 +258,7 @@ describe('getTreatments', () => {
treatments: expect.any(Object)
}
});
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenNthCalledWith(1, ['split1'], undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenNthCalledWith(1, ['split1'], undefined, undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveNthReturnedWith(1, action.payload.treatments);

// getting the 2nd evaluation result and validating it matches the results from SDK
Expand All @@ -270,7 +270,7 @@ describe('getTreatments', () => {
treatments: expect.any(Object)
}
});
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenNthCalledWith(2, ['split2', 'split3'], attributes);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenNthCalledWith(2, ['split2', 'split3'], attributes, undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveNthReturnedWith(2, action.payload.treatments);
expect(getClient(splitSdk).evalOnUpdate).toEqual({}); // control assertion - cbs scheduled for update
expect(getClient(splitSdk).evalOnReady.length).toEqual(2); // control assertion - cbs scheduled for ready
Expand All @@ -294,8 +294,8 @@ describe('getTreatments', () => {
expect(store.getActions().length).toBe(4);

// getting the evaluation result and validating it matches the results from SDK calls
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenNthCalledWith(3, ['split1'], undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenNthCalledWith(4, ['split2', 'split3'], attributes);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenNthCalledWith(3, ['split1'], undefined, undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenNthCalledWith(4, ['split2', 'split3'], attributes, undefined);
const expectedTreatments = {
...(splitSdk.factory.client().getTreatmentsWithConfig as jest.Mock).mock.results[2].value,
...(splitSdk.factory.client().getTreatmentsWithConfig as jest.Mock).mock.results[3].value,
Expand Down Expand Up @@ -369,8 +369,8 @@ describe('getTreatments', () => {

// getting the evaluation result and validating it matches the results from SDK
const treatments = action.payload.treatments;
expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledWith(['split2'], undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toBeCalledWith(['set2'], undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledWith(['split2'], undefined, undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toBeCalledWith(['set2'], undefined, undefined);
expect(treatments).toEqual({
...(splitSdk.factory.client().getTreatmentsWithConfig as jest.Mock).mock.results[0].value,
...(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets as jest.Mock).mock.results[0].value,
Expand All @@ -395,7 +395,8 @@ describe('getTreatments', () => {
store.dispatch<any>(initSplitSdk({ config: sdkBrowserConfig, onTimedout: onTimedoutCb, onReadyFromCache: onReadyFromCacheCb, onReady: onReadyCb }));

const attributes = { att1: 'att1' };
store.dispatch<any>(getTreatments({ splitNames: 'split3', attributes, evalOnUpdate: true, evalOnReadyFromCache: true }));
const properties = { prop1: 'prop1' };
store.dispatch<any>(getTreatments({ splitNames: 'split3', attributes, properties, evalOnUpdate: true, evalOnReadyFromCache: true }));

// If SDK is not ready, an ADD_TREATMENTS action is dispatched with control treatments without calling SDK client
expect(store.getActions().length).toBe(0);
Expand Down Expand Up @@ -436,7 +437,7 @@ describe('getTreatments', () => {

// getting the evaluation result and validating it matches the results from SDK
const treatments = action.payload.treatments;
expect(splitSdk.factory.client().getTreatmentsWithConfig).lastCalledWith(['split3'], attributes);
expect(splitSdk.factory.client().getTreatmentsWithConfig).lastCalledWith(['split3'], attributes, { properties });
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(treatments);
}

Expand All @@ -458,7 +459,7 @@ describe('getTreatments', () => {

// getting the evaluation result and validating it matches the results from SDK
let treatments = action.payload.treatments;
expect(splitSdk.factory.client().getTreatmentsWithConfig).lastCalledWith(['split3'], attributes);
expect(splitSdk.factory.client().getTreatmentsWithConfig).lastCalledWith(['split3'], attributes, { properties });
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(treatments);

expect(Object.values(getClient(splitSdk).evalOnUpdate).length).toBe(1); // control assertion - We should have an item to evaluate on update
Expand All @@ -478,7 +479,7 @@ describe('getTreatments', () => {

// getting the evaluation result and validating it matches the results from SDK
treatments = action.payload.treatments;
expect(splitSdk.factory.client().getTreatmentsWithConfig).lastCalledWith(['split3'], attributes);
expect(splitSdk.factory.client().getTreatmentsWithConfig).lastCalledWith(['split3'], attributes, { properties });
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(treatments);

expect(Object.values(getClient(splitSdk).evalOnUpdate).length).toBe(1); // control assertion - still have one evalOnUpdate subscription
Expand Down Expand Up @@ -546,7 +547,7 @@ describe('getTreatments', () => {
}
});

expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], undefined);
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], undefined, undefined);
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).toHaveLastReturnedWith(action.payload.treatments);

(splitSdk.factory as any).client('other-user-key').__emitter__.emit(Event.SDK_READY, 'other-user-key');
Expand All @@ -564,14 +565,15 @@ describe('getTreatments', () => {
});

// getting the evaluation result and validating it matches the results from SDK
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], undefined);
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], undefined, undefined);
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).toHaveLastReturnedWith(action.payload.treatments);

expect(getClient(splitSdk).evalOnUpdate).toEqual({}); // control assertion

// The getTreatments is dispatched again, but this time is evaluated with attributes and registered for 'evalOnUpdate'
const attributes = { att1: 'att1' };
store.dispatch<any>(getTreatments({ splitNames: 'split2', attributes, key: 'other-user-key', evalOnUpdate: true }));
const properties = { prop1: 'prop1' };
store.dispatch<any>(getTreatments({ splitNames: 'split2', attributes, properties, key: 'other-user-key', evalOnUpdate: true }));
action = store.getActions()[4];
expect(action).toEqual({
type: ADD_TREATMENTS,
Expand All @@ -580,7 +582,7 @@ describe('getTreatments', () => {
treatments: expect.any(Object)
}
});
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], attributes);
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], attributes, { properties });
expect(Object.values(getClient(splitSdk, 'other-user-key').evalOnUpdate).length).toBe(1); // control assertion - added evalOnUpdate subscription

// The SPLIT_UPDATE_WITH_EVALUATIONS action is dispatched when the SDK is updated for the new user key
Expand All @@ -595,7 +597,7 @@ describe('getTreatments', () => {
nonDefaultKey: true
}
});
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], attributes);
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], attributes, { properties });
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).toHaveLastReturnedWith(action.payload.treatments);
expect(Object.values(getClient(splitSdk, 'other-user-key').evalOnUpdate).length).toBe(1); // control assertion - keeping evalOnUpdate subscription

Expand All @@ -609,7 +611,7 @@ describe('getTreatments', () => {
treatments: expect.any(Object)
}
});
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], undefined);
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], undefined, undefined);
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).toHaveLastReturnedWith(action.payload.treatments);
expect(Object.values(getClient(splitSdk).evalOnUpdate).length).toBe(0); // control assertion - removed evalOnUpdate subscription

Expand Down
9 changes: 5 additions & 4 deletions src/__tests__/asyncActions.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,16 @@ describe('getTreatments', () => {
}
});
});
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenLastCalledWith(splitKey, ['split1'], undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenLastCalledWith(splitKey, ['split1'], undefined, undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(actions[0].payload.treatments);
expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toHaveBeenLastCalledWith(splitKey, ['set1'], undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toHaveBeenLastCalledWith(splitKey, ['set1'], undefined, undefined);
expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toHaveLastReturnedWith(actions[1].payload.treatments);

// Invoke with a list of feature flag names and a attributes object
const featureFlagNames = ['split1', 'split2'];
const attributes = { att1: 'att1' };
store.dispatch<any>(getTreatments({ key: 'other_user', splitNames: featureFlagNames, attributes }));
const properties = { prop1: 'prop1' };
store.dispatch<any>(getTreatments({ key: 'other_user', splitNames: featureFlagNames, attributes, properties }));

const action = store.getActions()[3];
expect(action).toEqual({
Expand All @@ -209,7 +210,7 @@ describe('getTreatments', () => {
treatments: expect.any(Object)
}
});
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenLastCalledWith('other_user', featureFlagNames, attributes);
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveBeenLastCalledWith('other_user', featureFlagNames, attributes, { properties });
expect(splitSdk.factory.client().getTreatmentsWithConfig).toHaveLastReturnedWith(action.payload.treatments);
}

Expand Down
2 changes: 2 additions & 0 deletions src/__tests__/helpers.browser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const featureFlagViews: SplitIO.SplitViews = [
configs: { on: null, off: null },
sets: [],
defaultTreatment: 'off',
impressionsDisabled: false,
}, {
name: 'split_2',
trafficType: 'user',
Expand All @@ -43,6 +44,7 @@ const featureFlagViews: SplitIO.SplitViews = [
configs: { on: null, off: null },
sets: [],
defaultTreatment: 'off',
impressionsDisabled: false,
},
];

Expand Down
2 changes: 2 additions & 0 deletions src/__tests__/helpers.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const featureFlagViews: SplitIO.SplitViews = [
configs: { on: null, off: null },
sets: [],
defaultTreatment: 'off',
impressionsDisabled: false,
}, {
name: 'split_2',
trafficType: 'user',
Expand All @@ -42,6 +43,7 @@ const featureFlagViews: SplitIO.SplitViews = [
configs: { on: null, off: null },
sets: [],
defaultTreatment: 'off',
impressionsDisabled: false,
},
];

Expand Down
10 changes: 6 additions & 4 deletions src/asyncActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,12 @@ export function initSplitSdk(params: IInitSplitSdkParams): (dispatch: Dispatch<A
*/
function __getTreatments(client: IClientNotDetached, evalParams: IGetTreatmentsParams[]): SplitIO.TreatmentsWithConfig {
return evalParams.reduce((acc, params) => {
const evaluationOptions = params.properties ? { properties: params.properties } : undefined;
return {
...acc,
...(params.splitNames ?
client.getTreatmentsWithConfig(params.splitNames as string[], params.attributes) :
client.getTreatmentsWithConfigByFlagSets(params.flagSets as string[], params.attributes)
client.getTreatmentsWithConfig(params.splitNames as string[], params.attributes, evaluationOptions) :
client.getTreatmentsWithConfigByFlagSets(params.flagSets as string[], params.attributes, evaluationOptions)
)
};
}, {});
Expand Down Expand Up @@ -171,9 +172,10 @@ export function getTreatments(params: IGetTreatmentsParams): Action | (() => voi

// Evaluate Split and return redux action.
const client = splitSdk.factory.client() as SplitIO.IClient;
const evaluationOptions = params.properties ? { properties: params.properties } : undefined;
const treatments = splitNames ?
client.getTreatmentsWithConfig(params.key, splitNames, params.attributes) :
client.getTreatmentsWithConfigByFlagSets(params.key, flagSets, params.attributes);
client.getTreatmentsWithConfig(params.key, splitNames, params.attributes, evaluationOptions) :
client.getTreatmentsWithConfigByFlagSets(params.key, flagSets, params.attributes, evaluationOptions);
return addTreatments(params.key, treatments);

}
Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ export type IGetTreatmentsParams = {
*/
attributes?: SplitIO.Attributes;

/**
* Optional properties to append to the generated impression object sent to Split backend.
*/
properties?: SplitIO.Properties;

/**
* This param indicates to re-evaluate the feature flags if the SDK is updated. For example, a `true` value might be
* the desired behavior for permission toggles or operation toggles, such as a kill switch, that you want to
Expand Down
Loading