Skip to content

Commit 382c99e

Browse files
committed
fix: get_identity_segments returns identity override segments
1 parent 60e244f commit 382c99e

File tree

4 files changed

+61
-6
lines changed

4 files changed

+61
-6
lines changed

flagsmith/flagsmith.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
map_context_and_identity_data_to_context,
1515
map_environment_document_to_context,
1616
map_environment_document_to_environment_updated_at,
17+
map_segment_results_to_identity_segments,
1718
)
1819
from flagsmith.models import DefaultFlag, Flags, Segment
1920
from flagsmith.offline_handlers import OfflineHandler
@@ -283,10 +284,8 @@ def get_identity_segments(
283284
evaluation_result = engine.get_evaluation_result(
284285
context=context,
285286
)
286-
return [
287-
Segment(id=int(segment_result["key"]), name=segment_result["name"])
288-
for segment_result in evaluation_result["segments"]
289-
]
287+
288+
return map_segment_results_to_identity_segments(evaluation_result["segments"])
290289

291290
def update_environment(self) -> None:
292291
try:

flagsmith/mappers.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
SegmentContext,
1212
SegmentRule,
1313
)
14+
from flag_engine.result.types import SegmentResult
1415
from flag_engine.segments.types import ContextValue
1516

16-
from flagsmith.types import StreamEvent, TraitConfig
17+
from flagsmith.models import Segment
18+
from flagsmith.types import SegmentMetadata, StreamEvent, TraitConfig
1719

1820
OverrideKey = typing.Tuple[
1921
str,
@@ -24,6 +26,25 @@
2426
OverridesKey = typing.Tuple[OverrideKey, ...]
2527

2628

29+
def map_segment_results_to_identity_segments(
30+
segment_results: list[SegmentResult],
31+
) -> list[Segment]:
32+
identity_segments: list[Segment] = []
33+
for segment_result in segment_results:
34+
if raw_metadata := segment_result.get("metadata"):
35+
metadata = typing.cast(SegmentMetadata, raw_metadata)
36+
if metadata.get("source") == "api" and (
37+
(flagsmith_id := metadata.get("flagsmith_id")) is not None
38+
):
39+
identity_segments.append(
40+
Segment(
41+
id=flagsmith_id,
42+
name=segment_result["name"],
43+
)
44+
)
45+
return identity_segments
46+
47+
2748
def map_sse_event_to_stream_event(event: sseclient.Event) -> StreamEvent:
2849
event_data = json.loads(event.data)
2950
return {
@@ -90,7 +111,7 @@ def map_environment_document_to_context(
90111
},
91112
"segments": {
92113
**{
93-
(segment_key := str(segment["id"])): {
114+
(segment_key := str(segment_id := segment["id"])): {
94115
"key": segment_key,
95116
"name": segment["name"],
96117
"rules": _map_environment_document_rules_to_context_rules(
@@ -101,6 +122,12 @@ def map_environment_document_to_context(
101122
segment.get("feature_states") or []
102123
)
103124
),
125+
"metadata": dict(
126+
SegmentMetadata(
127+
flagsmith_id=segment_id,
128+
source="api",
129+
)
130+
),
104131
}
105132
for segment in environment_document["project"]["segments"]
106133
},
@@ -142,6 +169,7 @@ def _map_identity_overrides_to_segments(
142169
# Create a segment context for each unique set of overrides
143170
# Generate a unique key to avoid collisions
144171
segment_key = str(hash(overrides_key))
172+
segment_metadata = SegmentMetadata(source="identity_overrides")
145173
segment_contexts[segment_key] = SegmentContext(
146174
key="", # Identity override segments never use % Split operator
147175
name="identity_overrides",
@@ -168,6 +196,7 @@ def _map_identity_overrides_to_segments(
168196
}
169197
for feature_key, feature_name, feature_enabled, feature_value in overrides_key
170198
],
199+
metadata=dict(segment_metadata),
171200
)
172201
return segment_contexts
173202

flagsmith/types.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,10 @@ class TraitConfig(typing.TypedDict):
3333
class ApplicationMetadata(typing.TypedDict):
3434
name: NotRequired[str]
3535
version: NotRequired[str]
36+
37+
38+
class SegmentMetadata(typing.TypedDict):
39+
flagsmith_id: NotRequired[int]
40+
"""The ID of the segment used in Flagsmith API."""
41+
source: NotRequired[typing.Literal["api", "identity_overrides"]]
42+
"""The source of the segment, e.g. 'api', 'identity_overrides'."""

tests/test_flagsmith.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,26 @@ def test_get_identity_segments_with_valid_trait(
509509
assert segments[0].name == "Test segment" # obtained from data/environment.json
510510

511511

512+
def test_get_identity_segments__identity_overrides__returns_expected(
513+
local_eval_flagsmith: Flagsmith,
514+
) -> None:
515+
# Given
516+
# the identifier matches the identity override in data/environment.json
517+
identifier = "overridden-id"
518+
# traits match the "Test segment" segment in data/environment.json
519+
traits = {"foo": "bar"}
520+
521+
# When
522+
segments = local_eval_flagsmith.get_identity_segments(identifier, traits)
523+
524+
# Then
525+
# identity override virtual segment is not returned,
526+
# only the segment matching the traits
527+
assert len(segments) == 1
528+
assert segments[0].id == 1
529+
assert segments[0].name == "Test segment"
530+
531+
512532
def test_local_evaluation_requires_server_key() -> None:
513533
with pytest.raises(ValueError):
514534
Flagsmith(environment_key="not-a-server-key", enable_local_evaluation=True)

0 commit comments

Comments
 (0)