From 34ff4657ac10978f6612a44897c90de7e4565e51 Mon Sep 17 00:00:00 2001 From: Oksamies Date: Sun, 7 Jan 2024 22:47:20 +0200 Subject: [PATCH] Add RemoveTeamMemberAPIView --- .../api/cyberstorm/tests/test_team.py | 85 ++++++++++++++++++- .../api/cyberstorm/views/__init__.py | 2 + .../thunderstore/api/cyberstorm/views/team.py | 57 ++++++++++++- django/thunderstore/api/urls.py | 6 ++ 4 files changed, 148 insertions(+), 2 deletions(-) diff --git a/django/thunderstore/api/cyberstorm/tests/test_team.py b/django/thunderstore/api/cyberstorm/tests/test_team.py index 7831c6719..1e4b0b2ae 100644 --- a/django/thunderstore/api/cyberstorm/tests/test_team.py +++ b/django/thunderstore/api/cyberstorm/tests/test_team.py @@ -7,7 +7,7 @@ from thunderstore.account.factories import ServiceAccountFactory from thunderstore.core.types import UserType from thunderstore.repository.factories import TeamFactory, TeamMemberFactory -from thunderstore.repository.models.team import Team +from thunderstore.repository.models.team import Team, TeamMember User = get_user_model() @@ -183,6 +183,89 @@ def test_team_member_list_api_view__for_member__sorts_results( assert result[4]["username"] == erin.username +@pytest.mark.django_db +def test_team_members_remove__when_removing_member__succeeds( + api_client: APIClient, + user: UserType, + team: Team, +): + TeamMemberFactory(team=team, user=user, role="owner") + api_client.force_authenticate(user) + just_a_member = TeamMemberFactory(team=team, role="member") + + response = api_client.post( + f"/api/cyberstorm/team/{team.name}/members/remove/", + json.dumps({"username": just_a_member.user.username}), + content_type="application/json", + ) + + assert response.status_code == 200 + response_json = response.json() + assert response_json["team_name"] == team.name + assert response_json["username"] == just_a_member.user.username + + +@pytest.mark.django_db +def test_team_members_remove__when_removing_member__fails_because_user_is_not_a_member_in_team( + api_client: APIClient, + user: UserType, + team: Team, +): + TeamMemberFactory(team=team, user=user, role="owner") + api_client.force_authenticate(user) + another_team = TeamFactory() + member_in_another_team = TeamMemberFactory(team=another_team, role="owner") + + response = api_client.post( + f"/api/cyberstorm/team/{team.name}/members/remove/", + json.dumps({"username": member_in_another_team.user.username}), + content_type="application/json", + ) + + assert response.status_code == 404 + response_json = response.json() + assert response_json["detail"] == "Not found." + + +@pytest.mark.django_db +def test_team_members_remove__when_removing_member__fails_because_team_does_not_exist( + api_client: APIClient, + user: UserType, +): + api_client.force_authenticate(user) + + response = api_client.post( + f"/api/cyberstorm/team/GhostTeam/members/remove/", + json.dumps({"username": user.username}), + content_type="application/json", + ) + + assert response.status_code == 404 + response_json = response.json() + assert response_json["detail"] == "Not found." + + +@pytest.mark.django_db +def test_team_members_remove__when_removing_member__fails_because_user_is_not_authenticated( + api_client: APIClient, + user: UserType, + team: Team, +): + TeamMemberFactory(team=team, user=user, role="owner") + just_a_member = TeamMemberFactory(team=team, role="member") + + response = api_client.post( + f"/api/cyberstorm/team/{team.name}/members/remove/", + json.dumps({"username": just_a_member.user.username}), + content_type="application/json", + ) + + assert response.status_code == 401 + response_json = response.json() + assert response_json["detail"] == "Authentication credentials were not provided." + assert TeamMember.objects.filter(team=team, user=user).count() == 1 + + @pytest.mark.django_db def test_team_service_account_list_api_view__for_member__returns_only_service_accounts( api_client: APIClient, diff --git a/django/thunderstore/api/cyberstorm/views/__init__.py b/django/thunderstore/api/cyberstorm/views/__init__.py index 9aec44f17..cef2ebfa2 100644 --- a/django/thunderstore/api/cyberstorm/views/__init__.py +++ b/django/thunderstore/api/cyberstorm/views/__init__.py @@ -12,6 +12,7 @@ from .package_rating import PackageRatingRateAPIView from .package_version_list import PackageVersionListAPIView from .team import ( + RemoveTeamMemberAPIView, TeamAPIView, TeamCreateAPIView, TeamMemberAddAPIView, @@ -38,4 +39,5 @@ "PackageDeprecateAPIView", "PackageListingEditCategoriesAPIView", "TeamCreateAPIView", + "RemoveTeamMemberAPIView", ] diff --git a/django/thunderstore/api/cyberstorm/views/team.py b/django/thunderstore/api/cyberstorm/views/team.py index e554f7aea..dbb54093d 100644 --- a/django/thunderstore/api/cyberstorm/views/team.py +++ b/django/thunderstore/api/cyberstorm/views/team.py @@ -19,7 +19,11 @@ CyberstormAutoSchemaMixin, conditional_swagger_auto_schema, ) -from thunderstore.repository.forms import AddTeamMemberForm, CreateTeamForm +from thunderstore.repository.forms import ( + AddTeamMemberForm, + CreateTeamForm, + RemoveTeamMemberForm, +) from thunderstore.repository.models.team import Team, TeamMember @@ -141,6 +145,57 @@ def post(self, request, team_name, format=None): raise ValidationError(form.errors) +class CyberstormRemoveTeamMemberRequestSerialiazer(serializers.Serializer): + username = serializers.CharField() + + +class CyberstormRemoveTeamMemberResponseSerialiazer(serializers.Serializer): + username = serializers.CharField() + team_name = serializers.CharField() + + +class RemoveTeamMemberAPIView(APIView): + permission_classes = [IsAuthenticated] + + @conditional_swagger_auto_schema( + request_body=CyberstormRemoveTeamMemberRequestSerialiazer, + responses={200: CyberstormRemoveTeamMemberResponseSerialiazer}, + operation_id="cyberstorm.team.members.remove", + tags=["cyberstorm"], + ) + def post(self, request: HttpRequest, team_name: str): + team = get_object_or_404(Team, name__iexact=team_name) + + serializer = CyberstormRemoveTeamMemberRequestSerialiazer(data=request.data) + serializer.is_valid(raise_exception=True) + + team_member = get_object_or_404( + TeamMember, + user__username__iexact=serializer.validated_data["username"], + team=team, + ) + + membership = team.get_membership_for_user(team_member.user) + + form = RemoveTeamMemberForm( + user=request.user, + data={"membership": membership}, + ) + + if form.is_valid(): + form.save() + return Response( + CyberstormRemoveTeamMemberResponseSerialiazer( + { + "username": serializer.validated_data["username"], + "team_name": team_name, + } + ).data + ) + else: + raise ValidationError(form.errors) + + class TeamServiceAccountListAPIView(CyberstormAutoSchemaMixin, TeamRestrictedAPIView): serializer_class = CyberstormServiceAccountSerializer filter_backends = [StrictOrderingFilter] diff --git a/django/thunderstore/api/urls.py b/django/thunderstore/api/urls.py index c740cdda9..6b98d33bf 100644 --- a/django/thunderstore/api/urls.py +++ b/django/thunderstore/api/urls.py @@ -14,6 +14,7 @@ PackageVersionChangelogAPIView, PackageVersionListAPIView, PackageVersionReadmeAPIView, + RemoveTeamMemberAPIView, TeamAPIView, TeamCreateAPIView, TeamMemberAddAPIView, @@ -128,4 +129,9 @@ UserDeleteAPIView.as_view(), name="cyberstorm.current-user.delete", ), + path( + "team//members/remove/", + RemoveTeamMemberAPIView.as_view(), + name="cyberstorm.team.members.remove", + ), ]