Skip to content

Commit

Permalink
feat: add is_live filter to versions endpoint (#3688)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewelwell committed Apr 3, 2024
1 parent 58b1f55 commit af0cc9c
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 1 deletion.
4 changes: 4 additions & 0 deletions api/features/versioning/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ def save(self, **kwargs):
live_from=live_from, published_by=self.context["request"].user
)
return self.instance


class EnvironmentFeatureVersionQuerySerializer(serializers.Serializer):
is_live = serializers.BooleanField(allow_null=True, required=False, default=None)
29 changes: 28 additions & 1 deletion api/features/versioning/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from django.db.models import BooleanField, ExpressionWrapper, Q
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.decorators import method_decorator
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.mixins import (
CreateModelMixin,
Expand All @@ -25,12 +29,19 @@
from features.versioning.serializers import (
EnvironmentFeatureVersionFeatureStateSerializer,
EnvironmentFeatureVersionPublishSerializer,
EnvironmentFeatureVersionQuerySerializer,
EnvironmentFeatureVersionSerializer,
)
from projects.permissions import VIEW_PROJECT
from users.models import FFAdminUser


@method_decorator(
name="list",
decorator=swagger_auto_schema(
query_serializer=EnvironmentFeatureVersionQuerySerializer()
),
)
class EnvironmentFeatureVersionViewSet(
GenericViewSet,
ListModelMixin,
Expand Down Expand Up @@ -77,10 +88,26 @@ def get_queryset(self):
if getattr(self, "swagger_fake_view", False):
return EnvironmentFeatureVersion.objects.none()

return EnvironmentFeatureVersion.objects.filter(
queryset = EnvironmentFeatureVersion.objects.filter(
environment=self.environment, feature_id=self.feature
)

query_serializer = EnvironmentFeatureVersionQuerySerializer(
data=self.request.query_params
)
query_serializer.is_valid(raise_exception=True)

if (is_live := query_serializer.validated_data.get("is_live")) is not None:
queryset = queryset.annotate(
_is_live=ExpressionWrapper(
Q(published_at__isnull=False, live_from__lte=timezone.now()),
output_field=BooleanField(),
)
)
queryset = queryset.filter(_is_live=is_live)

return queryset

def perform_create(self, serializer: Serializer) -> None:
created_by = None
if isinstance(self.request.user, FFAdminUser):
Expand Down
62 changes: 62 additions & 0 deletions api/tests/unit/features/versioning/test_unit_versioning_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@
from rest_framework import status

from environments.models import Environment
from environments.permissions.constants import VIEW_ENVIRONMENT
from features.models import FeatureSegment, FeatureState
from features.versioning.models import EnvironmentFeatureVersion
from projects.permissions import VIEW_PROJECT
from tests.types import (
WithEnvironmentPermissionsCallable,
WithProjectPermissionsCallable,
)

if typing.TYPE_CHECKING:
from rest_framework.test import APIClient
Expand Down Expand Up @@ -520,3 +526,59 @@ def test_cannot_delete_environment_default_feature_state_for_unpublished_environ

segment_override.refresh_from_db()
assert segment_override.deleted is False


def test_filter_versions_by_is_live(
environment_v2_versioning: Environment,
feature: "Feature",
staff_user: "FFAdminUser",
staff_client: "APIClient",
with_environment_permissions: WithEnvironmentPermissionsCallable,
with_project_permissions: WithProjectPermissionsCallable,
) -> None:
# Given
# we give the user the correct permissions
with_environment_permissions([VIEW_ENVIRONMENT], environment_v2_versioning.id)
with_project_permissions([VIEW_PROJECT])

# an unpublished environment feature version
unpublished_environment_feature_version = EnvironmentFeatureVersion.objects.create(
environment=environment_v2_versioning, feature=feature
)

# and a published version
published_environment_feature_version = EnvironmentFeatureVersion.objects.create(
environment=environment_v2_versioning, feature=feature
)
published_environment_feature_version.publish(staff_user)

_base_url = reverse(
"api-v1:versioning:environment-feature-versions-list",
args=[environment_v2_versioning.id, feature.id],
)
live_versions_url = "%s?is_live=true" % _base_url
not_live_versions_url = "%s?is_live=false" % _base_url

# When
live_versions_response = staff_client.get(live_versions_url)
not_live_versions_response = staff_client.get(not_live_versions_url)

# Then
# only the live versions are returned (the initial version) and the one we
# published above when we request the live versions
assert live_versions_response.status_code == status.HTTP_200_OK

live_versions_response_json = live_versions_response.json()
assert live_versions_response_json["count"] == 2
assert unpublished_environment_feature_version.uuid not in [
result["uuid"] for result in live_versions_response_json["results"]
]

# and only the unpublished version is returned when we request the 'not live' versions
assert not_live_versions_response.status_code == status.HTTP_200_OK

not_live_versions_response_json = not_live_versions_response.json()
assert not_live_versions_response_json["count"] == 1
assert not_live_versions_response_json["results"][0]["uuid"] == str(
unpublished_environment_feature_version.uuid
)

0 comments on commit af0cc9c

Please sign in to comment.