diff --git a/arkindex/documents/api/elements.py b/arkindex/documents/api/elements.py index 37c61d797499767d1a512732c148a5614e1e5ed5..7024a304eee36fd9216191c439caf98623c24761 100644 --- a/arkindex/documents/api/elements.py +++ b/arkindex/documents/api/elements.py @@ -1436,7 +1436,9 @@ class AllowedMetaDataEdit(CorpusACLMixin, RetrieveUpdateDestroyAPIView): @extend_schema(tags=['elements']) class ElementTypeCreate(CreateAPIView): """ - Create a new element type for a specific corpus + Create a new element type on a corpus. + + This requires admin access to the corpus. """ serializer_class = ElementTypeSerializer permission_classes = (IsVerifiedOrReadOnly, ) @@ -1448,13 +1450,15 @@ class ElementTypeCreate(CreateAPIView): ) class ElementTypeUpdate(UpdateAPIView): """ - Update an existing element type + Update an existing element type. + + This requires admin access to the element type's corpus. """ serializer_class = ElementTypeLightSerializer permission_classes = (IsVerifiedOrReadOnly, ) def get_queryset(self): - return ElementType.objects.filter(corpus__in=Corpus.objects.readable(self.request.user)) + return ElementType.objects.filter(corpus__in=Corpus.objects.admin(self.request.user)) @extend_schema_view( diff --git a/arkindex/documents/serializers/elements.py b/arkindex/documents/serializers/elements.py index a295d863725c7c33bdc6e7457f1a659c0f4445dc..569d2b0e055972008850eb7e7f7943bb1d039ed8 100644 --- a/arkindex/documents/serializers/elements.py +++ b/arkindex/documents/serializers/elements.py @@ -802,7 +802,7 @@ class ElementTypeSerializer(ElementTypeLightSerializer): if not self.context.get('request'): # Keep original fields so OpenAPI can build its schemas return - self.fields['corpus'].queryset = Corpus.objects.writable(self.context['request'].user) + self.fields['corpus'].queryset = Corpus.objects.admin(self.context['request'].user) class ElementBulkItemSerializer(serializers.Serializer): diff --git a/arkindex/documents/tests/test_element_type.py b/arkindex/documents/tests/test_element_type.py index 6caf058aa8b832259ea2bbe12d8f28ef8186c5f9..8b3e7e1105dd2249b9d3d9472fd17d93d739be34 100644 --- a/arkindex/documents/tests/test_element_type.py +++ b/arkindex/documents/tests/test_element_type.py @@ -3,7 +3,7 @@ from rest_framework import status from arkindex.documents.models import Corpus, ElementType from arkindex.project.tests import FixtureAPITestCase -from arkindex.users.models import User +from arkindex.users.models import Role, User class TestElementType(FixtureAPITestCase): @@ -15,7 +15,7 @@ class TestElementType(FixtureAPITestCase): cls.user = User.objects.get(email="user@user.fr") cls.private_corpus = Corpus.objects.create(name='private') - def test_create_element_type_requires_login(self): + def test_create_requires_login(self): response = self.client.post(reverse('api:element-type-create'), { 'corpus': self.corpus.id, 'slug': 'New_element_type', @@ -23,7 +23,7 @@ class TestElementType(FixtureAPITestCase): }, format='json') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - def test_create_element_type_no_access(self): + def test_create_no_access(self): self.client.force_login(self.user) response = self.client.post(reverse('api:element-type-create'), { 'corpus': self.private_corpus.id, @@ -32,7 +32,17 @@ class TestElementType(FixtureAPITestCase): }, format='json') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - def test_create_element_type_wrong_slug(self): + def test_create_requires_admin(self): + self.private_corpus.memberships.create(user=self.user, level=Role.Contributor.value) + self.client.force_login(self.user) + response = self.client.post(reverse('api:element-type-create'), { + 'corpus': self.private_corpus.id, + 'slug': 'New_element_type', + 'display_name': 'New element type', + }, format='json') + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_create_wrong_slug(self): self.client.force_login(self.superuser) response = self.client.post(reverse('api:element-type-create'), { 'corpus': self.corpus.id, @@ -41,7 +51,7 @@ class TestElementType(FixtureAPITestCase): }, format='json') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - def test_create_element_type(self): + def test_create(self): self.client.force_login(self.superuser) response = self.client.post(reverse('api:element-type-create'), { 'corpus': self.corpus.id, @@ -63,15 +73,25 @@ class TestElementType(FixtureAPITestCase): } ) - def test_update_element_type_requires_login(self): + def test_partial_update_requires_login(self): response = self.client.post(reverse('api:element-type', kwargs={'pk': self.element_type.id}), { 'slug': 'New_element_type', 'display_name': 'New element type', }, format='json') self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - def test_update_element_type_no_access(self): + def test_partial_update_no_access(self): + private_element_type = ElementType.objects.create(corpus=self.private_corpus, slug='element', display_name='Element') + self.client.force_login(self.user) + response = self.client.patch(reverse('api:element-type', kwargs={'pk': private_element_type.id}), { + 'slug': 'New_element_type', + 'display_name': 'New element type', + }, format='json') + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_partial_update_requires_admin(self): private_element_type = ElementType.objects.create(corpus=self.private_corpus, slug='element', display_name='Element') + self.private_corpus.memberships.create(user=self.user, level=Role.Contributor.value) self.client.force_login(self.user) response = self.client.patch(reverse('api:element-type', kwargs={'pk': private_element_type.id}), { 'slug': 'New_element_type', @@ -79,7 +99,7 @@ class TestElementType(FixtureAPITestCase): }, format='json') self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - def test_update_element_type_wrong_slug(self): + def test_partial_update_wrong_slug(self): element_type = ElementType.objects.create(corpus=self.corpus, slug='new_type', display_name='Element') self.client.force_login(self.superuser) response = self.client.patch(reverse('api:element-type', kwargs={'pk': self.element_type.id}), { @@ -88,9 +108,83 @@ class TestElementType(FixtureAPITestCase): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertDictEqual(response.json(), {'slug': ['Slug must be unique']}) - def test_update_element_type(self): + def test_partial_update(self): self.client.force_login(self.superuser) - response = self.client.patch(reverse('api:element-type', kwargs={'pk': self.element_type.id}), { + cases = [ + {'slug': 'new_element_type'}, + {'display_name': 'New element type'}, + {'folder': True}, + { + 'slug': 'new_element_type', + 'display_name': 'New element type', + 'folder': False, + }, + { + 'slug': 'new_element_type', + 'display_name': 'New element type', + } + ] + + for update_data in cases: + with self.subTest(**update_data): + response = self.client.patch( + reverse('api:element-type', kwargs={'pk': self.element_type.id}), + update_data, + format='json' + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + self.element_type.refresh_from_db() + self.assertIn('id', data) + self.assertDictEqual( + data, + { + 'id': str(self.element_type.id), + 'slug': self.element_type.slug, + 'display_name': self.element_type.display_name, + 'folder': self.element_type.folder + } + ) + + def test_update_requires_login(self): + response = self.client.post(reverse('api:element-type', kwargs={'pk': self.element_type.id}), { + 'slug': 'New_element_type', + 'display_name': 'New element type', + }, format='json') + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_update_no_access(self): + private_element_type = ElementType.objects.create(corpus=self.private_corpus, slug='element', display_name='Element') + self.client.force_login(self.user) + response = self.client.put(reverse('api:element-type', kwargs={'pk': private_element_type.id}), { + 'slug': 'New_element_type', + 'display_name': 'New element type', + }, format='json') + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_update_requires_admin(self): + private_element_type = ElementType.objects.create(corpus=self.private_corpus, slug='element', display_name='Element') + self.private_corpus.memberships.create(user=self.user, level=Role.Contributor.value) + self.client.force_login(self.user) + response = self.client.put(reverse('api:element-type', kwargs={'pk': private_element_type.id}), { + 'slug': 'New_element_type', + 'display_name': 'New element type', + }, format='json') + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_update_wrong_slug(self): + element_type = ElementType.objects.create(corpus=self.corpus, slug='new_type', display_name='Element') + self.client.force_login(self.superuser) + response = self.client.put(reverse('api:element-type', kwargs={'pk': self.element_type.id}), { + 'slug': element_type.slug, + 'display_name': 'New element type', + }, format='json') + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertDictEqual(response.json(), {'slug': ['Slug must be unique']}) + + def test_update(self): + self.client.force_login(self.superuser) + response = self.client.put(reverse('api:element-type', kwargs={'pk': self.element_type.id}), { 'slug': 'New_element_type', 'display_name': 'New element type', }, format='json')