diff --git a/src/backend/core/api/serializers.py b/src/backend/core/api/serializers.py index f97db290..a855caba 100644 --- a/src/backend/core/api/serializers.py +++ b/src/backend/core/api/serializers.py @@ -91,10 +91,19 @@ class BaseAccessSerializer(serializers.ModelSerializer): class DocumentAccessSerializer(BaseAccessSerializer): """Serialize document accesses.""" + user_id = serializers.PrimaryKeyRelatedField( + queryset=models.User.objects.all(), + write_only=True, + source="user", + required=False, + allow_null=True, + ) + user = UserSerializer(read_only=True) + class Meta: model = models.DocumentAccess resource_field_name = "document" - fields = ["id", "user", "team", "role", "abilities"] + fields = ["id", "user", "user_id", "team", "role", "abilities"] read_only_fields = ["id", "abilities"] @@ -126,6 +135,7 @@ class DocumentSerializer(BaseResourceSerializer): """Serialize documents.""" content = serializers.CharField(required=False) + accesses = DocumentAccessSerializer(many=True, read_only=True) class Meta: model = models.Document diff --git a/src/backend/core/models.py b/src/backend/core/models.py index c0434536..61c7c2df 100644 --- a/src/backend/core/models.py +++ b/src/backend/core/models.py @@ -289,6 +289,7 @@ class BaseAccess(BaseModel): return { "destroy": can_delete, "update": bool(set_role_to), + "partial_update": bool(set_role_to), "retrieve": bool(roles), "set_role_to": set_role_to, } diff --git a/src/backend/core/tests/documents/test_api_documents_retrieve.py b/src/backend/core/tests/documents/test_api_documents_retrieve.py index e3e81eff..c852174f 100644 --- a/src/backend/core/tests/documents/test_api_documents_retrieve.py +++ b/src/backend/core/tests/documents/test_api_documents_retrieve.py @@ -5,6 +5,7 @@ import pytest from rest_framework.test import APIClient from core import factories +from core.api import serializers pytestmark = pytest.mark.django_db @@ -112,30 +113,32 @@ def test_api_documents_retrieve_authenticated_related_direct(): document = factories.DocumentFactory() access1 = factories.UserDocumentAccessFactory(document=document, user=user) access2 = factories.UserDocumentAccessFactory(document=document) + access1_user = serializers.UserSerializer(instance=user).data + access2_user = serializers.UserSerializer(instance=access2.user).data response = client.get( f"/api/v1.0/documents/{document.id!s}/", ) assert response.status_code == 200 content = response.json() - assert sorted(content.pop("accesses"), key=lambda x: x["user"]) == sorted( + assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted( [ { "id": str(access1.id), - "user": str(user.id), + "user": access1_user, "team": "", "role": access1.role, "abilities": access1.get_abilities(user), }, { "id": str(access2.id), - "user": str(access2.user.id), + "user": access2_user, "team": "", "role": access2.role, "abilities": access2.get_abilities(user), }, ], - key=lambda x: x["user"], + key=lambda x: x["id"], ) assert response.json() == { "id": str(document.id), @@ -226,6 +229,7 @@ def test_api_documents_retrieve_authenticated_related_team_members( "retrieve": True, "set_role_to": [], "update": False, + "partial_update": False, } assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted( [ @@ -332,6 +336,7 @@ def test_api_documents_retrieve_authenticated_related_team_administrators( "retrieve": True, "set_role_to": ["administrator", "editor"], "update": True, + "partial_update": True, }, }, { @@ -344,6 +349,7 @@ def test_api_documents_retrieve_authenticated_related_team_administrators( "retrieve": True, "set_role_to": ["administrator", "reader"], "update": True, + "partial_update": True, }, }, { @@ -356,6 +362,7 @@ def test_api_documents_retrieve_authenticated_related_team_administrators( "retrieve": True, "set_role_to": ["editor", "reader"], "update": True, + "partial_update": True, }, }, { @@ -368,6 +375,7 @@ def test_api_documents_retrieve_authenticated_related_team_administrators( "retrieve": True, "set_role_to": [], "update": False, + "partial_update": False, }, }, { @@ -446,6 +454,7 @@ def test_api_documents_retrieve_authenticated_related_team_owners( "retrieve": True, "set_role_to": ["owner", "administrator", "editor"], "update": True, + "partial_update": True, }, }, { @@ -458,6 +467,7 @@ def test_api_documents_retrieve_authenticated_related_team_owners( "retrieve": True, "set_role_to": ["owner", "administrator", "reader"], "update": True, + "partial_update": True, }, }, { @@ -470,6 +480,7 @@ def test_api_documents_retrieve_authenticated_related_team_owners( "retrieve": True, "set_role_to": ["owner", "editor", "reader"], "update": True, + "partial_update": True, }, }, { @@ -485,6 +496,7 @@ def test_api_documents_retrieve_authenticated_related_team_owners( if other_access.role == "owner" else [], "update": other_access.role == "owner", + "partial_update": other_access.role == "owner", }, }, { diff --git a/src/backend/core/tests/templates/test_api_templates_retrieve.py b/src/backend/core/tests/templates/test_api_templates_retrieve.py index e83bdda0..06ca5f84 100644 --- a/src/backend/core/tests/templates/test_api_templates_retrieve.py +++ b/src/backend/core/tests/templates/test_api_templates_retrieve.py @@ -225,6 +225,7 @@ def test_api_templates_retrieve_authenticated_related_team_readers_or_editors( "retrieve": True, "set_role_to": [], "update": False, + "partial_update": False, } assert sorted(content.pop("accesses"), key=lambda x: x["id"]) == sorted( [ @@ -331,6 +332,7 @@ def test_api_templates_retrieve_authenticated_related_team_administrators( "retrieve": True, "set_role_to": ["administrator", "editor"], "update": True, + "partial_update": True, }, }, { @@ -343,6 +345,7 @@ def test_api_templates_retrieve_authenticated_related_team_administrators( "retrieve": True, "set_role_to": ["administrator", "reader"], "update": True, + "partial_update": True, }, }, { @@ -355,6 +358,7 @@ def test_api_templates_retrieve_authenticated_related_team_administrators( "retrieve": True, "set_role_to": ["editor", "reader"], "update": True, + "partial_update": True, }, }, { @@ -367,6 +371,7 @@ def test_api_templates_retrieve_authenticated_related_team_administrators( "retrieve": True, "set_role_to": [], "update": False, + "partial_update": False, }, }, { @@ -445,6 +450,7 @@ def test_api_templates_retrieve_authenticated_related_team_owners( "retrieve": True, "set_role_to": ["owner", "administrator", "editor"], "update": True, + "partial_update": True, }, }, { @@ -457,6 +463,7 @@ def test_api_templates_retrieve_authenticated_related_team_owners( "retrieve": True, "set_role_to": ["owner", "administrator", "reader"], "update": True, + "partial_update": True, }, }, { @@ -469,6 +476,7 @@ def test_api_templates_retrieve_authenticated_related_team_owners( "retrieve": True, "set_role_to": ["owner", "editor", "reader"], "update": True, + "partial_update": True, }, }, { @@ -484,6 +492,7 @@ def test_api_templates_retrieve_authenticated_related_team_owners( if other_access.role == "owner" else [], "update": other_access.role == "owner", + "partial_update": other_access.role == "owner", }, }, { diff --git a/src/backend/core/tests/test_api_document_accesses.py b/src/backend/core/tests/test_api_document_accesses.py index 7251529a..e632ec26 100644 --- a/src/backend/core/tests/test_api_document_accesses.py +++ b/src/backend/core/tests/test_api_document_accesses.py @@ -92,6 +92,9 @@ def test_api_document_accesses_list_authenticated_related(via, mock_user_get_tea f"/api/v1.0/documents/{document.id!s}/accesses/", ) + access2_user = serializers.UserSerializer(instance=access2.user).data + base_user = serializers.UserSerializer(instance=user).data + assert response.status_code == 200 content = response.json() assert len(content["results"]) == 3 @@ -99,7 +102,7 @@ def test_api_document_accesses_list_authenticated_related(via, mock_user_get_tea [ { "id": str(user_access.id), - "user": str(user.id) if via == "user" else None, + "user": base_user if via == "user" else None, "team": "lasuite" if via == "team" else "", "role": user_access.role, "abilities": user_access.get_abilities(user), @@ -113,7 +116,7 @@ def test_api_document_accesses_list_authenticated_related(via, mock_user_get_tea }, { "id": str(access2.id), - "user": str(access2.user.id), + "user": access2_user, "team": "", "role": access2.role, "abilities": access2.get_abilities(user), @@ -197,10 +200,12 @@ def test_api_document_accesses_retrieve_authenticated_related(via, mock_user_get f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/", ) + access_user = serializers.UserSerializer(instance=access.user).data + assert response.status_code == 200 assert response.json() == { "id": str(access.id), - "user": str(access.user.id), + "user": access_user, "team": "", "role": access.role, "abilities": access.get_abilities(user), @@ -340,7 +345,7 @@ def test_api_document_accesses_create_authenticated_administrator( response = client.post( f"/api/v1.0/documents/{document.id!s}/accesses/", { - "user": str(other_user.id), + "user_id": str(other_user.id), "role": role, }, format="json", @@ -349,12 +354,13 @@ def test_api_document_accesses_create_authenticated_administrator( assert response.status_code == 201 assert models.DocumentAccess.objects.filter(user=other_user).count() == 1 new_document_access = models.DocumentAccess.objects.filter(user=other_user).get() + other_user = serializers.UserSerializer(instance=other_user).data assert response.json() == { "abilities": new_document_access.get_abilities(user), "id": str(new_document_access.id), "team": "", "role": role, - "user": str(other_user.id), + "user": other_user, } @@ -384,7 +390,7 @@ def test_api_document_accesses_create_authenticated_owner(via, mock_user_get_tea response = client.post( f"/api/v1.0/documents/{document.id!s}/accesses/", { - "user": str(other_user.id), + "user_id": str(other_user.id), "role": role, }, format="json", @@ -393,9 +399,10 @@ def test_api_document_accesses_create_authenticated_owner(via, mock_user_get_tea assert response.status_code == 201 assert models.DocumentAccess.objects.filter(user=other_user).count() == 1 new_document_access = models.DocumentAccess.objects.filter(user=other_user).get() + other_user = serializers.UserSerializer(instance=other_user).data assert response.json() == { "id": str(new_document_access.id), - "user": str(other_user.id), + "user": other_user, "team": "", "role": role, "abilities": new_document_access.get_abilities(user), @@ -759,7 +766,13 @@ def test_api_document_accesses_update_owner_self(via, mock_user_get_teams): response = client.put( f"/api/v1.0/documents/{document.id!s}/accesses/{access.id!s}/", - data={**old_values, "role": new_role}, + data={ + **old_values, + "role": new_role, + "user_id": old_values.get("user", {}).get("id") + if old_values.get("user") is not None + else None, + }, format="json", ) diff --git a/src/backend/core/tests/test_models_document_accesses.py b/src/backend/core/tests/test_models_document_accesses.py index 8d5a3dec..4e94423b 100644 --- a/src/backend/core/tests/test_models_document_accesses.py +++ b/src/backend/core/tests/test_models_document_accesses.py @@ -87,6 +87,7 @@ def test_models_document_access_get_abilities_anonymous(): "destroy": False, "retrieve": False, "update": False, + "partial_update": False, "set_role_to": [], } @@ -100,6 +101,7 @@ def test_models_document_access_get_abilities_authenticated(): "destroy": False, "retrieve": False, "update": False, + "partial_update": False, "set_role_to": [], } @@ -119,6 +121,7 @@ def test_models_document_access_get_abilities_for_owner_of_self_allowed(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["administrator", "editor", "reader"], } @@ -133,6 +136,7 @@ def test_models_document_access_get_abilities_for_owner_of_self_last(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -149,6 +153,7 @@ def test_models_document_access_get_abilities_for_owner_of_owner(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["administrator", "editor", "reader"], } @@ -165,6 +170,7 @@ def test_models_document_access_get_abilities_for_owner_of_administrator(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["owner", "editor", "reader"], } @@ -181,6 +187,7 @@ def test_models_document_access_get_abilities_for_owner_of_editor(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["owner", "administrator", "reader"], } @@ -197,6 +204,7 @@ def test_models_document_access_get_abilities_for_owner_of_reader(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["owner", "administrator", "editor"], } @@ -216,6 +224,7 @@ def test_models_document_access_get_abilities_for_administrator_of_owner(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -232,6 +241,7 @@ def test_models_document_access_get_abilities_for_administrator_of_administrator "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["editor", "reader"], } @@ -248,6 +258,7 @@ def test_models_document_access_get_abilities_for_administrator_of_editor(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["administrator", "reader"], } @@ -264,6 +275,7 @@ def test_models_document_access_get_abilities_for_administrator_of_reader(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["administrator", "editor"], } @@ -283,6 +295,7 @@ def test_models_document_access_get_abilities_for_editor_of_owner(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -299,6 +312,7 @@ def test_models_document_access_get_abilities_for_editor_of_administrator(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -320,6 +334,7 @@ def test_models_document_access_get_abilities_for_editor_of_editor_user( "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -339,6 +354,7 @@ def test_models_document_access_get_abilities_for_reader_of_owner(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -355,6 +371,7 @@ def test_models_document_access_get_abilities_for_reader_of_administrator(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -376,6 +393,7 @@ def test_models_document_access_get_abilities_for_reader_of_reader_user( "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -395,5 +413,6 @@ def test_models_document_access_get_abilities_preset_role(django_assert_num_quer "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } diff --git a/src/backend/core/tests/test_models_template_accesses.py b/src/backend/core/tests/test_models_template_accesses.py index b15a19bb..e3ebd340 100644 --- a/src/backend/core/tests/test_models_template_accesses.py +++ b/src/backend/core/tests/test_models_template_accesses.py @@ -87,6 +87,7 @@ def test_models_template_access_get_abilities_anonymous(): "destroy": False, "retrieve": False, "update": False, + "partial_update": False, "set_role_to": [], } @@ -100,6 +101,7 @@ def test_models_template_access_get_abilities_authenticated(): "destroy": False, "retrieve": False, "update": False, + "partial_update": False, "set_role_to": [], } @@ -119,6 +121,7 @@ def test_models_template_access_get_abilities_for_owner_of_self_allowed(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["administrator", "editor", "reader"], } @@ -133,6 +136,7 @@ def test_models_template_access_get_abilities_for_owner_of_self_last(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -149,6 +153,7 @@ def test_models_template_access_get_abilities_for_owner_of_owner(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["administrator", "editor", "reader"], } @@ -165,6 +170,7 @@ def test_models_template_access_get_abilities_for_owner_of_administrator(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["owner", "editor", "reader"], } @@ -181,6 +187,7 @@ def test_models_template_access_get_abilities_for_owner_of_editor(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["owner", "administrator", "reader"], } @@ -197,6 +204,7 @@ def test_models_template_access_get_abilities_for_owner_of_reader(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["owner", "administrator", "editor"], } @@ -216,6 +224,7 @@ def test_models_template_access_get_abilities_for_administrator_of_owner(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -232,6 +241,7 @@ def test_models_template_access_get_abilities_for_administrator_of_administrator "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["editor", "reader"], } @@ -248,6 +258,7 @@ def test_models_template_access_get_abilities_for_administrator_of_editor(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["administrator", "reader"], } @@ -264,6 +275,7 @@ def test_models_template_access_get_abilities_for_administrator_of_reader(): "destroy": True, "retrieve": True, "update": True, + "partial_update": True, "set_role_to": ["administrator", "editor"], } @@ -283,6 +295,7 @@ def test_models_template_access_get_abilities_for_editor_of_owner(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -299,6 +312,7 @@ def test_models_template_access_get_abilities_for_editor_of_administrator(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -320,6 +334,7 @@ def test_models_template_access_get_abilities_for_editor_of_editor_user( "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -339,6 +354,7 @@ def test_models_template_access_get_abilities_for_reader_of_owner(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -355,6 +371,7 @@ def test_models_template_access_get_abilities_for_reader_of_administrator(): "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -376,6 +393,7 @@ def test_models_template_access_get_abilities_for_reader_of_reader_user( "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], } @@ -395,5 +413,6 @@ def test_models_template_access_get_abilities_preset_role(django_assert_num_quer "destroy": False, "retrieve": True, "update": False, + "partial_update": False, "set_role_to": [], }