Skip to content

Commit a4826f8

Browse files
authored
feat: Support implicit IdentityContext.key (#274)
1 parent 4004b6b commit a4826f8

File tree

4 files changed

+27
-15
lines changed

4 files changed

+27
-15
lines changed

.gitmodules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
[submodule "tests/engine_tests/engine-test-data"]
22
path = tests/engine_tests/engine-test-data
33
url = https://github.com/flagsmith/engine-test-data.git
4-
tag = v3.0.0
4+
tag = v3.1.0

flag_engine/context/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class FeatureValue(TypedDict):
3030

3131
class IdentityContext(TypedDict):
3232
identifier: str
33-
key: str
33+
key: NotRequired[str]
3434
traits: NotRequired[Dict[str, ContextValue]]
3535

3636

flag_engine/segments/evaluator.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ class FeatureContextWithSegmentName(TypedDict, typing.Generic[FeatureMetadataT])
4141
segment_name: str
4242

4343

44+
# Type alias for EvaluationContext with any metadata types
45+
# used in internal evaluation logic
46+
_EvaluationContextAnyMeta = EvaluationContext[typing.Any, typing.Any]
47+
48+
4449
def get_evaluation_result(
4550
context: EvaluationContext[SegmentMetadataT, FeatureMetadataT],
4651
) -> EvaluationResult[SegmentMetadataT, FeatureMetadataT]:
@@ -90,11 +95,7 @@ def get_evaluation_result(
9095
)
9196
)
9297

93-
identity_key = (
94-
identity_context["key"]
95-
if (identity_context := context.get("identity"))
96-
else None
97-
)
98+
identity_key = _get_identity_key(context)
9899
for feature_context in (context.get("features") or {}).values():
99100
feature_name = feature_context["name"]
100101
if feature_context_with_segment_name := segment_feature_contexts.get(
@@ -174,7 +175,7 @@ def get_flag_result_from_feature_context(
174175

175176

176177
def is_context_in_segment(
177-
context: EvaluationContext[typing.Any, typing.Any],
178+
context: _EvaluationContextAnyMeta,
178179
segment_context: SegmentContext[typing.Any, typing.Any],
179180
) -> bool:
180181
return bool(rules := segment_context["rules"]) and all(
@@ -186,7 +187,7 @@ def is_context_in_segment(
186187

187188

188189
def context_matches_rule(
189-
context: EvaluationContext[typing.Any, typing.Any],
190+
context: _EvaluationContextAnyMeta,
190191
rule: SegmentRule,
191192
segment_key: SupportsStr,
192193
) -> bool:
@@ -216,7 +217,7 @@ def context_matches_rule(
216217

217218

218219
def context_matches_condition(
219-
context: EvaluationContext[typing.Any, typing.Any],
220+
context: _EvaluationContextAnyMeta,
220221
condition: SegmentCondition,
221222
segment_key: SupportsStr,
222223
) -> bool:
@@ -277,7 +278,7 @@ def context_matches_condition(
277278

278279

279280
def get_context_value(
280-
context: EvaluationContext[typing.Any, typing.Any],
281+
context: _EvaluationContextAnyMeta,
281282
property: str,
282283
) -> ContextValue:
283284
value = None
@@ -371,8 +372,19 @@ def inner(
371372
}
372373

373374

375+
def _get_identity_key(
376+
context: _EvaluationContextAnyMeta,
377+
) -> typing.Optional[SupportsStr]:
378+
if identity_context := context.get("identity"):
379+
return (
380+
identity_context.get("key")
381+
or f"{context['environment']['key']}_{identity_context['identifier']}"
382+
)
383+
return None
384+
385+
374386
def _get_trait_value(
375-
context: EvaluationContext[SegmentMetadataT],
387+
context: _EvaluationContextAnyMeta,
376388
trait_key: str,
377389
) -> ContextValue:
378390
if identity_context := context.get("identity"):
@@ -384,7 +396,7 @@ def _get_trait_value(
384396
@lru_cache
385397
def _get_context_value_getter(
386398
property: str,
387-
) -> typing.Callable[[EvaluationContext[SegmentMetadataT]], ContextValue]:
399+
) -> typing.Callable[[_EvaluationContextAnyMeta], ContextValue]:
388400
"""
389401
Get a function to retrieve a context value based on property value,
390402
assumed to be either a JSONPath string or a trait key.
@@ -399,7 +411,7 @@ def _get_context_value_getter(
399411
# but not a valid JSONPath, is used.
400412
return partial(_get_trait_value, trait_key=property)
401413

402-
def getter(context: EvaluationContext[SegmentMetadataT]) -> ContextValue:
414+
def getter(context: _EvaluationContextAnyMeta) -> ContextValue:
403415
value: object
404416
if (value := _get_trait_value(context, property)) is not None:
405417
return value

0 commit comments

Comments
 (0)