diff --git a/CHANGES.txt b/CHANGES.txt index d93f835..cfee9bd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,7 @@ 2.0.0 (November 15, 2024) - Added support for targeting rules based on large segments. - Updated @splitsoftware/splitio package to version 11.0.1 that includes major updates, and updated some transitive dependencies for vulnerability fixes. + - Updated `getTreatments` action creator to not dispatch an action when called while the SDK is not ready or ready from cache, to avoid unnecessary updates in the state. - Renamed distribution folders from `/lib` to `/cjs` for CommonJS build, and `/es` to `/esm` for ECMAScript Modules build. - BREAKING CHANGES: - Removed the `core.trafficType` option from the `config` object accepted by the `initSplitSdk` action creator, and made the `trafficType` argument of the `track` helper function mandatory. diff --git a/src/__tests__/asyncActions.browser.test.ts b/src/__tests__/asyncActions.browser.test.ts index 4d23117..6f004a3 100644 --- a/src/__tests__/asyncActions.browser.test.ts +++ b/src/__tests__/asyncActions.browser.test.ts @@ -13,7 +13,7 @@ import { sdkBrowserConfig } from './utils/sdkConfigs'; import { SPLIT_READY, SPLIT_READY_WITH_EVALUATIONS, SPLIT_READY_FROM_CACHE, SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS, SPLIT_UPDATE, SPLIT_UPDATE_WITH_EVALUATIONS, SPLIT_TIMEDOUT, SPLIT_DESTROY, ADD_TREATMENTS, - ERROR_GETT_NO_INITSPLITSDK, ERROR_DESTROY_NO_INITSPLITSDK, getControlTreatmentsWithConfig, ERROR_GETT_NO_PARAM_OBJECT, + ERROR_GETT_NO_INITSPLITSDK, ERROR_DESTROY_NO_INITSPLITSDK, ERROR_GETT_NO_PARAM_OBJECT, } from '../constants'; /** Test targets */ @@ -323,7 +323,7 @@ describe('getTreatments', () => { } }); - it('stores control treatments (without calling SDK client) and registers pending evaluations if Split SDK is not operational, to dispatch it when ready (Using action result promise)', (done) => { + it('registers evaluations if Split SDK is not operational, to dispatch it when ready (Using action result promise)', (done) => { const store = mockStore(STATE_INITIAL); const actionResult = store.dispatch(initSplitSdk({ config: sdkBrowserConfig, onReadyFromCache: onReadyFromCacheCb })); @@ -332,10 +332,7 @@ describe('getTreatments', () => { // If SDK is not operational, ADD_TREATMENTS actions are dispatched, with control treatments for provided feature flag names, and no treatments for provided flag sets. - expect(store.getActions()).toEqual([ - { type: ADD_TREATMENTS, payload: { key: sdkBrowserConfig.core.key, treatments: getControlTreatmentsWithConfig(['split2']) } }, - { type: ADD_TREATMENTS, payload: { key: sdkBrowserConfig.core.key, treatments: {} } }, - ]); + expect(store.getActions().length).toBe(0); // SDK client is not called, but items are added to 'evalOnReady' list. expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledTimes(0); expect(splitSdk.factory.client().getTreatmentsWithConfigByFlagSets).toBeCalledTimes(0); @@ -345,8 +342,8 @@ describe('getTreatments', () => { // When the SDK is ready from cache, the SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS action is not dispatched if the `getTreatments` action was dispatched with `evalOnReadyFromCache` false (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE); function onReadyFromCacheCb() { - expect(store.getActions().length).toBe(3); - const action = store.getActions()[2]; + expect(store.getActions().length).toBe(1); + const action = store.getActions()[0]; expect(action).toEqual({ type: SPLIT_READY_FROM_CACHE, payload: { @@ -359,7 +356,7 @@ describe('getTreatments', () => { actionResult.then(() => { // The SPLIT_READY_WITH_EVALUATIONS action is dispatched if the SDK is ready and there are pending evaluations. - const action = store.getActions()[3]; + const action = store.getActions()[1]; expect(action).toEqual({ type: SPLIT_READY_WITH_EVALUATIONS, payload: { @@ -392,7 +389,7 @@ describe('getTreatments', () => { }); }); - it('stores control treatments (without calling SDK client) and registers pending evaluations if Split SDK is not operational, to dispatch it when ready from cache, ready, and updated (Using callbacks to assert that registered evaluations are not affected when SDK timeout)', (done) => { + it('registers pending evaluations if Split SDK is not operational, to dispatch it when ready from cache, ready, and updated (Using callbacks to assert that registered evaluations are not affected when SDK timeout)', (done) => { const store = mockStore(STATE_INITIAL); store.dispatch(initSplitSdk({ config: sdkBrowserConfig, onTimedout: onTimedoutCb, onReadyFromCache: onReadyFromCacheCb, onReady: onReadyCb })); @@ -401,15 +398,7 @@ describe('getTreatments', () => { store.dispatch(getTreatments({ splitNames: 'split3', attributes, 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(1); - let action = store.getActions()[0]; - expect(action).toEqual({ - type: ADD_TREATMENTS, - payload: { - key: sdkBrowserConfig.core.key, - treatments: getControlTreatmentsWithConfig(['split3']) - } - }); + expect(store.getActions().length).toBe(0); expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledTimes(0); // the item is added for evaluation on SDK_READY, and also on SDK_READY_FROM_CACHE and SDK_UPDATE events @@ -421,7 +410,7 @@ describe('getTreatments', () => { // When the SDK has timedout, the SPLIT_TIMEDOUT action is dispatched. It doesn't affect registered evaluations for other SDK events. (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_READY_TIMED_OUT); function onTimedoutCb() { - action = store.getActions()[1]; + const action = store.getActions()[0]; expect(action).toEqual({ type: SPLIT_TIMEDOUT, payload: { @@ -434,7 +423,7 @@ describe('getTreatments', () => { // SPLIT_READY_FROM_CACHE, because of the `evalOnReadyFromCache` param in `getTreatments` action (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE); function onReadyFromCacheCb() { - action = store.getActions()[2]; + const action = store.getActions()[1]; expect(action).toEqual({ type: SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS, payload: { @@ -456,7 +445,7 @@ describe('getTreatments', () => { // Using cb for ready event, because action result is rejected due to SDK timeout function onReadyCb() { // The SPLIT_READY_WITH_EVALUATIONS action is dispatched if the SDK is ready and there are pending evaluations. - action = store.getActions()[3]; + let action = store.getActions()[2]; expect(action).toEqual({ type: SPLIT_READY_WITH_EVALUATIONS, payload: { @@ -476,7 +465,7 @@ describe('getTreatments', () => { // Triggering an update dispatches SPLIT_UPDATE_WITH_EVALUATIONS (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_UPDATE); - action = store.getActions()[4]; + action = store.getActions()[3]; expect(action).toEqual({ type: SPLIT_UPDATE_WITH_EVALUATIONS, payload: { @@ -496,7 +485,7 @@ describe('getTreatments', () => { // We deregister the item from evalOnUpdate. store.dispatch(getTreatments({ splitNames: 'split3', evalOnUpdate: false, key: { matchingKey: sdkBrowserConfig.core.key as string, bucketingKey: 'bucket' } })); - action = store.getActions()[5]; + action = store.getActions()[4]; expect(action).toEqual({ type: ADD_TREATMENTS, payload: { @@ -508,7 +497,7 @@ describe('getTreatments', () => { // Now, SDK_UPDATE events do not trigger SPLIT_UPDATE_WITH_EVALUATIONS but SPLIT_UPDATE instead (splitSdk.factory as any).client().__emitter__.emit(Event.SDK_UPDATE); - action = store.getActions()[6]; + action = store.getActions()[5]; expect(action).toEqual({ type: SPLIT_UPDATE, payload: { @@ -516,14 +505,14 @@ describe('getTreatments', () => { } }); - expect(store.getActions().length).toBe(7); // control assertion - no more actions after the update. + expect(store.getActions().length).toBe(6); // control assertion - no more actions after the update. expect(splitSdk.factory.client().getTreatmentsWithConfig).toBeCalledTimes(4); // control assertion - called 4 times, in actions SPLIT_READY_FROM_CACHE_WITH_EVALUATIONS, SPLIT_READY_WITH_EVALUATIONS, SPLIT_UPDATE_WITH_EVALUATIONS and ADD_TREATMENTS. done(); } }); - it('for non-default clients, it stores control treatments (without calling SDK client) and registers pending evaluations if the client is not operational, to dispatch it when ready from cache, ready, and updated (Using callbacks to assert that registered evaluations are not affected when the client timeouts)', (done) => { + it('for non-default clients, registers pending evaluations if the client is not operational, to dispatch it when ready from cache, ready, and updated (Using callbacks to assert that registered evaluations are not affected when the client timeouts)', (done) => { // Init SDK and set ready const store = mockStore(STATE_INITIAL); @@ -676,7 +665,7 @@ describe('destroySplitSdk', () => { const actionResult = store.dispatch(destroySplitSdk()); actionResult.then(() => { - const action = store.getActions()[3]; + const action = store.getActions()[1]; expect(action).toEqual({ type: SPLIT_DESTROY, payload: { diff --git a/src/asyncActions.ts b/src/asyncActions.ts index 84268c0..0bab016 100644 --- a/src/asyncActions.ts +++ b/src/asyncActions.ts @@ -3,7 +3,7 @@ import { SplitFactory as SplitFactoryForLocalhost } from '@splitsoftware/splitio import { Dispatch, Action } from 'redux'; import { IInitSplitSdkParams, IGetTreatmentsParams, IDestroySplitSdkParams, ISplitFactoryBuilder } from './types'; import { splitReady, splitReadyWithEvaluations, splitReadyFromCache, splitReadyFromCacheWithEvaluations, splitTimedout, splitUpdate, splitUpdateWithEvaluations, splitDestroy, addTreatments } from './actions'; -import { VERSION, ERROR_GETT_NO_INITSPLITSDK, ERROR_DESTROY_NO_INITSPLITSDK, getControlTreatmentsWithConfig } from './constants'; +import { VERSION, ERROR_GETT_NO_INITSPLITSDK, ERROR_DESTROY_NO_INITSPLITSDK } from './constants'; import { matching, __getStatus, validateGetTreatmentsParams, isMainClient } from './utils'; /** @@ -164,10 +164,7 @@ export function getTreatments(params: IGetTreatmentsParams): Action | (() => voi 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 - // @TODO remove eventually to minimize state changes - return addTreatments(params.key || (splitSdk.config as SplitIO.IBrowserSettings).core.key, splitNames ? getControlTreatmentsWithConfig(splitNames) : {}); + return () => { }; } } else { // Split SDK running in Node.js diff --git a/src/constants.ts b/src/constants.ts index a2d00cb..f0e9eaa 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -15,13 +15,6 @@ export const CONTROL_WITH_CONFIG: SplitIO.TreatmentWithConfig = { config: null, }; -export const getControlTreatmentsWithConfig = (featureFlagNames: string[]): SplitIO.TreatmentsWithConfig => { - return featureFlagNames.reduce((pValue: SplitIO.TreatmentsWithConfig, cValue: string) => { - pValue[cValue] = CONTROL_WITH_CONFIG; - return pValue; - }, {}); -}; - // Action types export const SPLIT_READY = 'SPLIT_READY';