Skip to content

Commit

Permalink
Merge pull request #160 from Flagsmith/release/3.3.2
Browse files Browse the repository at this point in the history
Release 3.3.2
  • Loading branch information
matthewelwell authored Mar 29, 2023
2 parents 930101d + b6f26df commit 3ade9e8
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 21 deletions.
5 changes: 1 addition & 4 deletions flag_engine/api/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ def get_value(self, obj, attr, **kwargs):
continue

existing_feature_state = features_map.get(fs.feature_id)
if (
not existing_feature_state
or fs.version > existing_feature_state.version
):
if not existing_feature_state or fs > existing_feature_state:
features_map[fs.feature_id] = fs

return list(features_map.values())
2 changes: 1 addition & 1 deletion flag_engine/api/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def serialize_feature_states(self, instance: typing.Any) -> typing.List[dict]:

existing_feature_state = feature_states.get(feature_state.feature_id)
if not existing_feature_state or (
feature_state.version > existing_feature_state.version
feature_state > existing_feature_state
):
feature_states[feature_state.feature_id] = feature_state
return self.feature_state_schema.dump(list(feature_states.values()), many=True)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name="flagsmith-flag-engine",
version="3.3.1",
version="3.3.2",
author="Flagsmith",
author_email="[email protected]",
packages=find_packages(include=["flag_engine", "flag_engine.*"]),
Expand Down
13 changes: 13 additions & 0 deletions tests/mock_django_classes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import typing
from dataclasses import dataclass, field
from datetime import datetime
from unittest.mock import MagicMock

from flag_engine.utils.datetime import utcnow_with_tz

Expand Down Expand Up @@ -162,6 +163,7 @@ def __init__(
] = None,
version: int = 1,
live_from: datetime = None,
gt_mock: MagicMock = MagicMock(),
):
self.id = id
self.feature_segment_id = getattr(feature_segment, "id", None)
Expand All @@ -178,6 +180,17 @@ def __init__(
)
self.version = version
self.live_from = live_from or utcnow_with_tz()
self.gt_mock = gt_mock

def __gt__(self, other: "DjangoFeatureState"):
"""
This is a workaround for now to avoid reimplementing the __gt__ method from the
FeatureState class in the django application.
# TODO: remove this logic from the engine and move it to the django repo.
"""
if self.gt_mock is None:
raise NotImplementedError()
return self.gt_mock(self, other)

def get_feature_state_value(self):
return self.value
Expand Down
57 changes: 48 additions & 9 deletions tests/unit/api/test_document_builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def test_build_environment_api_key_document(django_environment_api_key):


def test_build_environment_document_with_multiple_feature_state_versions(
django_project,
django_project, mocker
):
# Given
yesterday = utcnow_with_tz() - timedelta(days=1)
Expand All @@ -108,12 +108,31 @@ def test_build_environment_document_with_multiple_feature_state_versions(
)

# and 3 feature states for the same feature, 2 with live_from dates in the past and
# one with a live from date in the future
# one with a live from date in the future. Note that, due to the fact that we are
# using the FeatureState.__gt__ method to compare feature states (which is too
# complicated to replicate here), we're exposing a mock to pass to the __gt__ method
# on the DjangoFeatureState class we're using here. We then mimic the behaviour of
# the method in the FeatureState class in the Django project.
default_fs_kwargs = {"feature": feature, "live_from": yesterday, "enabled": True}
feature_state_v1 = DjangoFeatureState(id=1, version=1, **default_fs_kwargs)
feature_state_v2 = DjangoFeatureState(id=2, version=2, **default_fs_kwargs)

gt_mock_fs_1 = mocker.MagicMock(return_value=False)
feature_state_v1 = DjangoFeatureState(
id=1, version=1, **default_fs_kwargs, gt_mock=gt_mock_fs_1
)

gt_mock_fs_2 = mocker.MagicMock(return_value=True)
feature_state_v2 = DjangoFeatureState(
id=2, version=2, **default_fs_kwargs, gt_mock=gt_mock_fs_2
)

