Skip to content

Commit b97a5fe

Browse files
Merge pull request #316 from splitio/localhost_fixes_for_client_side
Fixes for localhost mode on client-side SDKs (Browser SDK, React Native SDK, etc)
2 parents c60ee08 + 87cbb6e commit b97a5fe

File tree

4 files changed

+57
-4
lines changed

4 files changed

+57
-4
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
1.15.1 (May 28, 2024)
22
- Updated the Redis storage to lazily import the `ioredis` dependency when the storage is created. This prevents errors when the SDK is imported or bundled in a .mjs file, as `ioredis` is a CommonJS module.
33
- Bugfixing - Restored some input validation error logs that were removed in version 1.12.0. The logs inform the user when the `getTreatment(s)` methods are called with an invalid value as feature flag name or flag set name.
4+
- Bugfixing - Fixed localhost mode for client-side SDKs to emit SDK_UPDATE when mocked feature flags are updated in the `features` property of the config object (Related to issue https://github.com/splitio/javascript-browser-client/issues/119).
45

56
1.15.0 (May 13, 2024)
67
- Added an optional settings validation parameter to let overwrite the default flag spec version, used by the JS Synchronizer.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { SplitIO } from '../../../../types';
2+
import { splitsParserFromSettingsFactory } from '../splitsParserFromSettings';
3+
4+
const FEATURE_ON = { conditions: [{ conditionType: 'ROLLOUT', label: 'default rule', matcherGroup: { combiner: 'AND', matchers: [{ keySelector: null, matcherType: 'ALL_KEYS', negate: false }] }, partitions: [{ size: 100, treatment: 'on' }] }], configurations: {}, trafficTypeName: 'localhost' };
5+
const FEATURE_OFF = { conditions: [{ conditionType: 'ROLLOUT', label: 'default rule', matcherGroup: { combiner: 'AND', matchers: [{ keySelector: null, matcherType: 'ALL_KEYS', negate: false }] }, partitions: [{ size: 100, treatment: 'off' }] }], configurations: {}, trafficTypeName: 'localhost' };
6+
7+
test('splitsParserFromSettingsFactory', () => {
8+
9+
const instance = splitsParserFromSettingsFactory();
10+
11+
const settings = { features: {} as SplitIO.MockedFeaturesMap };
12+
expect(instance(settings)).toEqual({});
13+
14+
// Pass the same settings
15+
expect(instance(settings)).toEqual(false);
16+
17+
// New features object with new content
18+
settings.features = { feature1: 'on' };
19+
expect(instance(settings)).toEqual({ feature1: FEATURE_ON });
20+
21+
// New features object but same content
22+
settings.features = { feature1: 'on' };
23+
expect(instance(settings)).toEqual(false);
24+
25+
// Update property
26+
settings.features['feature1'] = 'off';
27+
expect(instance(settings)).toEqual({ feature1: FEATURE_OFF });
28+
29+
// New settings object but same content
30+
expect(instance({ features: { feature1: 'off' } })).toEqual(false);
31+
32+
// Update property. Same content but in a different format
33+
settings.features['feature1'] = { treatment: 'off', config: null };
34+
expect(instance(settings)).toEqual({ feature1: FEATURE_OFF });
35+
36+
// Add new feature flag property
37+
settings.features['feature2'] = { treatment: 'on', config: null };
38+
expect(instance(settings)).toEqual({ feature1: FEATURE_OFF, feature2: FEATURE_ON });
39+
40+
// New settings object but same content
41+
expect(instance({ features: { feature1: { treatment: 'off', config: null }, feature2: { treatment: 'on', config: null } } })).toEqual(false);
42+
43+
// Update property
44+
settings.features['feature2'].config = 'some_config';
45+
expect(instance(settings)).toEqual({ feature1: FEATURE_OFF, feature2: { ...FEATURE_ON, configurations: { on: 'some_config' } } });
46+
47+
// @ts-expect-error No object implies no features
48+
settings.features = undefined;
49+
expect(instance(settings)).toEqual({});
50+
});

src/sync/offline/splitsParser/splitsParserFromSettings.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ISplitPartial } from '../../../dtos/types';
22
import { ISettings, SplitIO } from '../../../types';
3-
import { isObject, forOwn } from '../../../utils/lang';
3+
import { isObject, forOwn, merge } from '../../../utils/lang';
44
import { parseCondition } from './parseCondition';
55

66
function hasTreatmentChanged(prev: string | SplitIO.TreatmentWithConfig, curr: string | SplitIO.TreatmentWithConfig) {
@@ -22,7 +22,7 @@ export function splitsParserFromSettingsFactory() {
2222

2323
// Different amount of items
2424
if (names.length !== Object.keys(previousMock).length) {
25-
previousMock = currentData;
25+
previousMock = merge({}, currentData) as SplitIO.MockedFeaturesMap;
2626
return true;
2727
}
2828

@@ -31,7 +31,7 @@ export function splitsParserFromSettingsFactory() {
3131
const newTreatment = hasTreatmentChanged(previousMock[name], currentData[name]);
3232
const changed = newSplit || newTreatment;
3333

34-
if (changed) previousMock = currentData;
34+
if (changed) previousMock = merge({}, currentData) as SplitIO.MockedFeaturesMap;
3535

3636
return changed;
3737
});
@@ -41,7 +41,7 @@ export function splitsParserFromSettingsFactory() {
4141
*
4242
* @param settings validated object with mocked features mapping.
4343
*/
44-
return function splitsParserFromSettings(settings: ISettings): false | Record<string, ISplitPartial> {
44+
return function splitsParserFromSettings(settings: Pick<ISettings, 'features'>): false | Record<string, ISplitPartial> {
4545
const features = settings.features as SplitIO.MockedFeaturesMap || {};
4646

4747
if (!mockUpdated(features)) return false;

src/utils/settingsValidation/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
109109

110110
// creates a settings object merging base, defaults and config objects.
111111
const withDefaults = merge({}, base, defaults, config) as ISettings;
112+
// Keeps reference to the `features` property
113+
withDefaults.features = get(config, 'features');
112114

113115
// ensure a valid logger.
114116
// First thing to validate, since other validators might use the logger.

0 commit comments

Comments
 (0)