From e75946847e62ebf40d7ee6cd7991cb87c484db15 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 22 Jun 2026 18:18:57 -0700 Subject: [PATCH] Fix: include last-modified in doc etag (#13044) --- src/documents/conditionals.py | 6 ++--- .../tests/test_version_conditionals.py | 25 ++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/documents/conditionals.py b/src/documents/conditionals.py index 82114e8d6..bb937cc93 100644 --- a/src/documents/conditionals.py +++ b/src/documents/conditionals.py @@ -70,13 +70,13 @@ def suggestions_last_modified(request, pk: int) -> datetime | None: def metadata_etag(request, pk: int) -> str | None: """ - Metadata is extracted from the original file, so use its checksum as the - ETag + Metadata responses include metadata as well as document fields, so include + the modification time with the checksum so metadata-only changes invalidate cache. """ doc = resolve_effective_document_by_pk(pk, request).document if doc is None: return None - return doc.checksum + return f"{doc.checksum}:{doc.modified.isoformat()}" def metadata_last_modified(request, pk: int) -> datetime | None: diff --git a/src/documents/tests/test_version_conditionals.py b/src/documents/tests/test_version_conditionals.py index 81218ed4c..d26d7b03d 100644 --- a/src/documents/tests/test_version_conditionals.py +++ b/src/documents/tests/test_version_conditionals.py @@ -1,7 +1,9 @@ +from datetime import timedelta from types import SimpleNamespace from unittest import mock from django.test import TestCase +from django.utils import timezone from documents.conditionals import metadata_etag from documents.conditionals import preview_etag @@ -29,10 +31,31 @@ class TestConditionals(DirectoriesMixin, TestCase): ) request = SimpleNamespace(query_params={}) - self.assertEqual(metadata_etag(request, root.id), latest.checksum) + self.assertEqual( + metadata_etag(request, root.id), + f"{latest.checksum}:{latest.modified.isoformat()}", + ) self.assertEqual(preview_etag(request, root.id), latest.archive_checksum) self.assertEqual(thumbnail_etag(request, root.id), latest.checksum) + def test_metadata_etag_changes_when_document_modified_changes(self) -> None: + doc = Document.objects.create( + title="doc", + checksum="same-checksum", + mime_type="application/pdf", + ) + request = SimpleNamespace(query_params={}) + + original_etag = metadata_etag(request, doc.id) + new_modified = timezone.now() + timedelta(seconds=5) + Document.objects.filter(id=doc.id).update(modified=new_modified) + + self.assertNotEqual(metadata_etag(request, doc.id), original_etag) + self.assertEqual( + metadata_etag(request, doc.id), + f"{doc.checksum}:{new_modified.isoformat()}", + ) + def test_resolve_effective_doc_returns_none_for_invalid_or_unrelated_version( self, ) -> None: