From 04b8400766950ab7f369339883cb8370a59ae6f7 Mon Sep 17 00:00:00 2001 From: Samuel Paccoud - DINUM Date: Tue, 13 May 2025 17:58:45 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(backend)=20add=20max=5Frole=20field?= =?UTF-8?q?=20to=20the=20document=20access=20API=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The frontend needs to know what to display on an access. The maximum role between the access role and the role equivalent to all accesses on the document's ancestors should be computed on the backend. --- src/backend/core/api/serializers.py | 19 +++++++- .../documents/test_api_document_accesses.py | 9 +++- .../test_api_document_accesses_create.py | 43 +++++++++++-------- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/backend/core/api/serializers.py b/src/backend/core/api/serializers.py index 561c48ff..83afc260 100644 --- a/src/backend/core/api/serializers.py +++ b/src/backend/core/api/serializers.py @@ -306,6 +306,7 @@ class DocumentAccessSerializer(serializers.ModelSerializer): team = serializers.CharField(required=False, allow_blank=True) abilities = serializers.SerializerMethodField(read_only=True) max_ancestors_role = serializers.SerializerMethodField(read_only=True) + max_role = serializers.SerializerMethodField(read_only=True) class Meta: model = models.DocumentAccess @@ -319,8 +320,15 @@ class DocumentAccessSerializer(serializers.ModelSerializer): "role", "abilities", "max_ancestors_role", + "max_role", + ] + read_only_fields = [ + "id", + "document", + "abilities", + "max_ancestors_role", + "max_role", ] - read_only_fields = ["id", "document", "abilities", "max_ancestors_role"] def get_abilities(self, instance) -> dict: """Return abilities of the logged-in user on the instance.""" @@ -333,6 +341,13 @@ class DocumentAccessSerializer(serializers.ModelSerializer): """Return max_ancestors_role if annotated; else None.""" return getattr(instance, "max_ancestors_role", None) + def get_max_role(self, instance): + """Return max_ancestors_role if annotated; else None.""" + return choices.RoleChoices.max( + getattr(instance, "max_ancestors_role", None), + instance.role, + ) + def update(self, instance, validated_data): """Make "user" field readonly but only on update.""" validated_data.pop("team", None) @@ -356,6 +371,7 @@ class DocumentAccessLightSerializer(DocumentAccessSerializer): "role", "abilities", "max_ancestors_role", + "max_role", ] read_only_fields = [ "id", @@ -364,6 +380,7 @@ class DocumentAccessLightSerializer(DocumentAccessSerializer): "role", "abilities", "max_ancestors_role", + "max_role", ] diff --git a/src/backend/core/tests/documents/test_api_document_accesses.py b/src/backend/core/tests/documents/test_api_document_accesses.py index fd301f54..eb6fa92b 100644 --- a/src/backend/core/tests/documents/test_api_document_accesses.py +++ b/src/backend/core/tests/documents/test_api_document_accesses.py @@ -153,6 +153,7 @@ def test_api_document_accesses_list_authenticated_related_non_privileged( "team": access.team, "role": access.role, "max_ancestors_role": None, + "max_role": access.role, "abilities": { "destroy": False, "partial_update": False, @@ -253,6 +254,7 @@ def test_api_document_accesses_list_authenticated_related_privileged( if access.user else None, "max_ancestors_role": None, + "max_role": access.role, "team": access.team, "role": access.role, "abilities": access.get_abilities(user), @@ -617,6 +619,7 @@ def test_api_document_accesses_retrieve_authenticated_related( "team": "", "role": access.role, "max_ancestors_role": None, + "max_role": access.role, "abilities": access.get_abilities(user), } @@ -775,10 +778,11 @@ def test_api_document_accesses_update_administrator_except_owner( access.refresh_from_db() updated_values = serializers.DocumentAccessSerializer(instance=access).data - if field == "role": + if field in ["role", "max_role"]: assert updated_values == { **old_values, "role": new_values["role"], + "max_role": new_values["role"], } else: assert updated_values == old_values @@ -951,10 +955,11 @@ def test_api_document_accesses_update_owner( access.refresh_from_db() updated_values = serializers.DocumentAccessSerializer(instance=access).data - if field == "role": + if field in ["role", "max_role"]: assert updated_values == { **old_values, "role": new_values["role"], + "max_role": new_values["role"], } else: assert updated_values == old_values diff --git a/src/backend/core/tests/documents/test_api_document_accesses_create.py b/src/backend/core/tests/documents/test_api_document_accesses_create.py index 8a32aa23..3c1e1b93 100644 --- a/src/backend/core/tests/documents/test_api_document_accesses_create.py +++ b/src/backend/core/tests/documents/test_api_document_accesses_create.py @@ -176,10 +176,11 @@ def test_api_document_accesses_create_authenticated_administrator_share_to_user( "path": new_document_access.document.path, }, "id": str(new_document_access.id), - "user": other_user, - "team": "", - "role": role, "max_ancestors_role": None, + "max_role": role, + "role": role, + "team": "", + "user": other_user, } assert len(mail.outbox) == 1 email = mail.outbox[0] @@ -266,10 +267,11 @@ def test_api_document_accesses_create_authenticated_administrator_share_to_team( "path": new_document_access.document.path, }, "id": str(new_document_access.id), - "user": None, - "team": "new-team", - "role": role, "max_ancestors_role": None, + "max_role": role, + "role": role, + "team": "new-team", + "user": None, } assert len(mail.outbox) == 0 @@ -323,17 +325,18 @@ def test_api_document_accesses_create_authenticated_owner_share_to_user( 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), "document": { "id": str(new_document_access.document_id), - "path": new_document_access.document.path, "depth": new_document_access.document.depth, + "path": new_document_access.document.path, }, "id": str(new_document_access.id), - "user": other_user, - "team": "", - "role": role, "max_ancestors_role": None, - "abilities": new_document_access.get_abilities(user), + "max_role": role, + "role": role, + "team": "", + "user": other_user, } assert len(mail.outbox) == 1 email = mail.outbox[0] @@ -396,17 +399,18 @@ def test_api_document_accesses_create_authenticated_owner_share_to_team( new_document_access = models.DocumentAccess.objects.filter(team="new-team").get() other_user = serializers.UserSerializer(instance=other_user).data assert response.json() == { + "abilities": new_document_access.get_abilities(user), "document": { "id": str(new_document_access.document_id), "path": new_document_access.document.path, "depth": new_document_access.document.depth, }, "id": str(new_document_access.id), - "user": None, - "team": "new-team", - "role": role, "max_ancestors_role": None, - "abilities": new_document_access.get_abilities(user), + "max_role": role, + "role": role, + "team": "new-team", + "user": None, } assert len(mail.outbox) == 0 @@ -457,17 +461,18 @@ def test_api_document_accesses_create_email_in_receivers_language(via, mock_user ).get() other_user_data = serializers.UserSerializer(instance=other_user).data assert response.json() == { + "abilities": new_document_access.get_abilities(user), "document": { "id": str(new_document_access.document_id), "path": new_document_access.document.path, "depth": new_document_access.document.depth, }, "id": str(new_document_access.id), - "user": other_user_data, - "team": "", - "role": role, "max_ancestors_role": None, - "abilities": new_document_access.get_abilities(user), + "max_role": role, + "role": role, + "team": "", + "user": other_user_data, } assert len(mail.outbox) == index + 1 email = mail.outbox[index]