Skip to content

Commit b8aa931

Browse files
committed
feat: map the engine DEFAULT reason to STATIC
Signed-off-by: wadii <wadii.zaim@flagsmith.com>
1 parent 940dda7 commit b8aa931

3 files changed

Lines changed: 40 additions & 6 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ details = of_client.get_string_details(
104104
)
105105
```
106106

107-
The hook records an exposure only when the flag resolved with a variant and reason `SPLIT` — a multivariate percentage-split assignment (enabled, identified, not offline). With `flagsmith` ≥6.2 resolution reasons come from the Flagsmith engine verbatim (e.g. `SPLIT; weight=30`); on older SDKs or APIs the provider infers them. Repeated evaluations are safe: duplicate exposures are deduplicated downstream.
107+
The hook records an exposure only when the flag resolved with a variant and reason `SPLIT` — a multivariate percentage-split assignment (enabled, identified, not offline). With `flagsmith` ≥6.2 resolution reasons come from the Flagsmith engine (e.g. `SPLIT; weight=30`; the engine's `DEFAULT` maps to `STATIC`); on older SDKs or APIs the provider infers them. Repeated evaluations are safe: duplicate exposures are deduplicated downstream.
108108

109109
**2. Explicit `track()`.** Use the reserved `feature_flag.exposure` event name when you need to record an exposure decoupled from evaluation:
110110

‎openfeature_flagsmith/provider.py‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,14 @@ def _parse_reason(
337337
# Offline documents may be arbitrarily old.
338338
if getattr(self._client, "offline_mode", False):
339339
return Reason.STALE
340-
# Engine reason, verbatim ("SPLIT; weight=30"), on flagsmith >=6.2.
341-
engine_reason = getattr(flag, "reason", None)
342-
if engine_reason is not None:
343-
return engine_reason
340+
client_reason = getattr(flag, "reason", None)
341+
if client_reason is not None:
342+
# The Flagsmith client's DEFAULT means the environment default
343+
# state was served; OpenFeature reserves DEFAULT for the code
344+
# default.
345+
if str(client_reason).split(";", 1)[0].strip() == "DEFAULT":
346+
return Reason.STATIC
347+
return client_reason
344348
if evaluation_context.targeting_key:
345349
# A variant means a percentage-split assignment (SPLIT);
346350
# TARGETING_MATCH is reserved for segment matches.

‎tests/test_provider.py‎

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ def test_track_drops_reserved_dollar_names(
652652
@requires_engine_reasons
653653
@pytest.mark.parametrize(
654654
"engine_reason",
655-
["SPLIT; weight=50.0", "TARGETING_MATCH; segment=premium", "DEFAULT"],
655+
["SPLIT; weight=50.0", "TARGETING_MATCH; segment=premium"],
656656
)
657657
def test_engine_reason_is_forwarded_verbatim(
658658
mock_flagsmith_client: MagicMock, engine_reason: str
@@ -684,6 +684,36 @@ def test_engine_reason_is_forwarded_verbatim(
684684
assert result.reason == engine_reason
685685

686686

687+
@requires_engine_reasons
688+
def test_engine_default_reason_maps_to_static(
689+
mock_flagsmith_client: MagicMock,
690+
) -> None:
691+
# Given
692+
key = "my_feature"
693+
mock_flagsmith_client.get_identity_flags.return_value = Flags(
694+
{
695+
key: Flag(
696+
feature_id=1,
697+
feature_name=key,
698+
enabled=True,
699+
value="v",
700+
reason="DEFAULT",
701+
)
702+
}
703+
)
704+
provider = FlagsmithProvider(mock_flagsmith_client)
705+
706+
# When
707+
result = provider.resolve_string_details(
708+
key,
709+
default_value="default",
710+
evaluation_context=EvaluationContext(targeting_key="user-1"),
711+
)
712+
713+
# Then
714+
assert result.reason == Reason.STATIC
715+
716+
687717
@requires_engine_reasons
688718
def test_disabled_reason_wins_over_engine_reason(
689719
mock_flagsmith_client: MagicMock,

0 commit comments

Comments
 (0)