Skip to content

Commit cc1c293

Browse files
committed
feat/wire-analytics-pipeline-to-flag-evaluation
1 parent 3070147 commit cc1c293

2 files changed

Lines changed: 57 additions & 1 deletion

File tree

flagsmith/models.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import typing
44
from dataclasses import dataclass, field
55

6-
from flagsmith.analytics import AnalyticsProcessor
6+
from flagsmith.analytics import AnalyticsProcessor, PipelineAnalyticsProcessor
77
from flagsmith.exceptions import FlagsmithFeatureDoesNotExistError
88
from flagsmith.types import SDKEvaluationResult, SDKFlagResult
99

@@ -57,13 +57,19 @@ class Flags:
5757
flags: typing.Dict[str, Flag] = field(default_factory=dict)
5858
default_flag_handler: typing.Optional[typing.Callable[[str], DefaultFlag]] = None
5959
_analytics_processor: typing.Optional[AnalyticsProcessor] = None
60+
_pipeline_analytics_processor: typing.Optional[PipelineAnalyticsProcessor] = None
61+
_identity_identifier: typing.Optional[str] = None
62+
_traits: typing.Optional[typing.Dict[str, typing.Any]] = None
6063

6164
@classmethod
6265
def from_evaluation_result(
6366
cls,
6467
evaluation_result: SDKEvaluationResult,
6568
analytics_processor: typing.Optional[AnalyticsProcessor],
6669
default_flag_handler: typing.Optional[typing.Callable[[str], DefaultFlag]],
70+
pipeline_analytics_processor: typing.Optional[PipelineAnalyticsProcessor] = None,
71+
identity_identifier: typing.Optional[str] = None,
72+
traits: typing.Optional[typing.Dict[str, typing.Any]] = None,
6773
) -> Flags:
6874
return cls(
6975
flags={
@@ -73,6 +79,9 @@ def from_evaluation_result(
7379
},
7480
default_flag_handler=default_flag_handler,
7581
_analytics_processor=analytics_processor,
82+
_pipeline_analytics_processor=pipeline_analytics_processor,
83+
_identity_identifier=identity_identifier,
84+
_traits=traits,
7685
)
7786

7887
@classmethod
@@ -81,6 +90,9 @@ def from_api_flags(
8190
api_flags: typing.Sequence[typing.Mapping[str, typing.Any]],
8291
analytics_processor: typing.Optional[AnalyticsProcessor],
8392
default_flag_handler: typing.Optional[typing.Callable[[str], DefaultFlag]],
93+
pipeline_analytics_processor: typing.Optional[PipelineAnalyticsProcessor] = None,
94+
identity_identifier: typing.Optional[str] = None,
95+
traits: typing.Optional[typing.Dict[str, typing.Any]] = None,
8496
) -> Flags:
8597
flags = {
8698
flag_data["feature"]["name"]: Flag.from_api_flag(flag_data)
@@ -91,6 +103,9 @@ def from_api_flags(
91103
flags=flags,
92104
default_flag_handler=default_flag_handler,
93105
_analytics_processor=analytics_processor,
106+
_pipeline_analytics_processor=pipeline_analytics_processor,
107+
_identity_identifier=identity_identifier,
108+
_traits=traits,
94109
)
95110

96111
def all_flags(self) -> typing.List[Flag]:
@@ -141,6 +156,15 @@ def get_flag(self, feature_name: str) -> typing.Union[DefaultFlag, Flag]:
141156
if self._analytics_processor and hasattr(flag, "feature_name"):
142157
self._analytics_processor.track_feature(flag.feature_name)
143158

159+
if self._pipeline_analytics_processor and hasattr(flag, "feature_name"):
160+
self._pipeline_analytics_processor.record_evaluation_event(
161+
flag_key=flag.feature_name,
162+
enabled=flag.enabled,
163+
value=flag.value,
164+
identity_identifier=self._identity_identifier,
165+
traits=self._traits,
166+
)
167+
144168
return flag
145169

146170

tests/test_models.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import typing
2+
from unittest import mock
23

34
import pytest
45

6+
from flagsmith.analytics import PipelineAnalyticsProcessor
57
from flagsmith.models import Flag, Flags
68
from flagsmith.types import SDKEvaluationResult, SDKFlagResult
79

@@ -149,3 +151,33 @@ def test_flag_from_evaluation_result_missing_metadata__raises_expected() -> None
149151
# When & Then
150152
with pytest.raises(ValueError):
151153
Flag.from_evaluation_result(flag_result)
154+
155+
156+
def test_get_flag_records_pipeline_evaluation_event(
157+
pipeline_analytics_processor: PipelineAnalyticsProcessor,
158+
) -> None:
159+
flags = Flags(
160+
flags={"my_feature": Flag(enabled=True, value="v1", feature_name="my_feature", feature_id=1)},
161+
_pipeline_analytics_processor=pipeline_analytics_processor,
162+
_identity_identifier="user123",
163+
_traits={"plan": "premium"},
164+
)
165+
166+
with mock.patch.object(pipeline_analytics_processor, "record_evaluation_event") as mock_record:
167+
flags.get_flag("my_feature")
168+
169+
mock_record.assert_called_once_with(
170+
flag_key="my_feature",
171+
enabled=True,
172+
value="v1",
173+
identity_identifier="user123",
174+
traits={"plan": "premium"},
175+
)
176+
177+
178+
def test_get_flag_without_pipeline_processor() -> None:
179+
flags = Flags(
180+
flags={"my_feature": Flag(enabled=True, value="v1", feature_name="my_feature", feature_id=1)},
181+
)
182+
flag = flags.get_flag("my_feature")
183+
assert flag.enabled is True

0 commit comments

Comments
 (0)