diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index 41fb03d9e..b09e6a14b 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -722,6 +722,9 @@ def add_to_index(sender, document, **kwargs) -> None: from documents import index index.add_or_update_document(document) + if document.root_document_id is not None and document.root_document is not None: + # keep in sync when a new version is consumed. + index.add_or_update_document(document.root_document) def run_workflows_added( diff --git a/src/documents/tests/test_api_document_versions.py b/src/documents/tests/test_api_document_versions.py index 4f70222e9..38d5f5810 100644 --- a/src/documents/tests/test_api_document_versions.py +++ b/src/documents/tests/test_api_document_versions.py @@ -133,7 +133,10 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): content="v2-content", ) - with mock.patch("documents.index.remove_document_from_index"): + with ( + mock.patch("documents.index.remove_document_from_index"), + mock.patch("documents.index.add_or_update_document"), + ): resp = self.client.delete(f"/api/documents/{root.id}/versions/{v2.id}/") self.assertEqual(resp.status_code, status.HTTP_200_OK) @@ -142,7 +145,10 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): root.refresh_from_db() self.assertEqual(root.content, "root-content") - with mock.patch("documents.index.remove_document_from_index"): + with ( + mock.patch("documents.index.remove_document_from_index"), + mock.patch("documents.index.add_or_update_document"), + ): resp = self.client.delete(f"/api/documents/{root.id}/versions/{v1.id}/") self.assertEqual(resp.status_code, status.HTTP_200_OK) @@ -165,7 +171,10 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): ) version_id = version.id - with mock.patch("documents.index.remove_document_from_index"): + with ( + mock.patch("documents.index.remove_document_from_index"), + mock.patch("documents.index.add_or_update_document"), + ): resp = self.client.delete( f"/api/documents/{root.id}/versions/{version_id}/", ) @@ -232,7 +241,10 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): root_document=root, ) - with mock.patch("documents.index.remove_document_from_index"): + with ( + mock.patch("documents.index.remove_document_from_index"), + mock.patch("documents.index.add_or_update_document"), + ): resp = self.client.delete( f"/api/documents/{version.id}/versions/{version.id}/", ) @@ -246,6 +258,32 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase): self.assertEqual(resp.status_code, status.HTTP_404_NOT_FOUND) + def test_delete_version_reindexes_root_document(self) -> None: + root = Document.objects.create( + title="root", + checksum="root", + mime_type="application/pdf", + ) + version = Document.objects.create( + title="v1", + checksum="v1", + mime_type="application/pdf", + root_document=root, + ) + + with ( + mock.patch("documents.index.remove_document_from_index") as remove_index, + mock.patch("documents.index.add_or_update_document") as add_or_update, + ): + resp = self.client.delete( + f"/api/documents/{root.id}/versions/{version.id}/", + ) + + self.assertEqual(resp.status_code, status.HTTP_200_OK) + remove_index.assert_called_once_with(version) + add_or_update.assert_called_once() + self.assertEqual(add_or_update.call_args[0][0].id, root.id) + def test_delete_version_returns_403_without_permission(self) -> None: owner = User.objects.create_user(username="owner") other = User.objects.create_user(username="other") diff --git a/src/documents/tests/test_task_signals.py b/src/documents/tests/test_task_signals.py index abe36087e..098cf8491 100644 --- a/src/documents/tests/test_task_signals.py +++ b/src/documents/tests/test_task_signals.py @@ -7,7 +7,9 @@ from django.test import TestCase from documents.data_models import ConsumableDocument from documents.data_models import DocumentMetadataOverrides from documents.data_models import DocumentSource +from documents.models import Document from documents.models import PaperlessTask +from documents.signals.handlers import add_to_index from documents.signals.handlers import before_task_publish_handler from documents.signals.handlers import task_failure_handler from documents.signals.handlers import task_postrun_handler @@ -198,3 +200,35 @@ class TestTaskSignalHandler(DirectoriesMixin, TestCase): task = PaperlessTask.objects.get() self.assertEqual(celery.states.FAILURE, task.status) + + def test_add_to_index_indexes_root_once_for_root_documents(self) -> None: + root = Document.objects.create( + title="root", + checksum="root", + mime_type="application/pdf", + ) + + with mock.patch("documents.index.add_or_update_document") as add: + add_to_index(sender=None, document=root) + + add.assert_called_once_with(root) + + def test_add_to_index_reindexes_root_for_version_documents(self) -> None: + root = Document.objects.create( + title="root", + checksum="root", + mime_type="application/pdf", + ) + version = Document.objects.create( + title="version", + checksum="version", + mime_type="application/pdf", + root_document=root, + ) + + with mock.patch("documents.index.add_or_update_document") as add: + add_to_index(sender=None, document=version) + + self.assertEqual(add.call_count, 2) + self.assertEqual(add.call_args_list[0].args[0].id, version.id) + self.assertEqual(add.call_args_list[1].args[0].id, root.id) diff --git a/src/documents/views.py b/src/documents/views.py index 90bd4a6a4..1d140d8d9 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -1716,6 +1716,7 @@ class DocumentViewSet( index.remove_document_from_index(version_doc) version_doc_id = version_doc.id version_doc.delete() + index.add_or_update_document(root_doc) if settings.AUDIT_LOG_ENABLED: actor = ( request.user if request.user and request.user.is_authenticated else None