diff --git a/alexandria/core/tests/__snapshots__/test_viewsets.ambr b/alexandria/core/tests/__snapshots__/test_viewsets.ambr index 6b00be96..30dd98a9 100644 --- a/alexandria/core/tests/__snapshots__/test_viewsets.ambr +++ b/alexandria/core/tests/__snapshots__/test_viewsets.ambr @@ -2226,6 +2226,8 @@ 'SELECT "alexandria_core_mark"."created_at", "alexandria_core_mark"."created_by_user", "alexandria_core_mark"."created_by_group", "alexandria_core_mark"."modified_at", "alexandria_core_mark"."modified_by_user", "alexandria_core_mark"."modified_by_group", "alexandria_core_mark"."metainfo", "alexandria_core_mark"."slug", "alexandria_core_mark"."name", "alexandria_core_mark"."description" FROM "alexandria_core_mark" WHERE "alexandria_core_mark"."slug" = \'father-should-keep\' LIMIT 21', 'SELECT "alexandria_core_tag"."created_at", "alexandria_core_tag"."created_by_user", "alexandria_core_tag"."created_by_group", "alexandria_core_tag"."modified_at", "alexandria_core_tag"."modified_by_user", "alexandria_core_tag"."modified_by_group", "alexandria_core_tag"."metainfo", "alexandria_core_tag"."id", "alexandria_core_tag"."name", "alexandria_core_tag"."description", "alexandria_core_tag"."tag_synonym_group_id" FROM "alexandria_core_tag" INNER JOIN "alexandria_core_document_tags" ON ("alexandria_core_tag"."id" = "alexandria_core_document_tags"."tag_id") WHERE ("alexandria_core_document_tags"."document_id" = \'9dd4e461268c8034f5c8564e155c67a6\'::uuid AND NOT ("alexandria_core_tag"."id" IN (SELECT U0."id" FROM "alexandria_core_tag" U0 INNER JOIN "alexandria_core_document_tags" U1 ON (U0."id" = U1."tag_id") WHERE U1."document_id" = \'9dd4e461268c8034f5c8564e155c67a6\'::uuid)))', 'SELECT "alexandria_core_mark"."created_at", "alexandria_core_mark"."created_by_user", "alexandria_core_mark"."created_by_group", "alexandria_core_mark"."modified_at", "alexandria_core_mark"."modified_by_user", "alexandria_core_mark"."modified_by_group", "alexandria_core_mark"."metainfo", "alexandria_core_mark"."slug", "alexandria_core_mark"."name", "alexandria_core_mark"."description" FROM "alexandria_core_mark" INNER JOIN "alexandria_core_document_marks" ON ("alexandria_core_mark"."slug" = "alexandria_core_document_marks"."mark_id") WHERE ("alexandria_core_document_marks"."document_id" = \'9dd4e461268c8034f5c8564e155c67a6\'::uuid AND NOT ("alexandria_core_mark"."slug" IN (SELECT U0."slug" FROM "alexandria_core_mark" U0 INNER JOIN "alexandria_core_document_marks" U1 ON (U0."slug" = U1."mark_id") WHERE U1."document_id" = \'9dd4e461268c8034f5c8564e155c67a6\'::uuid)))', + 'SELECT "alexandria_core_document"."created_at", "alexandria_core_document"."created_by_user", "alexandria_core_document"."created_by_group", "alexandria_core_document"."modified_at", "alexandria_core_document"."modified_by_user", "alexandria_core_document"."modified_by_group", "alexandria_core_document"."metainfo", "alexandria_core_document"."id", "alexandria_core_document"."title", "alexandria_core_document"."description", "alexandria_core_document"."category_id", "alexandria_core_document"."date" FROM "alexandria_core_document" WHERE "alexandria_core_document"."id" = \'9dd4e461268c8034f5c8564e155c67a6\'::uuid LIMIT 21', + 'SELECT "alexandria_core_category"."created_at", "alexandria_core_category"."created_by_user", "alexandria_core_category"."created_by_group", "alexandria_core_category"."modified_at", "alexandria_core_category"."modified_by_user", "alexandria_core_category"."modified_by_group", "alexandria_core_category"."metainfo", "alexandria_core_category"."slug", "alexandria_core_category"."name", "alexandria_core_category"."description", "alexandria_core_category"."allowed_mime_types", "alexandria_core_category"."color", "alexandria_core_category"."parent_id" FROM "alexandria_core_category" WHERE "alexandria_core_category"."slug" = \'note-act-source\' LIMIT 21', ''' UPDATE "alexandria_core_document" SET "created_at" = '2017-05-21 00:00:00+00:00'::timestamptz, "created_by_user" = 'admin', "created_by_group" = 'admin', "modified_at" = '2017-05-21 00:00:00+00:00'::timestamptz, "modified_by_user" = 'admin', "modified_by_group" = 'admin', "metainfo" = '{}'::jsonb, "title" = 'Michael Edwards', "description" = 'Open else look tree arm responsibility week. Environmental statement bag someone them style. Public these health team change. Tax final upon stay sing middle suggest.', "category_id" = 'note-act-source', "date" = '1999-11-26'::date WHERE "alexandria_core_document"."id" = '9dd4e461268c8034f5c8564e155c67a6'::uuid @@ -2243,7 +2245,7 @@ 'BEGIN', 'COMMIT', ]), - 'query_count': 19, + 'query_count': 21, 'request': dict({ 'CONTENT_LENGTH': '813', 'CONTENT_TYPE': 'application/vnd.api+json', diff --git a/alexandria/core/tests/test_validation.py b/alexandria/core/tests/test_validation.py index e6f4cd4f..677dfed9 100644 --- a/alexandria/core/tests/test_validation.py +++ b/alexandria/core/tests/test_validation.py @@ -4,14 +4,14 @@ from generic_permissions.config import ValidatorsConfig from generic_permissions.validation import validator_for -from alexandria.core.models import Document +from alexandria.core.models import Tag -def test_custom_validation(db, reset_config_classes, document, file, admin_client): +def test_custom_validation(db, reset_config_classes, tag, file, admin_client): call_counter = Counter() class TestValidator: - @validator_for(Document) + @validator_for(Tag) def validate(self, data, context): data["created_by_group"] = "foobar" call_counter["validate"] += 1 @@ -21,17 +21,12 @@ def validate(self, data, context): ValidatorsConfig.register_handler_class(TestValidator) - url = reverse("document-detail", args=[document.pk]) + url = reverse("tag-detail", args=[tag.pk]) # first, ensure validator is called for Document admin_client.patch(url, json={}) assert call_counter["validate"] == 1 # See if the validation had some effect - document.refresh_from_db() - assert document.created_by_group == "foobar" - - # second, ensure validator is not called for File - url = reverse("file-detail", args=[file.pk]) - admin_client.patch(url, json={}) - assert call_counter["validate"] == 1 + tag.refresh_from_db() + assert tag.created_by_group == "foobar" diff --git a/alexandria/core/tests/test_views.py b/alexandria/core/tests/test_views.py index 8b7b8419..4df5de2f 100644 --- a/alexandria/core/tests/test_views.py +++ b/alexandria/core/tests/test_views.py @@ -368,6 +368,37 @@ def test_document_delete_some_tags(admin_client, tag_factory, document_factory): ) +def test_move_document_to_new_category( + admin_client, category_factory, file_factory, document_factory +): + category_not_allowed = category_factory.create(allowed_mime_types=["plain/text"]) + category_allowed = category_factory.create(allowed_mime_types=["image/jpeg"]) + document = document_factory() + file_factory.create(document=document, name="Image.jpeg", mime_type="image/jpeg") + + url = reverse("document-detail", args=[document.pk]) + + data = { + "data": { + "type": "documents", + "id": document.pk, + "relationships": { + "category": { + "data": {"id": category_not_allowed.pk, "type": "categories"} + } + }, + } + } + + response = admin_client.patch(url, data) + + assert response.status_code == HTTP_400_BAD_REQUEST + + data["data"]["relationships"]["category"]["data"]["id"] = category_allowed.pk + response = admin_client.patch(url, data) + assert response.status_code == HTTP_200_OK + + @pytest.mark.parametrize( "presigned, expected_status", [(True, HTTP_200_OK), (False, HTTP_403_FORBIDDEN)], diff --git a/alexandria/core/validations.py b/alexandria/core/validations.py index 9af7f9e2..6d0b6960 100644 --- a/alexandria/core/validations.py +++ b/alexandria/core/validations.py @@ -8,7 +8,7 @@ from generic_permissions.validation import validator_for from rest_framework.exceptions import ValidationError -from alexandria.core.models import File +from alexandria.core.models import Document, File log = logging.getLogger(__name__) @@ -101,3 +101,15 @@ def validate_file(self, data, context): data["mime_type"] = content_type_header return data + + @validator_for(Document) + def validate_document(self, data, context): + if context["request"].method == "PATCH" and "category" in data: + category = data["category"] + document = context["view"].get_object() + if not document.category.pk == category.pk: + # Validate if a document is moved to another category that the + # mime type of the file is still compatible with the category's + # mime types. + validate_mime_type(document.get_latest_original().mime_type, category) + return data