Skip to content

Commit

Permalink
Fix corner case, to dispatch a single action rather than 2
Browse files Browse the repository at this point in the history
  • Loading branch information
EmilianoSanchez committed Sep 13, 2024
1 parent ec3c3d3 commit 73a6720
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 41 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@splitsoftware/splitio-redux",
"version": "1.14.0",
"version": "1.13.1-rc.6",
"description": "A library to easily use Split JS SDK with Redux and React Redux",
"main": "lib/index.js",
"module": "es/index.js",
Expand Down
31 changes: 12 additions & 19 deletions src/__tests__/asyncActions.browser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -540,32 +540,25 @@ describe('getTreatments', () => {
expect(getClient(splitSdk, 'other-user-key').evalOnReady.length).toEqual(1); // control assertion - 1 evaluation was registered for SDK_READY on the new client
expect(getClient(splitSdk).evalOnUpdate).toEqual({});

// If SDK was ready from cache, the SPLIT_READY_FROM_CACHE action is dispatched for the new clients
// If SDK was ready from cache, the SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS action is dispatched for the new clients, calling SDK client to evaluate from cache
let action = store.getActions()[2];
expect(action).toEqual({
type: SPLIT_READY_FROM_CACHE,
type: SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS,
payload: {
key: 'other-user-key',
timestamp: expect.any(Number)
timestamp: 0,
treatments: expect.any(Object),
nonDefaultKey: true
}
});

// and an ADD_TREATMENTS action is dispatched calling SDK client to evaluate from cache
action = store.getActions()[3];
expect(action).toEqual({
type: ADD_TREATMENTS,
payload: {
key: 'other-user-key',
treatments: expect.any(Object)
}
});
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).lastCalledWith(['split2'], 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');

// The SPLIT_READY_WITH_EVALUATIONS action is dispatched synchronously once the SDK is ready for the new user key
action = store.getActions()[4];
action = store.getActions()[3];
expect(action).toEqual({
type: SPLIT_READY_WITH_EVALUATIONS,
payload: {
Expand All @@ -585,7 +578,7 @@ describe('getTreatments', () => {
// 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 }));
action = store.getActions()[5];
action = store.getActions()[4];
expect(action).toEqual({
type: ADD_TREATMENTS,
payload: {
Expand All @@ -598,7 +591,7 @@ describe('getTreatments', () => {

// The SPLIT_UPDATE_WITH_EVALUATIONS action is dispatched when the SDK is updated for the new user key
(splitSdk.factory as any).client('other-user-key').__emitter__.emit(Event.SDK_UPDATE);
action = store.getActions()[6];
action = store.getActions()[5];
expect(action).toEqual({
type: SPLIT_UPDATE_WITH_EVALUATIONS,
payload: {
Expand All @@ -614,7 +607,7 @@ describe('getTreatments', () => {

// We deregister the item from evalOnUpdate.
store.dispatch<any>(getTreatments({ splitNames: 'split2', key: 'other-user-key', evalOnUpdate: false }));
action = store.getActions()[7];
action = store.getActions()[6];
expect(action).toEqual({
type: ADD_TREATMENTS,
payload: {
Expand All @@ -628,7 +621,7 @@ describe('getTreatments', () => {

// Now, SDK_UPDATE events do not trigger SPLIT_UPDATE_WITH_EVALUATIONS but SPLIT_UPDATE instead
(splitSdk.factory as any).client('other-user-key').__emitter__.emit(Event.SDK_UPDATE);
action = store.getActions()[8];
action = store.getActions()[7];
expect(action).toEqual({
type: SPLIT_UPDATE,
payload: {
Expand All @@ -637,8 +630,8 @@ describe('getTreatments', () => {
}
});

expect(store.getActions().length).toBe(9); // control assertion - no more actions after the update.
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).toBeCalledTimes(5); // control assertion - called 5 times, in actions ADD_TREATMENTS, SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS, SPLIT_READY_WITH_EVALUATIONS, SPLIT_UPDATE_WITH_EVALUATIONS and ADD_TREATMENTS.
expect(store.getActions().length).toBe(8); // control assertion - no more actions after the update.
expect(splitSdk.factory.client('other-user-key').getTreatmentsWithConfig).toBeCalledTimes(5); // control assertion - called 5 times, in actions SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS, SPLIT_READY_WITH_EVALUATIONS, ADD_TREATMENTS, SPLIT_UPDATE_WITH_EVALUATIONS and ADD_TREATMENTS.

done();
});
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/utils/mockBrowserSplitSdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function mockSdk() {

return jest.fn((config: SplitIO.IBrowserSettings, __updateModules?: (modules: { settings: { version: string } }) => void) => {

// isReadyFromCache is a shared status among clients
// ATM, isReadyFromCache is shared among clients
let isReadyFromCache = false;

function mockClient(key?: SplitIO.SplitKey) {
Expand Down
36 changes: 18 additions & 18 deletions src/asyncActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,12 @@ export function getTreatments(params: IGetTreatmentsParams): Action | (() => voi
if (status.isOperational) {
// If the SDK is operational (i.e., it is ready or ready from cache), it evaluates and adds treatments to the store
const treatments = __getTreatments(client, [params]);
return addTreatments(params.key || (splitSdk.config as SplitIO.IBrowserSettings).core.key, treatments);

// Shared clients might be ready from cache immediately, so we need to dispatch a single action that updates treatments and `isReadyFromCache` status
// @TODO handle this corner case by refactoring actions into a single action that includes both the client status and optional evaluation/s
return status.isReadyFromCache && !status.isReady && !isMainClient(params.key) ?
splitReadyFromCacheWithEvaluations(params.key, treatments, status.lastUpdate, true) :
addTreatments(params.key || (splitSdk.config as SplitIO.IBrowserSettings).core.key, treatments);
} else {
// Otherwise, it adds control treatments to the store, without calling the SDK (no impressions sent)
// With flag sets, an empty object is passed since we don't know their feature flag names
Expand Down Expand Up @@ -243,23 +248,18 @@ export function getClient(splitSdk: ISplitSdk, key?: SplitIO.SplitKey): IClientN
});

// On SDK ready from cache, dispatch `splitReadyFromCache` action
const status = __getStatus(client);
if (status.isReadyFromCache) { // can be true immediately for shared clients
if (splitSdk.dispatch) splitSdk.dispatch(splitReadyFromCache(status.lastUpdate, key));
} else {
client.once(client.Event.SDK_READY_FROM_CACHE, function onReadyFromCache() {
if (!splitSdk.dispatch) return;

const lastUpdate = __getStatus(client).lastUpdate;
if (client.evalOnReadyFromCache.length) {
const treatments = __getTreatments(client, client.evalOnReadyFromCache);

splitSdk.dispatch(splitReadyFromCacheWithEvaluations(key || (splitSdk.config as SplitIO.IBrowserSettings).core.key, treatments, lastUpdate, key && true));
} else {
splitSdk.dispatch(splitReadyFromCache(lastUpdate, key));
}
});
}
client.once(client.Event.SDK_READY_FROM_CACHE, function onReadyFromCache() {
if (!splitSdk.dispatch) return;

const lastUpdate = __getStatus(client).lastUpdate;
if (client.evalOnReadyFromCache.length) {
const treatments = __getTreatments(client, client.evalOnReadyFromCache);

splitSdk.dispatch(splitReadyFromCacheWithEvaluations(key || (splitSdk.config as SplitIO.IBrowserSettings).core.key, treatments, lastUpdate, key && true));
} else {
splitSdk.dispatch(splitReadyFromCache(lastUpdate, key));
}
});

// On SDK update, evaluate the registered `getTreatments` actions and dispatch `splitUpdate` action
client.on(client.Event.SDK_UPDATE, function onUpdate() {
Expand Down

0 comments on commit 73a6720

Please sign in to comment.