Skip to content

Commit 2ae6edc

Browse files
authored
refactor: Migrate from is_context_in_segment to get_evaluation_result (#6896)
1 parent 2d9f0fa commit 2ae6edc

11 files changed

Lines changed: 319 additions & 238 deletions

File tree

api/environments/identities/helpers.py

Lines changed: 0 additions & 31 deletions
This file was deleted.

api/environments/identities/models.py

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from django.db import models
44
from django.db.models import Prefetch, Q
5+
from flag_engine.engine import get_evaluation_result
56

67
from environments.identities.managers import IdentityManager
78
from environments.identities.traits.models import Trait
@@ -11,15 +12,7 @@
1112
from features.multivariate.models import MultivariateFeatureStateValue
1213
from features.versioning.versioning_service import get_environment_flags_list
1314
from segments.models import Segment
14-
from util.engine_models.context.mappers import (
15-
is_context_in_segment,
16-
map_environment_identity_to_context,
17-
)
18-
from util.mappers.engine import (
19-
map_identity_to_engine,
20-
map_segment_to_engine,
21-
map_traits_to_engine,
22-
)
15+
from util.mappers.engine import map_environment_to_evaluation_context
2316

2417

2518
class Identity(models.Model):
@@ -154,7 +147,6 @@ def get_segments(
154147
:param overrides_only: only retrieve the segments which have a valid override in the environment
155148
:return: List of matching segments
156149
"""
157-
matching_segments = []
158150
db_traits = (
159151
self.identity_traits.all() if (traits is None and self.id) else traits or []
160152
)
@@ -164,29 +156,18 @@ def get_segments(
164156
else:
165157
all_segments = self.environment.project.get_segments_from_cache()
166158

167-
engine_identity = map_identity_to_engine(
168-
self,
169-
with_overrides=False,
170-
with_traits=False,
159+
segments_by_pk = {segment.pk: segment for segment in all_segments}
160+
context = map_environment_to_evaluation_context(
161+
identity=self,
162+
environment=self.environment,
163+
traits=db_traits,
164+
segments=all_segments,
171165
)
172-
engine_traits = map_traits_to_engine(db_traits)
173-
174-
for segment in all_segments:
175-
engine_segment = map_segment_to_engine(segment)
176-
177-
context = map_environment_identity_to_context(
178-
environment=self.environment,
179-
identity=engine_identity,
180-
override_traits=engine_traits,
181-
)
182-
183-
if is_context_in_segment(
184-
context=context,
185-
segment=engine_segment,
186-
):
187-
matching_segments.append(segment)
188-
189-
return matching_segments
166+
result = get_evaluation_result(context)
167+
return [
168+
segments_by_pk[segment_result["metadata"]["pk"]]
169+
for segment_result in result["segments"]
170+
]
190171

191172
def get_all_user_traits(self): # type: ignore[no-untyped-def]
192173
# this is pointless, we should probably replace all uses with the below code

api/features/models.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
LifecycleModelMixin,
2626
hook,
2727
)
28+
from flag_engine.utils.hashing import get_hashed_percentage_for_object_ids
2829
from ordered_model.models import OrderedModelBase # type: ignore[import-untyped]
2930
from simple_history.models import HistoricalRecords # type: ignore[import-untyped]
3031

@@ -49,9 +50,6 @@
4950
SoftDeleteExportableModel,
5051
abstract_base_auditable_model_factory,
5152
)
52-
from environments.identities.helpers import (
53-
get_hashed_percentage_for_object_ids,
54-
)
5553
from features.constants import ENVIRONMENT, FEATURE_SEGMENT, IDENTITY
5654
from features.custom_lifecycle import CustomLifecycleModelMixin
5755
from features.feature_states.models import AbstractBaseFeatureValueModel
@@ -750,8 +748,8 @@ def get_multivariate_feature_state_value(
750748
# avoid further queries to the DB
751749
mv_options = list(self.multivariate_feature_state_values.all())
752750

753-
percentage_value = (
754-
get_hashed_percentage_for_object_ids([self.id, identity_hash_key]) * 100
751+
percentage_value = get_hashed_percentage_for_object_ids(
752+
[self.id, identity_hash_key]
755753
)
756754

757755
# Iterate over the mv options in order of id (so we get the same value each

api/integrations/webhook/serializers.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
import typing
22

33
from django.db.models import Q
4+
from flag_engine.engine import get_evaluation_result
45
from rest_framework import serializers
56

67
from features.serializers import FeatureStateSerializerFull
78
from integrations.common.serializers import (
89
BaseEnvironmentIntegrationModelSerializer,
910
)
1011
from segments.models import Segment
11-
from util.engine_models.context.mappers import is_context_in_segment
12-
from util.mappers.engine import (
13-
map_engine_identity_to_context,
14-
map_identity_to_engine,
15-
map_segment_to_engine,
16-
)
12+
from util.mappers.engine import map_environment_to_evaluation_context
1713

1814
from .models import WebhookConfiguration
1915

@@ -32,16 +28,14 @@ class Meta:
3228
fields = ("id", "name", "member")
3329

3430
def get_member(self, obj: Segment) -> bool:
35-
engine_identity = map_identity_to_engine(
36-
self.context.get("identity"), # type: ignore[arg-type]
37-
with_overrides=False,
38-
)
39-
engine_segment = map_segment_to_engine(obj)
40-
context = map_engine_identity_to_context(engine_identity)
41-
return is_context_in_segment(
42-
context=context,
43-
segment=engine_segment,
31+
identity = self.context["identity"]
32+
context = map_environment_to_evaluation_context(
33+
identity=identity,
34+
environment=identity.environment,
35+
segments=[obj],
4436
)
37+
result = get_evaluation_result(context)
38+
return bool(result["segments"])
4539

4640

4741
class IntegrationFeatureStateSerializer(FeatureStateSerializerFull):

api/segments/types.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from typing_extensions import TypedDict
2+
3+
4+
class SegmentEngineMetadata(TypedDict):
5+
pk: int

api/tests/integration/environments/identities/test_integration_identities.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131
@pytest.mark.parametrize(
3232
"hashed_percentage, expected_mv_value",
3333
(
34-
(variant_1_percentage_allocation / 100 - 0.01, variant_1_value),
35-
(total_variance_percentage / 100 - 0.01, variant_2_value),
36-
(total_variance_percentage / 100 + 0.01, control_value),
34+
(variant_1_percentage_allocation - 1, variant_1_value),
35+
(total_variance_percentage - 1, variant_2_value),
36+
(total_variance_percentage + 1, control_value),
3737
),
3838
)
3939
@mock.patch("features.models.get_hashed_percentage_for_object_ids")

api/tests/unit/environments/identities/test_unit_identities_helpers.py

Lines changed: 0 additions & 124 deletions
This file was deleted.

api/tests/unit/environments/identities/test_unit_identities_views.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@
2323
STRING,
2424
)
2525
from environments.identities import views
26-
from environments.identities.helpers import (
27-
get_hashed_percentage_for_object_ids,
28-
)
2926
from environments.identities.models import Identity
3027
from environments.identities.traits.models import Trait
3128
from environments.models import Environment, EnvironmentAPIKey
@@ -646,12 +643,9 @@ def test_identities_endpoint_returns_default_value_if_rule_type_percentage_split
646643
segment=segment, type=SegmentRule.ALL_RULE
647644
)
648645

649-
identity_percentage_value = get_hashed_percentage_for_object_ids(
650-
[segment.id, identity.id]
651-
)
652646
Condition.objects.create(
653647
operator=PERCENTAGE_SPLIT,
654-
value=int(identity_percentage_value / 2),
648+
value=0,
655649
rule=segment_rule,
656650
)
657651
feature_segment = FeatureSegment.objects.create(

api/tests/unit/features/test_unit_features_models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ def test_feature_state_type_feature_segment(
594594
assert feature_state.type == FEATURE_SEGMENT
595595

596596

597-
@pytest.mark.parametrize("hashed_percentage", (0.0, 0.3, 0.5, 0.8, 0.999999))
597+
@pytest.mark.parametrize("hashed_percentage", (0.0, 30.0, 50.0, 80.0, 99.9999))
598598
@mock.patch("features.models.get_hashed_percentage_for_object_ids")
599599
def test_get_multivariate_value_returns_correct_value_when_we_pass_identity( # type: ignore[no-untyped-def]
600600
mock_get_hashed_percentage,

0 commit comments

Comments
 (0)