Skip to content

Commit 4e37994

Browse files
fix: unwrap TraitConfig values in local evaluation before segment matching (#252)
1 parent 8c15261 commit 4e37994

File tree

2 files changed

+79
-3
lines changed

2 files changed

+79
-3
lines changed

sdk/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import { FlagsmithAPIError, FlagsmithClientError } from './errors.js';
88

99
import { DefaultFlag, Flags } from './models.js';
1010
import { EnvironmentDataPollingManager } from './polling_manager.js';
11-
import { Deferred, generateIdentitiesData, getUserAgent, retryFetch } from './utils.js';
11+
import {
12+
Deferred,
13+
generateIdentitiesData,
14+
getUserAgent,
15+
isTraitConfig,
16+
retryFetch
17+
} from './utils.js';
1218
import {
1319
SegmentModel,
1420
EnvironmentModel,
@@ -275,7 +281,7 @@ export class Flagsmith {
275281
identifier,
276282
Object.keys(traits || {}).map(key => ({
277283
key,
278-
value: traits?.[key]
284+
value: isTraitConfig(traits?.[key]) ? traits![key].value : traits?.[key]
279285
}))
280286
);
281287

@@ -474,7 +480,7 @@ export class Flagsmith {
474480
identifier,
475481
Object.keys(traits).map(key => ({
476482
key,
477-
value: traits[key]
483+
value: isTraitConfig(traits[key]) ? traits[key].value : traits[key]
478484
}))
479485
);
480486

tests/sdk/flagsmith-identity-flags.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,76 @@ test('test_identity_with_transient_traits', async () => {
209209
expect(identityFlags[0].featureName).toBe('some_feature');
210210
});
211211

212+
test('getIdentityFlags local evaluation with plain traits matches segment', async () => {
213+
const identifier = 'identifier';
214+
// Plain trait format: age=30 should match segment rule "age LESS_THAN 40"
215+
const traits = { age: 30 };
216+
217+
const flg = flagsmith({
218+
environmentKey: 'ser.key',
219+
enableLocalEvaluation: true
220+
});
221+
222+
const flags = await flg.getIdentityFlags(identifier, traits);
223+
224+
// Should get segment override value, not the default
225+
expect(flags.getFeatureValue('some_feature')).toBe('segment_override');
226+
expect(flags.isFeatureEnabled('some_feature')).toBe(false);
227+
});
228+
229+
test('getIdentityFlags local evaluation with TraitConfig format matches segment', async () => {
230+
const identifier = 'identifier';
231+
// TraitConfig format: same trait value wrapped with transient metadata
232+
const traits = { age: { value: 30, transient: true } };
233+
234+
const flg = flagsmith({
235+
environmentKey: 'ser.key',
236+
enableLocalEvaluation: true
237+
});
238+
239+
const flags = await flg.getIdentityFlags(identifier, traits);
240+
241+
// Should get segment override value — same result as plain trait format
242+
expect(flags.getFeatureValue('some_feature')).toBe('segment_override');
243+
expect(flags.isFeatureEnabled('some_feature')).toBe(false);
244+
});
245+
246+
test('getIdentityFlags local evaluation with mixed trait formats matches segment', async () => {
247+
const identifier = 'identifier';
248+
// Mix of plain and TraitConfig formats
249+
const traits = {
250+
age: { value: 30, transient: true },
251+
some_other_trait: 'plain_value'
252+
};
253+
254+
const flg = flagsmith({
255+
environmentKey: 'ser.key',
256+
enableLocalEvaluation: true
257+
});
258+
259+
const flags = await flg.getIdentityFlags(identifier, traits);
260+
261+
// Should get segment override value
262+
expect(flags.getFeatureValue('some_feature')).toBe('segment_override');
263+
expect(flags.isFeatureEnabled('some_feature')).toBe(false);
264+
});
265+
266+
test('getIdentitySegments with TraitConfig format matches segment', async () => {
267+
const identifier = 'identifier';
268+
// TraitConfig format should work for getIdentitySegments too
269+
const traits = { age: { value: 30, transient: true } };
270+
271+
const flg = flagsmith({
272+
environmentKey: 'ser.key',
273+
enableLocalEvaluation: true
274+
});
275+
276+
const segments = await flg.getIdentitySegments(identifier, traits);
277+
278+
expect(segments).toHaveLength(1);
279+
expect(segments[0].name).toBe('regular_segment');
280+
});
281+
212282
test('getIdentityFlags fails if API call failed and no default flag handler was provided', async () => {
213283
const flg = flagsmith({
214284
fetch: badFetch

0 commit comments

Comments
 (0)