From b3ae6e1a30cc64fe3491f6eda6e4c082e4562e91 Mon Sep 17 00:00:00 2001 From: Manuel Raynaud Date: Thu, 19 Mar 2026 09:36:55 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B(backend)=20duplicate=20a=20documen?= =?UTF-8?q?t=20as=20last-sibling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a document is duplicated, it is duplicated at the direct right of the duplicated document. Doing this force to move all the other documents at the right, if it is duplicated at the root this can impact a lot of documents, create lot of locks in the database. If the process is stop for any reason then the paths can be in an inconsistent paths in the Document table --- CHANGELOG.md | 1 + src/backend/core/api/viewsets.py | 2 +- .../tests/documents/test_api_documents_duplicate.py | 13 ++++++++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44719c24..440f9fd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to - ♿️(frontend) fix language dropdown ARIA for screen readers #2020 - ♿️(frontend) fix waffle aria-label spacing for new-window links #2030 - 🐛(backend) stop using add_sibling method to create sandbox document +- 🐛(backend) duplicate a document as last-sibling ## [v4.8.1] - 2026-03-17 diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index d402c91b..a1d13221 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -1361,7 +1361,7 @@ class DocumentViewSet( ) else: duplicated_document = document_to_duplicate.add_sibling( - "right", + "last-sibling", title=title, content=base64_yjs_content, attachments=attachments, diff --git a/src/backend/core/tests/documents/test_api_documents_duplicate.py b/src/backend/core/tests/documents/test_api_documents_duplicate.py index 40760539..15e92273 100644 --- a/src/backend/core/tests/documents/test_api_documents_duplicate.py +++ b/src/backend/core/tests/documents/test_api_documents_duplicate.py @@ -123,7 +123,7 @@ def test_api_documents_duplicate_success(index): image_refs[0][0] ] # Only the first image key assert duplicated_document.get_parent() == document.get_parent() - assert duplicated_document.path == document.get_next_sibling().path + assert duplicated_document.path == document.get_last_sibling().path # Check that accesses were not duplicated. # The user who did the duplicate is forced as owner @@ -180,6 +180,7 @@ def test_api_documents_duplicate_with_accesses_admin(role): client = APIClient() client.force_login(user) + documents_before = factories.DocumentFactory.create_batch(20) document = factories.DocumentFactory( users=[(user, role)], title="document with accesses", @@ -187,6 +188,12 @@ def test_api_documents_duplicate_with_accesses_admin(role): user_access = factories.UserDocumentAccessFactory(document=document) team_access = factories.TeamDocumentAccessFactory(document=document) + documents_after = factories.DocumentFactory.create_batch(20) + + all_documents = documents_before + [document] + documents_after + + paths = {document.pk: document.path for document in all_documents} + # Duplicate the document via the API endpoint requesting to duplicate accesses response = client.post( f"/api/v1.0/documents/{document.id!s}/duplicate/", @@ -212,6 +219,10 @@ def test_api_documents_duplicate_with_accesses_admin(role): assert duplicated_accesses.get(user=user_access.user).role == user_access.role assert duplicated_accesses.get(team=team_access.team).role == team_access.role + for document in all_documents: + document.refresh_from_db() + assert document.path == paths[document.id] + @pytest.mark.parametrize("role", ["editor", "reader"]) def test_api_documents_duplicate_with_accesses_non_admin(role):