From 8322e91531bd1e7717d564b9abee5edae62a2e96 Mon Sep 17 00:00:00 2001
From: Erwan Rouchet <rouchet@teklia.com>
Date: Tue, 15 Feb 2022 15:43:58 +0100
Subject: [PATCH] Add a delete_children query parameter on DestroyElement

---
 arkindex/documents/api/elements.py            | 22 ++++++++++--
 .../documents/tests/test_destroy_elements.py  | 36 +++++++++++++++++--
 2 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/arkindex/documents/api/elements.py b/arkindex/documents/api/elements.py
index b93ab14ab7..ea5dd24839 100644
--- a/arkindex/documents/api/elements.py
+++ b/arkindex/documents/api/elements.py
@@ -873,8 +873,20 @@ class ElementChildren(ElementsListBase):
     get=extend_schema(description="Retrieve a single element's information and metadata"),
     patch=extend_schema(description='Rename an element'),
     put=extend_schema(description="Edit an element's attributes. Requires a write access on the corpus."),
-    delete=extend_schema(description='Delete an element. Requires either an admin access on the corpus, '
-                                     'or a write access and to be the creator of this element.'),
+    delete=extend_schema(
+        description='Delete an element. Requires either an admin access on the corpus, '
+                    'or a write access and to be the creator of this element.',
+        parameters=[
+            OpenApiParameter(
+                'delete_children',
+                description='Delete all child elements of this element recursively. '
+                            'By default, this only removes this element as a parent of the child elements, '
+                            'without deleting any child element.',
+                type=bool,
+                required=False,
+            ),
+        ],
+    ),
 )
 class ElementRetrieve(ACLMixin, RetrieveUpdateDestroyAPIView):
     serializer_class = ElementSerializer
@@ -923,8 +935,12 @@ class ElementRetrieve(ACLMixin, RetrieveUpdateDestroyAPIView):
 
     def delete(self, request, *args, **kwargs):
         self.check_object_permissions(self.request, self.get_object())
+
         queryset = Element.objects.filter(id=self.kwargs['pk'])
-        element_trash(queryset, user_id=self.request.user.id)
+        delete_children = self.request.query_params.get('delete_children', 'false').lower() not in ('false', '0')
+
+        element_trash(queryset, user_id=self.request.user.id, delete_children=delete_children)
+
         return Response(status=status.HTTP_204_NO_CONTENT)
 
 
diff --git a/arkindex/documents/tests/test_destroy_elements.py b/arkindex/documents/tests/test_destroy_elements.py
index 34be212608..06823b9c1a 100644
--- a/arkindex/documents/tests/test_destroy_elements.py
+++ b/arkindex/documents/tests/test_destroy_elements.py
@@ -58,11 +58,41 @@ class TestDestroyElements(FixtureAPITestCase):
         self.assertEqual(len(args), 0)
         self.assertCountEqual(list(kwargs.pop('queryset')), list(self.corpus.elements.filter(id=castle_story.id)))
         self.assertDictEqual(kwargs, {
-            'delete_children': True,
+            'delete_children': False,
             'user_id': self.user.id,
             'description': 'Element deletion',
         })
 
+    @patch('arkindex.project.triggers.documents_tasks.element_trash.delay')
+    def test_element_destroy_delete_children(self, delay_mock):
+        self.client.force_login(self.user)
+        castle_story = self.corpus.elements.create(
+            type=self.volume_type,
+            name='Castle story'
+        )
+        self.assertTrue(self.corpus.elements.filter(id=castle_story.id).exists())
+
+        for delete_children in [True, False]:
+            with self.subTest(delete_children=delete_children):
+                delay_mock.reset_mock()
+
+                with self.assertNumQueries(7):
+                    response = self.client.delete(
+                        reverse('api:element-retrieve', kwargs={'pk': str(castle_story.id)})
+                        + f'?delete_children={delete_children}',
+                    )
+                self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
+
+                self.assertEqual(delay_mock.call_count, 1)
+                args, kwargs = delay_mock.call_args
+                self.assertEqual(len(args), 0)
+                self.assertCountEqual(list(kwargs.pop('queryset')), list(self.corpus.elements.filter(id=castle_story.id)))
+                self.assertDictEqual(kwargs, {
+                    'delete_children': delete_children,
+                    'user_id': self.user.id,
+                    'description': 'Element deletion',
+                })
+
     @patch('arkindex.project.triggers.documents_tasks.element_trash.delay')
     def test_element_destroy_creator(self, delay_mock):
         """
@@ -84,7 +114,7 @@ class TestDestroyElements(FixtureAPITestCase):
         self.assertEqual(len(args), 0)
         self.assertCountEqual(list(kwargs.pop('queryset')), list(self.private_corpus.elements.filter(id=castle_story.id)))
         self.assertDictEqual(kwargs, {
-            'delete_children': True,
+            'delete_children': False,
             'user_id': self.user.id,
             'description': 'Element deletion',
         })
@@ -123,7 +153,7 @@ class TestDestroyElements(FixtureAPITestCase):
         self.assertEqual(len(args), 0)
         self.assertCountEqual(list(kwargs.pop('queryset')), list(self.corpus.elements.filter(id=self.vol.id)))
         self.assertDictEqual(kwargs, {
-            'delete_children': True,
+            'delete_children': False,
             'user_id': self.user.id,
             'description': 'Element deletion',
         })
-- 
GitLab