Skip to content

Commit

Permalink
fix(versioning): multiple versioned segment overrides added to enviro…
Browse files Browse the repository at this point in the history
…nment document (#3974)
  • Loading branch information
matthewelwell authored May 17, 2024
1 parent 03cdee3 commit aa5cc95
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
58 changes: 58 additions & 0 deletions api/tests/unit/util/mappers/test_unit_mappers_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@

from environments.models import Environment
from features.models import FeatureSegment, FeatureState
from features.versioning.models import EnvironmentFeatureVersion
from features.versioning.tasks import enable_v2_versioning
from integrations.common.models import IntegrationsModel
from integrations.dynatrace.models import DynatraceConfiguration
from integrations.mixpanel.models import MixpanelConfiguration
from integrations.segment.models import SegmentConfiguration
from integrations.webhook.models import WebhookConfiguration
from segments.models import Segment, SegmentRule
from users.models import FFAdminUser
from util.mappers import engine

if TYPE_CHECKING:
Expand Down Expand Up @@ -598,3 +600,59 @@ def test_map_environment_to_engine_following_migration_to_v2_versioning(
assert mapped_segment_override.django_id == v2_segment_override.id
assert mapped_segment_override.enabled is True
assert mapped_segment_override.feature_state_value == v2_segment_override_value


def test_map_environment_to_engine_v2_versioning_segment_overrides(
environment_v2_versioning: Environment,
segment: Segment,
feature: "Feature",
staff_user: FFAdminUser,
) -> None:
# Given
# Another segment
another_segment = Segment.objects.create(
name="another_segment", project=feature.project
)

# First, let's create a version that includes 2 segment overrides
v2 = EnvironmentFeatureVersion.objects.create(
feature=feature, environment=environment_v2_versioning
)
for _segment in [segment, another_segment]:
FeatureState.objects.create(
feature=feature,
environment=environment_v2_versioning,
environment_feature_version=v2,
feature_segment=FeatureSegment.objects.create(
feature=feature,
segment=_segment,
environment=environment_v2_versioning,
environment_feature_version=v2,
),
)
v2.publish(staff_user)

# Now, let's create another new version which will keep one of the segment overrides
# and remove the other.
v3 = EnvironmentFeatureVersion.objects.create(
feature=feature, environment=environment_v2_versioning
)

v3_segment_override = FeatureState.objects.get(
feature_segment__segment=segment, environment_feature_version=v3
)
FeatureState.objects.filter(
feature_segment__segment=another_segment, environment_feature_version=v3
).delete()

v3.publish(staff_user)

# When
environment_model = engine.map_environment_to_engine(environment_v2_versioning)

# Then
assert len(environment_model.project.segments[0].feature_states) == 1
assert (
environment_model.project.segments[0].feature_states[0].django_id
== v3_segment_override.id
)
24 changes: 24 additions & 0 deletions api/util/mappers/engine.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections.abc import Iterable
from itertools import chain
from typing import TYPE_CHECKING, Dict, List, Optional
from uuid import UUID

from flag_engine.environments.integrations.models import IntegrationModel
from flag_engine.environments.models import (
Expand All @@ -25,6 +26,7 @@
)

from environments.constants import IDENTITY_INTEGRATIONS_RELATION_NAMES
from features.versioning.models import EnvironmentFeatureVersion

if TYPE_CHECKING: # pragma: no cover
from environments.identities.models import Identity, Trait
Expand Down Expand Up @@ -200,6 +202,16 @@ def map_environment_to_engine(
project_segment_feature_states_by_segment_id = _get_segment_feature_states(
project_segments,
environment.pk,
latest_environment_feature_version_uuids=(
{
efv.uuid
for efv in EnvironmentFeatureVersion.objects.get_latest_versions(
environment
)
}
if environment.use_v2_feature_versioning
else []
),
)
environment_feature_states: List["FeatureState"] = _get_prioritised_feature_states(
[
Expand Down Expand Up @@ -419,14 +431,26 @@ def _get_prioritised_feature_states(
def _get_segment_feature_states(
segments: Iterable["Segment"],
environment_id: int,
latest_environment_feature_version_uuids: Iterable[UUID],
) -> Dict[int, List["FeatureState"]]:
feature_states_by_segment_id = {}

for segment in segments:
segment_feature_states = feature_states_by_segment_id.setdefault(segment.pk, [])

for feature_segment in segment.feature_segments.all():
if feature_segment.environment_id != environment_id:
continue

if (
latest_environment_feature_version_uuids
and feature_segment.environment_feature_version_id
not in latest_environment_feature_version_uuids
):
continue

segment_feature_states += _get_prioritised_feature_states(
feature_segment.feature_states.all()
)

return feature_states_by_segment_id

0 comments on commit aa5cc95

Please sign in to comment.