Repository: python-discord/site

Issue:
Support deleting infractions
The bot tries to delete infractions that fail to apply on the Discord side of things. This is cause it sends the infraction to our API before applying it on Discord. However, our API doesn't actually support the delete method so it returns a 405.


Discussion:

Resolving patch:
diff --git a/pydis_site/apps/api/viewsets/bot/infraction.py b/pydis_site/apps/api/viewsets/bot/infraction.py
--- a/pydis_site/apps/api/viewsets/bot/infraction.py
+++ b/pydis_site/apps/api/viewsets/bot/infraction.py
@@ -5,6 +5,7 @@
 from rest_framework.filters import OrderingFilter, SearchFilter
 from rest_framework.mixins import (
     CreateModelMixin,
+    DestroyModelMixin,
     ListModelMixin,
     RetrieveModelMixin
 )
@@ -18,7 +19,13 @@
 )
 
 
-class InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, GenericViewSet):
+class InfractionViewSet(
+    CreateModelMixin,
+    RetrieveModelMixin,
+    ListModelMixin,
+    GenericViewSet,
+    DestroyModelMixin
+):
     """
     View providing CRUD operations on infractions for Discord users.
 
@@ -108,6 +115,13 @@ class InfractionViewSet(CreateModelMixin, RetrieveModelMixin, ListModelMixin, Ge
     - 400: if a field in the request body is invalid or disallowed
     - 404: if an infraction with the given `id` could not be found
 
+    ### DELETE /bot/infractions/<id:int>
+    Delete the infraction with the given `id`.
+
+    #### Status codes
+    - 204: returned on success
+    - 404: if a infraction with the given `id` does not exist
+
     ### Expanded routes
     All routes support expansion of `user` and `actor` in responses. To use an expanded route,
     append `/expanded` to the end of the route e.g. `GET /bot/infractions/expanded`.


Test patch:
diff --git a/pydis_site/apps/api/tests/test_infractions.py b/pydis_site/apps/api/tests/test_infractions.py
--- a/pydis_site/apps/api/tests/test_infractions.py
+++ b/pydis_site/apps/api/tests/test_infractions.py
@@ -512,6 +512,36 @@ def test_integrity_error_if_missing_active_field(self):
             )
 
 
+class InfractionDeletionTests(APISubdomainTestCase):
+    @classmethod
+    def setUpTestData(cls):
+        cls.user = User.objects.create(
+            id=9876,
+            name='Unknown user',
+            discriminator=9876,
+        )
+
+        cls.warning = Infraction.objects.create(
+            user_id=cls.user.id,
+            actor_id=cls.user.id,
+            type='warning',
+            active=False
+        )
+
+    def test_delete_unknown_infraction_returns_404(self):
+        url = reverse('bot:infraction-detail', args=('something',), host='api')
+        response = self.client.delete(url)
+
+        self.assertEqual(response.status_code, 404)
+
+    def test_delete_known_infraction_returns_204(self):
+        url = reverse('bot:infraction-detail', args=(self.warning.id,), host='api')
+        response = self.client.delete(url)
+
+        self.assertEqual(response.status_code, 204)
+        self.assertRaises(Infraction.DoesNotExist, Infraction.objects.get, id=self.warning.id)
+
+
 class ExpandedTests(APISubdomainTestCase):
     @classmethod
     def setUpTestData(cls):