Skip to content

Commit 8d0f04e

Browse files
committed
fix: support flat traits
Signed-off-by: Danju Visvanathan <danju.visvanathan@gmail.com>
1 parent 47147ec commit 8d0f04e

2 files changed

Lines changed: 114 additions & 1 deletion

File tree

openfeature_flagsmith/provider.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,15 @@ def _resolve(
134134

135135
def _get_flags(self, evaluation_context: EvaluationContext = EvaluationContext()):
136136
if targeting_key := evaluation_context.targeting_key:
137+
traits = {}
138+
for key, value in evaluation_context.attributes.items():
139+
if key == "traits":
140+
continue
141+
else:
142+
traits[key] = value
143+
traits.update(evaluation_context.attributes.get("traits", {}))
137144
return self._client.get_identity_flags(
138145
identifier=targeting_key,
139-
traits=evaluation_context.attributes.get("traits", {}),
146+
traits=traits,
140147
)
141148
return self._client.get_environment_flags()

tests/test_provider.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,112 @@ def test_identity_flags_are_used_if_targeting_key_provided(
319319
)
320320

321321

322+
def test_identity_flags_are_used_with_flat_attributes(
323+
mock_flagsmith_client: MagicMock,
324+
) -> None:
325+
# Given
326+
key = "key"
327+
targeting_key = "targeting_key"
328+
traits = {"foo": "bar", "age": 25}
329+
value = "foo"
330+
default_value = "default"
331+
332+
provider = FlagsmithProvider(mock_flagsmith_client)
333+
334+
mock_flagsmith_client.get_environment_flags.side_effect = NotImplementedError()
335+
mock_flagsmith_client.get_identity_flags.return_value = Flags(
336+
{key: Flag(feature_id=1, feature_name=key, enabled=True, value=value)}
337+
)
338+
339+
# When
340+
result = provider.resolve_string_details(
341+
flag_key=key,
342+
default_value=default_value,
343+
evaluation_context=EvaluationContext(
344+
targeting_key=targeting_key, attributes=traits
345+
),
346+
)
347+
348+
# Then
349+
assert result.value == value
350+
assert result.error_code is None
351+
assert result.reason is None
352+
353+
mock_flagsmith_client.get_identity_flags.assert_called_once_with(
354+
identifier=targeting_key, traits=traits
355+
)
356+
357+
358+
def test_identity_flags_flat_attributes_and_nested_traits_are_merged(
359+
mock_flagsmith_client: MagicMock,
360+
) -> None:
361+
# Given
362+
key = "key"
363+
targeting_key = "targeting_key"
364+
value = "foo"
365+
default_value = "default"
366+
367+
provider = FlagsmithProvider(mock_flagsmith_client)
368+
369+
mock_flagsmith_client.get_environment_flags.side_effect = NotImplementedError()
370+
mock_flagsmith_client.get_identity_flags.return_value = Flags(
371+
{key: Flag(feature_id=1, feature_name=key, enabled=True, value=value)}
372+
)
373+
374+
# When
375+
result = provider.resolve_string_details(
376+
flag_key=key,
377+
default_value=default_value,
378+
evaluation_context=EvaluationContext(
379+
targeting_key=targeting_key,
380+
attributes={"flat_trait": "flat_value", "traits": {"nested_trait": "nested_value"}},
381+
),
382+
)
383+
384+
# Then
385+
assert result.value == value
386+
assert result.error_code is None
387+
assert result.reason is None
388+
389+
mock_flagsmith_client.get_identity_flags.assert_called_once_with(
390+
identifier=targeting_key,
391+
traits={"flat_trait": "flat_value", "nested_trait": "nested_value"},
392+
)
393+
394+
395+
def test_identity_flags_nested_traits_take_precedence_over_flat_attributes(
396+
mock_flagsmith_client: MagicMock,
397+
) -> None:
398+
# Given
399+
key = "key"
400+
targeting_key = "targeting_key"
401+
value = "foo"
402+
default_value = "default"
403+
404+
provider = FlagsmithProvider(mock_flagsmith_client)
405+
406+
mock_flagsmith_client.get_environment_flags.side_effect = NotImplementedError()
407+
mock_flagsmith_client.get_identity_flags.return_value = Flags(
408+
{key: Flag(feature_id=1, feature_name=key, enabled=True, value=value)}
409+
)
410+
411+
# When
412+
provider.resolve_string_details(
413+
flag_key=key,
414+
default_value=default_value,
415+
evaluation_context=EvaluationContext(
416+
targeting_key=targeting_key,
417+
attributes={"shared_key": "flat_value", "traits": {"shared_key": "nested_value"}},
418+
),
419+
)
420+
421+
# Then
422+
mock_flagsmith_client.get_identity_flags.assert_called_once_with(
423+
identifier=targeting_key,
424+
traits={"shared_key": "nested_value"},
425+
)
426+
427+
322428
def test_resolve_boolean_details_uses_enabled_when_use_boolean_config_value_is_false(
323429
mock_flagsmith_client: MagicMock,
324430
) -> None:

0 commit comments

Comments
 (0)