Skip to content

Commit

Permalink
Add CS Package Deprecate form and view
Browse files Browse the repository at this point in the history
  • Loading branch information
Oksamies committed Sep 10, 2024
1 parent 77ccb9b commit 896d7f5
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 0 deletions.
98 changes: 98 additions & 0 deletions django/thunderstore/api/cyberstorm/tests/test_package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import json

import pytest
from rest_framework.test import APIClient

from thunderstore.core.types import UserType
from thunderstore.repository.models import Package
from thunderstore.repository.models.team import TeamMember


@pytest.mark.django_db
def test_package_deprecate_api_view__succeeds(
api_client: APIClient,
package: Package,
team_member: TeamMember,
) -> None:
api_client.force_authenticate(team_member.user)

assert Package.objects.get(pk=package.pk).is_deprecated == False

response = api_client.post(
f"/api/cyberstorm/package/{package.namespace}/{package.name}/deprecate/",
json.dumps({"is_deprecated": True}),
content_type="application/json",
)
actual = response.json()

assert actual["is_deprecated"] == True
assert Package.objects.get(pk=package.pk).is_deprecated == True

response = api_client.post(
f"/api/cyberstorm/package/{package.namespace}/{package.name}/deprecate/",
json.dumps({"is_deprecated": False}),
content_type="application/json",
)
actual = response.json()

assert actual["is_deprecated"] == False
assert Package.objects.get(pk=package.pk).is_deprecated == False


@pytest.mark.django_db
def test_package_deprecate_api_view__returns_error_for_non_existent_package(
api_client: APIClient,
user: UserType,
) -> None:
api_client.force_authenticate(user)
response = api_client.post(
f"/api/cyberstorm/package/BAD/BAD/deprecate/",
json.dumps({"is_deprecated": True}),
content_type="application/json",
)
actual = response.json()

assert actual["detail"] == "Not found."


@pytest.mark.django_db
def test_package_deprecate_api_view__returns_error_for_no_user(
api_client: APIClient,
) -> None:
response = api_client.post(
f"/api/cyberstorm/package/BAD/BAD/deprecate/",
json.dumps({"is_deprecated": True}),
content_type="application/json",
)
actual = response.json()

assert actual["detail"] == "Authentication credentials were not provided."


@pytest.mark.django_db
def test_package_deprecate_api_view__returns_error_for_bad_data(
api_client: APIClient,
package: Package,
user: UserType,
) -> None:
api_client.force_authenticate(user)
package.is_active = False
package.save()

response = api_client.post(
f"/api/cyberstorm/package/{package.namespace}/{package.name}/deprecate/",
json.dumps({"bad_data": True}),
content_type="application/json",
)
actual = response.json()

assert actual["is_deprecated"] == ["This field is required."]

response = api_client.post(
f"/api/cyberstorm/package/{package.namespace}/{package.name}/deprecate/",
json.dumps({"is_deprecated": "bad"}),
content_type="application/json",
)
actual = response.json()

assert actual["is_deprecated"] == ["Must be a valid boolean."]
2 changes: 2 additions & 0 deletions django/thunderstore/api/cyberstorm/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .community_filters import CommunityFiltersAPIView
from .community_list import CommunityListAPIView
from .markdown import PackageVersionChangelogAPIView, PackageVersionReadmeAPIView
from .package import PackageDeprecateAPIView
from .package_listing import PackageListingAPIView
from .package_listing_list import (
PackageListingByCommunityListAPIView,
Expand Down Expand Up @@ -33,4 +34,5 @@
"TeamMemberListAPIView",
"TeamServiceAccountListAPIView",
"PackageRatingRateAPIView",
"PackageDeprecateAPIView",
]
48 changes: 48 additions & 0 deletions django/thunderstore/api/cyberstorm/views/package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from django.http import HttpRequest
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.generics import get_object_or_404
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from thunderstore.api.utils import conditional_swagger_auto_schema
from thunderstore.repository.forms import DeprecateForm
from thunderstore.repository.models import Package


class CyberstormDeprecatePackageRequestSerialiazer(serializers.Serializer):
is_deprecated = serializers.BooleanField()


class CyberstormDeprecatePackageResponseSerialiazer(serializers.Serializer):
is_deprecated = serializers.BooleanField()


class PackageDeprecateAPIView(APIView):
permission_classes = [IsAuthenticated]

