From 0f596bb15e5953c535d07b9af1c82e308cf182c9 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 23 Apr 2026 21:55:55 +0200 Subject: [PATCH] feat(document): getThumbnailUrl appends URL-encoded timestamp as cache-buster Matches the shape the frontend previously built via encodeURIComponent(thumbnailGeneratedAt), so the backend is now the single source of truth for the thumbnail URL convention (#309). Co-Authored-By: Claude Opus 4.7 --- .../familienarchiv/model/Document.java | 6 ++++- .../familienarchiv/model/DocumentTest.java | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/org/raddatz/familienarchiv/model/Document.java b/backend/src/main/java/org/raddatz/familienarchiv/model/Document.java index dcdd1a2e..de216c89 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/model/Document.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/model/Document.java @@ -8,6 +8,8 @@ import org.hibernate.annotations.UpdateTimestamp; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import io.swagger.v3.oas.annotations.media.Schema; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.HashSet; @@ -127,6 +129,8 @@ public class Document { public String getThumbnailUrl() { if (thumbnailKey == null) return null; - return "/api/documents/" + id + "/thumbnail"; + String base = "/api/documents/" + id + "/thumbnail"; + if (thumbnailGeneratedAt == null) return base; + return base + "?v=" + URLEncoder.encode(thumbnailGeneratedAt.toString(), StandardCharsets.UTF_8); } } diff --git a/backend/src/test/java/org/raddatz/familienarchiv/model/DocumentTest.java b/backend/src/test/java/org/raddatz/familienarchiv/model/DocumentTest.java index 29071779..8b1f9c59 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/model/DocumentTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/model/DocumentTest.java @@ -2,6 +2,7 @@ package org.raddatz.familienarchiv.model; import org.junit.jupiter.api.Test; +import java.time.LocalDateTime; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @@ -36,4 +37,25 @@ class DocumentTest { assertThat(doc.getThumbnailUrl()) .isEqualTo("/api/documents/" + id + "/thumbnail"); } + + @Test + void getThumbnailUrl_includesCacheBuster_whenBothKeyAndGeneratedAtPresent() { + UUID id = UUID.fromString("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"); + LocalDateTime generatedAt = LocalDateTime.of(2026, 4, 23, 14, 30, 45); + Document doc = Document.builder() + .id(id) + .title("Brief") + .originalFilename("brief.pdf") + .status(DocumentStatus.UPLOADED) + .thumbnailKey("thumbnails/" + id + ".jpg") + .thumbnailGeneratedAt(generatedAt) + .build(); + + // frontend equivalent: `?v=${encodeURIComponent(doc.thumbnailGeneratedAt)}` + // where thumbnailGeneratedAt is the ISO-8601 string Jackson serialises. + // LocalDateTime.toString() produces "2026-04-23T14:30:45"; encodeURIComponent + // turns ":" into "%3A" but leaves "T" and digits alone. + String expected = "/api/documents/" + id + "/thumbnail?v=2026-04-23T14%3A30%3A45"; + assertThat(doc.getThumbnailUrl()).isEqualTo(expected); + } }