Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add is_live filter to versions endpoint #3688

Merged
merged 4 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
)