@conditional_swagger_auto_schema(
request_body=CyberstormDeprecatePackageRequestSerialiazer,
responses={200: CyberstormDeprecatePackageResponseSerialiazer},
operation_id="cyberstorm.package.deprecate",
tags=["cyberstorm"],
)
def post(self, request: HttpRequest, namespace_id: str, package_name: str):
serializer = CyberstormDeprecatePackageRequestSerialiazer(data=request.data)
serializer.is_valid(raise_exception=True)
package = get_object_or_404(
Package,
namespace__name=namespace_id,
name__iexact=package_name,
)
form = DeprecateForm(
user=request.user,
instance=package,
data=serializer.validated_data,
)
if form.is_valid():
package = form.execute()
return Response(CyberstormDeprecatePackageResponseSerialiazer(package).data)
else:
raise ValidationError(form.errors)
6 changes: 6 additions & 0 deletions django/thunderstore/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
CommunityAPIView,
CommunityFiltersAPIView,
CommunityListAPIView,
PackageDeprecateAPIView,
PackageListingAPIView,
PackageListingByCommunityListAPIView,
PackageListingByDependencyListAPIView,
Expand Down Expand Up @@ -84,6 +85,11 @@
PackageRatingRateAPIView.as_view(),
name="cyberstorm.package_rating.rate",
),
path(
"package/<str:namespace_id>/<str:package_name>/deprecate/",
PackageDeprecateAPIView.as_view(),
name="cyberstorm.package.deprecate",
),
path(
"team/<str:team_id>/",
TeamAPIView.as_view(),
Expand Down
1 change: 1 addition & 0 deletions django/thunderstore/repository/forms/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .package import *
from .package_rating import *
from .team import *
34 changes: 34 additions & 0 deletions django/thunderstore/repository/forms/package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from typing import Optional

from django import forms
from django.contrib.auth import get_user_model

from thunderstore.core.types import UserType
from thunderstore.repository.models import Package

User = get_user_model()


class DeprecateForm(forms.ModelForm):
class Meta:
model = Package
fields = ["is_deprecated"]

def __init__(self, user: Optional[UserType], *args, **kwargs):
super().__init__(*args, **kwargs)
self.user = user

def clean(self):
self.instance.ensure_user_can_manage_deprecation(self.user)
value = self.data.get("is_deprecated", None)
if not isinstance(value, bool):
raise forms.ValidationError("Given value for is_deprecated is invalid.")
return super().clean()

def execute(self):
desired_state = self.cleaned_data.get("is_deprecated")
if desired_state:
self.instance.deprecate()
else:
self.instance.undeprecate()
return self.instance
137 changes: 137 additions & 0 deletions django/thunderstore/repository/tests/test_package_forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import pytest
from django.forms import ValidationError

from thunderstore.account.models.service_account import ServiceAccount
from thunderstore.repository.forms import DeprecateForm
from thunderstore.repository.models import Package
from thunderstore.repository.models.team import TeamMember


@pytest.mark.django_db
def test_package_deprecate_form__correct_values__succeeds(
team_member: TeamMember, package: Package
) -> None:
# Deprecate
p = Package.objects.get(pk=package.pk)
form = DeprecateForm(
user=team_member.user,
instance=p,
data={"is_deprecated": True},
)
assert form.is_valid() is True
pkg = form.execute()
assert pkg.is_deprecated == True
# Undeprecate
p = Package.objects.get(pk=package.pk)
form = DeprecateForm(
user=team_member.user,
instance=p,
data={"is_deprecated": False},
)
assert form.is_valid() is True
pkg = form.execute()
assert pkg.is_deprecated == False


@pytest.mark.django_db
def test_package_deprecate_form__already_on_state__succeeds(
team_member: TeamMember, package: Package
) -> None:
# Deprecate
p = Package.objects.get(pk=package.pk)
form = DeprecateForm(
user=team_member.user,
instance=p,
data={"is_deprecated": True},
)
assert form.is_valid() is True
pkg = form.execute()
assert pkg.is_deprecated == True
# Second time
p = Package.objects.get(pk=package.pk)
form = DeprecateForm(
user=team_member.user,
instance=p,
data={"is_deprecated": True},
)
assert form.is_valid() is True
pkg = form.execute()
assert pkg.is_deprecated == True
# Undeprecate
p = Package.objects.get(pk=package.pk)
form = DeprecateForm(
user=team_member.user,
instance=p,
data={"is_deprecated": False},
)
assert form.is_valid() is True
pkg = form.execute()
assert pkg.is_deprecated == False
# Second time
p = Package.objects.get(pk=package.pk)
form = DeprecateForm(
user=team_member.user,
instance=p,
data={"is_deprecated": False},
)
assert form.is_valid() is True
pkg = form.execute()
assert pkg.is_deprecated == False


@pytest.mark.django_db
def test_package_deprecate_form__bad_value__fails(
team_member: TeamMember, package: Package
) -> None:
error = "Given value for is_deprecated is invalid."
form = DeprecateForm(
user=team_member.user,
instance=package,
data={"is_deprecated": "bad"},
)
assert form.is_valid() is False
assert error in str(repr(form.errors))


@pytest.mark.django_db
def test_package_deprecate_form__user_none__fails(
package: Package,
) -> None:
form = DeprecateForm(
user=None,
instance=package,
data={"is_deprecated": True},
)
with pytest.raises(ValidationError) as e:
form.clean()
assert "Must be authenticated" in str(e.value)


@pytest.mark.django_db
def test_package_deprecate_form__user_deactivated__fails(
team_member: TeamMember, package: Package
) -> None:
team_member.user.is_active = False
team_member.user.save()
form = DeprecateForm(
user=team_member.user,
instance=package,
data={"is_deprecated": True},
)
with pytest.raises(ValidationError) as e:
form.clean()
assert "User has been deactivated" in str(e.value)


@pytest.mark.django_db
def test_package_deprecate_form__user_is_service_account__fails(
service_account: ServiceAccount, package: Package
) -> None:
form = DeprecateForm(
user=service_account.user,
instance=package,
data={"is_deprecated": True},
)
with pytest.raises(ValidationError) as e:
form.clean()
assert "Service accounts are unable to perform this action" in str(e.value)

0 comments on commit 896d7f5

Please sign in to comment.