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

Add CS Package Deprecate form and view #1025

Open
wants to merge 1 commit into
base: cs-pkg-rate
Choose a base branch
from
Open
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
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)
Loading