Skip to content

Commit 5d1b91e

Browse files
committed
feat: use the variant as the exposure event value
When `get_experiment_flag` records a `$flag_exposure` event, send the selected variant as the event value when present (the variant's key, or "control" for the control bucket), falling back to the flag value for standard flags. This identifies the experiment cohort without changing the event shape.
1 parent e50ada6 commit 5d1b91e

2 files changed

Lines changed: 74 additions & 2 deletions

File tree

flagsmith/flagsmith.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,11 +362,13 @@ def get_experiment_flag(
362362
if not self._event_processor:
363363
raise ValueError("Events must be enabled to use experiment flags.")
364364
flag = self.get_identity_flags(identifier, traits).get_flag(feature_name)
365-
if not flag.is_default:
365+
if isinstance(flag, Flag):
366366
self.track_exposure_event(
367367
feature_name=feature_name,
368368
identifier=identifier,
369-
value=flag.value,
369+
# The variant identifies the cohort for multivariate experiments;
370+
# fall back to the value for standard flags.
371+
value=flag.variant if flag.variant is not None else flag.value,
370372
traits=traits,
371373
)
372374
return flag

tests/test_flagsmith.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,3 +1116,73 @@ def default_flag_handler(feature_name: str) -> DefaultFlag:
11161116
assert result.is_default is True
11171117
assert result.value == "default-variant"
11181118
mock_track.assert_not_called()
1119+
1120+
1121+
def test_get_experiment_flag_uses_variant_as_exposure_value(
1122+
mocker: MockerFixture, api_key: str
1123+
) -> None:
1124+
# Given - a resolved flag carrying a variant
1125+
config = EventProcessorConfig(events_api_url="http://test/")
1126+
flagsmith = Flagsmith(
1127+
environment_key=api_key, enable_events=True, event_processor_config=config
1128+
)
1129+
flag = Flag(
1130+
enabled=True,
1131+
value="blue",
1132+
feature_name="checkout_v2",
1133+
feature_id=1,
1134+
variant="control",
1135+
)
1136+
mocker.patch.object(
1137+
flagsmith,
1138+
"get_identity_flags",
1139+
return_value=Flags(flags={"checkout_v2": flag}),
1140+
)
1141+
mock_track = mocker.patch.object(flagsmith._event_processor, "track_exposure_event")
1142+
1143+
# When
1144+
flagsmith.get_experiment_flag(feature_name="checkout_v2", identifier="user1")
1145+
1146+
# Then - the exposure value is the variant, not the flag value
1147+
mock_track.assert_called_once_with(
1148+
feature_name="checkout_v2",
1149+
identifier="user1",
1150+
value="control",
1151+
traits=None,
1152+
metadata=None,
1153+
)
1154+
1155+
1156+
def test_get_experiment_flag_falls_back_to_value_without_variant(
1157+
mocker: MockerFixture, api_key: str
1158+
) -> None:
1159+
# Given - a resolved flag with no variant
1160+
config = EventProcessorConfig(events_api_url="http://test/")
1161+
flagsmith = Flagsmith(
1162+
environment_key=api_key, enable_events=True, event_processor_config=config
1163+
)
1164+
flag = Flag(
1165+
enabled=True,
1166+
value="blue",
1167+
feature_name="checkout_v2",
1168+
feature_id=1,
1169+
variant=None,
1170+
)
1171+
mocker.patch.object(
1172+
flagsmith,
1173+
"get_identity_flags",
1174+
return_value=Flags(flags={"checkout_v2": flag}),
1175+
)
1176+
mock_track = mocker.patch.object(flagsmith._event_processor, "track_exposure_event")
1177+
1178+
# When
1179+
flagsmith.get_experiment_flag(feature_name="checkout_v2", identifier="user1")
1180+
1181+
# Then - the exposure value falls back to the flag value
1182+
mock_track.assert_called_once_with(
1183+
feature_name="checkout_v2",
1184+
identifier="user1",
1185+
value="blue",
1186+
traits=None,
1187+
metadata=None,
1188+
)

0 commit comments

Comments
 (0)