diff --git a/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java b/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java index 3e3f1b4b..a69a77e0 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/document/DocumentService.java @@ -29,7 +29,6 @@ import org.raddatz.familienarchiv.ocr.TrainingLabel; import org.raddatz.familienarchiv.person.Person; import org.raddatz.familienarchiv.tag.Tag; import org.raddatz.familienarchiv.document.DocumentRepository; -import org.springframework.context.annotation.Lazy; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -77,10 +76,6 @@ public class DocumentService { private final AuditService auditService; private final TranscriptionBlockQueryService transcriptionBlockQueryService; private final AuditLogQueryService auditLogQueryService; - // @Lazy breaks the DocumentService ↔ ThumbnailAsyncRunner cycle: the runner - // now reaches Document data through DocumentService (per the layering rule), - // and Spring needs a proxy here to defer the back-edge until both beans exist. - @Lazy private final ThumbnailAsyncRunner thumbnailAsyncRunner; public record StoreResult(Document document, boolean isNew) {} diff --git a/backend/src/main/java/org/raddatz/familienarchiv/document/ThumbnailAsyncRunner.java b/backend/src/main/java/org/raddatz/familienarchiv/document/ThumbnailAsyncRunner.java index ed239ade..3b82aff1 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/document/ThumbnailAsyncRunner.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/document/ThumbnailAsyncRunner.java @@ -28,7 +28,7 @@ import java.util.concurrent.TimeoutException; @Slf4j public class ThumbnailAsyncRunner { - private final DocumentService documentService; + private final DocumentRepository documentRepository; private final ThumbnailService thumbnailService; /** Per-document timeout for the whole generate() call — defense against corrupt PDFs. */ @@ -59,7 +59,7 @@ public class ThumbnailAsyncRunner { */ @Async("thumbnailExecutor") public void generateAsync(UUID documentId) { - Optional docOpt = documentService.findById(documentId); + Optional docOpt = documentRepository.findById(documentId); if (docOpt.isEmpty()) { log.warn("Thumbnail generation skipped: document not found id={}", documentId); return; diff --git a/backend/src/main/java/org/raddatz/familienarchiv/document/ThumbnailService.java b/backend/src/main/java/org/raddatz/familienarchiv/document/ThumbnailService.java index b937a126..26b9e67e 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/document/ThumbnailService.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/document/ThumbnailService.java @@ -62,16 +62,16 @@ public class ThumbnailService { private final FileService fileService; private final S3Client s3Client; - private final DocumentService documentService; + private final DocumentRepository documentRepository; @Value("${app.s3.bucket}") private String bucketName; public ThumbnailService(FileService fileService, S3Client s3Client, - DocumentService documentService) { + DocumentRepository documentRepository) { this.fileService = fileService; this.s3Client = s3Client; - this.documentService = documentService; + this.documentRepository = documentRepository; } public Outcome generate(Document doc) { @@ -167,7 +167,7 @@ public class ThumbnailService { doc.setThumbnailGeneratedAt(LocalDateTime.now()); doc.setThumbnailAspect(result.aspect()); doc.setPageCount(result.pageCount()); - documentService.updateThumbnailMetadata(doc); + documentRepository.save(doc); return Outcome.SUCCESS; } catch (Exception e) { // Thumbnail is already in S3 but the entity update failed. Because the S3 diff --git a/backend/src/test/java/org/raddatz/familienarchiv/document/ThumbnailServiceTest.java b/backend/src/test/java/org/raddatz/familienarchiv/document/ThumbnailServiceTest.java index 97611274..55f140bf 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/document/ThumbnailServiceTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/document/ThumbnailServiceTest.java @@ -39,17 +39,17 @@ class ThumbnailServiceTest { private FileService fileService; private S3Client s3Client; - private DocumentService documentService; + private DocumentRepository documentRepository; private ThumbnailService thumbnailService; @BeforeEach void setUp() { fileService = mock(FileService.class); s3Client = mock(S3Client.class); - documentService = mock(DocumentService.class); - thumbnailService = new ThumbnailService(fileService, s3Client, documentService); + documentRepository = mock(DocumentRepository.class); + thumbnailService = new ThumbnailService(fileService, s3Client, documentRepository); ReflectionTestUtils.setField(thumbnailService, "bucketName", "test-bucket"); - when(documentService.updateThumbnailMetadata(any(Document.class))).thenAnswer(i -> i.getArgument(0)); + when(documentRepository.save(any(Document.class))).thenAnswer(i -> i.getArgument(0)); } @Test @@ -103,7 +103,7 @@ class ThumbnailServiceTest { assertThat(doc.getThumbnailKey()).isEqualTo("thumbnails/" + doc.getId() + ".jpg"); assertThat(doc.getThumbnailGeneratedAt()).isNotNull(); - verify(documentService).updateThumbnailMetadata(doc); + verify(documentRepository).save(doc); } @Test @@ -152,7 +152,7 @@ class ThumbnailServiceTest { assertThat(outcome).isEqualTo(ThumbnailService.Outcome.FAILED); assertThat(doc.getThumbnailKey()).isNull(); - verify(documentService, never()).updateThumbnailMetadata(any()); + verify(documentRepository, never()).save(any()); } @Test @@ -165,7 +165,7 @@ class ThumbnailServiceTest { assertThat(outcome).isEqualTo(ThumbnailService.Outcome.FAILED); verifyNoInteractions(s3Client); - verify(documentService, never()).updateThumbnailMetadata(any()); + verify(documentRepository, never()).save(any()); } @Test @@ -260,7 +260,7 @@ class ThumbnailServiceTest { assertThat(outcome).isEqualTo(ThumbnailService.Outcome.FAILED); verifyNoInteractions(s3Client); - verify(documentService, never()).updateThumbnailMetadata(any()); + verify(documentRepository, never()).save(any()); } @Test @@ -275,7 +275,7 @@ class ThumbnailServiceTest { assertThat(outcome).isEqualTo(ThumbnailService.Outcome.FAILED); verifyNoInteractions(s3Client); - verify(documentService, never()).updateThumbnailMetadata(any()); + verify(documentRepository, never()).save(any()); } @Test @@ -286,14 +286,14 @@ class ThumbnailServiceTest { Document doc = makeDoc("application/pdf", "documents/letter.pdf"); when(fileService.downloadFileStream(anyString())) .thenReturn(new ByteArrayInputStream(createSamplePdf())); - when(documentService.updateThumbnailMetadata(any())) + when(documentRepository.save(any())) .thenThrow(new RuntimeException("constraint violation")); ThumbnailService.Outcome outcome = thumbnailService.generate(doc); assertThat(outcome).isEqualTo(ThumbnailService.Outcome.FAILED); verify(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class)); - verify(documentService).updateThumbnailMetadata(any()); + verify(documentRepository).save(any()); } // ─── helpers ──────────────────────────────────────────────────────────────