gt_mock_fs_3 = mocker.MagicMock(return_value=False)
feature_state_v3 = DjangoFeatureState(
id=3, version=3, feature=feature, enabled=True, live_from=tomorrow
id=3,
version=3,
feature=feature,
enabled=True,
live_from=tomorrow,
gt_mock=gt_mock_fs_3,
)

django_environment = DjangoEnvironment(
Expand All @@ -132,21 +151,41 @@ def test_build_environment_document_with_multiple_feature_state_versions(


def test_build_identity_document_with_multiple_feature_state_versions(
django_environment, django_disabled_feature_state
django_environment, django_disabled_feature_state, mocker
):
# Given
yesterday = utcnow_with_tz() - timedelta(days=1)
tomorrow = utcnow_with_tz() + timedelta(days=1)
feature = django_disabled_feature_state.feature

gt_mock_identity_fs_1 = mocker.MagicMock(return_value=False)
identity_feature_state_v1 = DjangoFeatureState(
id=2, version=1, feature=feature, live_from=yesterday, enabled=True
id=2,
version=1,
feature=feature,
live_from=yesterday,
enabled=True,
gt_mock=gt_mock_identity_fs_1,
)

gt_mock_identity_fs_2 = mocker.MagicMock(return_value=True)
identity_feature_state_v2 = DjangoFeatureState(
id=3, version=2, feature=feature, live_from=yesterday, enabled=True
id=3,
version=2,
feature=feature,
live_from=yesterday,
enabled=True,
gt_mock=gt_mock_identity_fs_2,
)

gt_mock_identity_fs_3 = mocker.MagicMock(return_value=False)
identity_feature_state_v3 = DjangoFeatureState(
id=3, version=2, feature=feature, live_from=tomorrow, enabled=True
id=3,
version=2,
feature=feature,
live_from=tomorrow,
enabled=True,
gt_mock=gt_mock_identity_fs_3,
)
django_identity = DjangoIdentity(
id=1,
Expand Down
34 changes: 28 additions & 6 deletions tests/unit/api/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import pytest
from marshmallow import fields

from flag_engine.features.constants import STANDARD
from flag_engine.utils.datetime import utcnow_with_tz
from flag_engine.api.fields import (
APITraitValueField,
DjangoFeatureStatesRelatedManagerField,
DjangoRelatedManagerField,
)
from tests.mock_django_classes import DjangoFeatureState, DjangoFeature


@pytest.mark.parametrize(
Expand Down Expand Up @@ -82,7 +84,9 @@ def filter_func(e):
assert serialized_data == [3, 4]


def test_django_feature_state_related_manager_field_serialize_discards_old_versions():
def test_django_feature_state_related_manager_field_serialize_discards_old_versions(
django_project,
):
# Given
# a mock object to serialize
attribute_name = "feature_states"
Expand All @@ -91,12 +95,30 @@ def test_django_feature_state_related_manager_field_serialize_discards_old_versi
# and which has some 'feature states' associated with it in the way that you'd
# expect a django object to. Each feature state associated with the same feature
# but with incrementing version numbers.
feature_id = 1
yesterday = utcnow_with_tz() - timedelta(days=1)
feature_states = [
mock.MagicMock(id=i, feature_id=feature_id, version=i, live_from=yesterday)
for i in (1, 2, 3, 4, 5)
]

def gt_mock_side_effect(first, second):
# Simplified version of FeatureState.__gt__ from the django project.
return first.live_from < utcnow_with_tz() and (
first.live_from > second.live_from or first.version > second.version
)

django_feature = DjangoFeature(
id=1, name="test_feature", project=django_project, type=STANDARD
)

feature_states = []
for i in range(1, 6):
mock_fs = DjangoFeatureState(
id=i,
feature=django_feature,
version=i,
live_from=yesterday,
enabled=True,
gt_mock=mock.MagicMock(side_effect=gt_mock_side_effect),
)
feature_states.append(mock_fs)

getattr(object_to_serialize, attribute_name).all.return_value = feature_states

# and a filter function which will filter out the last feature state based on it's
Expand Down

0 comments on commit 3ade9e8

Please sign in to comment.