diff --git a/package-lock.json b/package-lock.json index 0df10ce..d3269ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio-redux", - "version": "1.14.0", + "version": "1.13.1-rc.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-redux", - "version": "1.14.0", + "version": "1.13.1-rc.6", "license": "Apache-2.0", "dependencies": { "@splitsoftware/splitio": "10.28.0", diff --git a/package.json b/package.json index a1715c3..a49d22a 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/__tests__/asyncActions.browser.test.ts b/src/__tests__/asyncActions.browser.test.ts index f826835..d6ee032 100644 --- a/src/__tests__/asyncActions.browser.test.ts +++ b/src/__tests__/asyncActions.browser.test.ts @@ -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: { @@ -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(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: { @@ -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: { @@ -614,7 +607,7 @@ describe('getTreatments', () => { // We deregister the item from evalOnUpdate. store.dispatch(getTreatments({ splitNames: 'split2', key: 'other-user-key', evalOnUpdate: false })); - action = store.getActions()[7]; + action = store.getActions()[6]; expect(action).toEqual({ type: ADD_TREATMENTS, payload: { @@ -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: { @@ -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(); }); diff --git a/src/__tests__/utils/mockBrowserSplitSdk.ts b/src/__tests__/utils/mockBrowserSplitSdk.ts index 5cee78b..37c6107 100644 --- a/src/__tests__/utils/mockBrowserSplitSdk.ts +++ b/src/__tests__/utils/mockBrowserSplitSdk.ts @@ -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) { diff --git a/src/asyncActions.ts b/src/asyncActions.ts index d9a45de..0d5404e 100644 --- a/src/asyncActions.ts +++ b/src/asyncActions.ts @@ -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 @@ -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() {