feat(transcription): DTOs accept mentionedPersons sidecar with @Valid cascade

CreateTranscriptionBlockDTO and UpdateTranscriptionBlockDTO gain a
List<PersonMention> mentionedPersons field. @Valid is on the field itself,
not just on the controller method, so JSR-303 recurses into the list
elements when the controller boundary calls @Valid on the @RequestBody. The
collection defaults to an empty ArrayList via @Builder.Default; existing
constructor call sites in TranscriptionServiceTest are extended with
List.of() to match the new @AllArgsConstructor signature.

The controller-side @Valid wiring lands in the next commit alongside the
length-201 validation test.

Refs #362

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-28 20:11:01 +02:00
parent 7805da52e6
commit 80ddfb47ac
3 changed files with 30 additions and 8 deletions

View File

@@ -1,14 +1,21 @@
package org.raddatz.familienarchiv.dto; package org.raddatz.familienarchiv.dto;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.Positive;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.raddatz.familienarchiv.model.PersonMention;
import java.util.ArrayList;
import java.util.List;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Builder
public class CreateTranscriptionBlockDTO { public class CreateTranscriptionBlockDTO {
@Min(0) @Min(0)
private int pageNumber; private int pageNumber;
@@ -22,4 +29,8 @@ public class CreateTranscriptionBlockDTO {
private double height; private double height;
private String text; private String text;
private String label; private String label;
@Valid
@Builder.Default
private List<PersonMention> mentionedPersons = new ArrayList<>();
} }

View File

@@ -1,13 +1,24 @@
package org.raddatz.familienarchiv.dto; package org.raddatz.familienarchiv.dto;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.raddatz.familienarchiv.model.PersonMention;
import java.util.ArrayList;
import java.util.List;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Builder
public class UpdateTranscriptionBlockDTO { public class UpdateTranscriptionBlockDTO {
private String text; private String text;
private String label; private String label;
@Valid
@Builder.Default
private List<PersonMention> mentionedPersons = new ArrayList<>();
} }

View File

@@ -98,7 +98,7 @@ class TranscriptionServiceTest {
return b; return b;
}); });
CreateTranscriptionBlockDTO dto = new CreateTranscriptionBlockDTO(1, 0.1, 0.2, 0.3, 0.4, "hello", null); CreateTranscriptionBlockDTO dto = new CreateTranscriptionBlockDTO(1, 0.1, 0.2, 0.3, 0.4, "hello", null, java.util.List.of());
TranscriptionBlock result = transcriptionService.createBlock(docId, dto, userId); TranscriptionBlock result = transcriptionService.createBlock(docId, dto, userId);
@@ -168,7 +168,7 @@ class TranscriptionServiceTest {
when(documentService.getDocumentById(any())).thenReturn( when(documentService.getDocumentById(any())).thenReturn(
Document.builder().scriptType(ScriptType.TYPEWRITER).build()); Document.builder().scriptType(ScriptType.TYPEWRITER).build());
UpdateTranscriptionBlockDTO dto = new UpdateTranscriptionBlockDTO("new text", null); UpdateTranscriptionBlockDTO dto = new UpdateTranscriptionBlockDTO("new text", null, java.util.List.of());
TranscriptionBlock result = transcriptionService.updateBlock(docId, blockId, dto, userId); TranscriptionBlock result = transcriptionService.updateBlock(docId, blockId, dto, userId);
@@ -189,7 +189,7 @@ class TranscriptionServiceTest {
when(documentService.getDocumentById(any())).thenReturn( when(documentService.getDocumentById(any())).thenReturn(
Document.builder().scriptType(ScriptType.TYPEWRITER).build()); Document.builder().scriptType(ScriptType.TYPEWRITER).build());
UpdateTranscriptionBlockDTO dto = new UpdateTranscriptionBlockDTO("text", "Anrede"); UpdateTranscriptionBlockDTO dto = new UpdateTranscriptionBlockDTO("text", "Anrede", java.util.List.of());
TranscriptionBlock result = transcriptionService.updateBlock(docId, blockId, dto, UUID.randomUUID()); TranscriptionBlock result = transcriptionService.updateBlock(docId, blockId, dto, UUID.randomUUID());
@@ -208,7 +208,7 @@ class TranscriptionServiceTest {
Document.builder().scriptType(ScriptType.TYPEWRITER).build()); Document.builder().scriptType(ScriptType.TYPEWRITER).build());
TranscriptionBlock result = transcriptionService.updateBlock( TranscriptionBlock result = transcriptionService.updateBlock(
docId, blockId, new UpdateTranscriptionBlockDTO("new", null), UUID.randomUUID()); docId, blockId, new UpdateTranscriptionBlockDTO("new", null, java.util.List.of()), UUID.randomUUID());
assertThat(result.getSource()).isEqualTo(BlockSource.MANUAL); assertThat(result.getSource()).isEqualTo(BlockSource.MANUAL);
} }
@@ -226,7 +226,7 @@ class TranscriptionServiceTest {
when(documentService.getDocumentById(any())).thenReturn( when(documentService.getDocumentById(any())).thenReturn(
Document.builder().scriptType(ScriptType.HANDWRITING_KURRENT).sender(sender).build()); Document.builder().scriptType(ScriptType.HANDWRITING_KURRENT).sender(sender).build());
transcriptionService.updateBlock(docId, blockId, new UpdateTranscriptionBlockDTO("text", null), UUID.randomUUID()); transcriptionService.updateBlock(docId, blockId, new UpdateTranscriptionBlockDTO("text", null, java.util.List.of()), UUID.randomUUID());
verify(senderModelService).checkAndTriggerTraining(senderId); verify(senderModelService).checkAndTriggerTraining(senderId);
} }
@@ -242,7 +242,7 @@ class TranscriptionServiceTest {
when(documentService.getDocumentById(any())).thenReturn( when(documentService.getDocumentById(any())).thenReturn(
Document.builder().scriptType(ScriptType.HANDWRITING_KURRENT).build()); Document.builder().scriptType(ScriptType.HANDWRITING_KURRENT).build());
transcriptionService.updateBlock(docId, blockId, new UpdateTranscriptionBlockDTO("text", null), UUID.randomUUID()); transcriptionService.updateBlock(docId, blockId, new UpdateTranscriptionBlockDTO("text", null, java.util.List.of()), UUID.randomUUID());
verify(senderModelService, never()).checkAndTriggerTraining(any()); verify(senderModelService, never()).checkAndTriggerTraining(any());
} }
@@ -477,7 +477,7 @@ class TranscriptionServiceTest {
Document.builder().scriptType(ScriptType.TYPEWRITER).build()); Document.builder().scriptType(ScriptType.TYPEWRITER).build());
when(annotationRepository.findById(annotId)).thenReturn(Optional.of(annotation)); when(annotationRepository.findById(annotId)).thenReturn(Optional.of(annotation));
transcriptionService.updateBlock(docId, blockId, new UpdateTranscriptionBlockDTO("new text", null), userId); transcriptionService.updateBlock(docId, blockId, new UpdateTranscriptionBlockDTO("new text", null, java.util.List.of()), userId);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
ArgumentCaptor<Map<String, Object>> payloadCaptor = ArgumentCaptor.forClass(Map.class); ArgumentCaptor<Map<String, Object>> payloadCaptor = ArgumentCaptor.forClass(Map.class);
@@ -502,7 +502,7 @@ class TranscriptionServiceTest {
when(documentService.getDocumentById(any())).thenReturn( when(documentService.getDocumentById(any())).thenReturn(
Document.builder().scriptType(ScriptType.TYPEWRITER).build()); Document.builder().scriptType(ScriptType.TYPEWRITER).build());
transcriptionService.updateBlock(docId, blockId, new UpdateTranscriptionBlockDTO("same text", null), userId); transcriptionService.updateBlock(docId, blockId, new UpdateTranscriptionBlockDTO("same text", null, java.util.List.of()), userId);
verify(auditService, never()).logAfterCommit(any(), any(), any(), any()); verify(auditService, never()).logAfterCommit(any(), any(), any(), any());
} }