diff --git a/backend/src/main/java/org/raddatz/familienarchiv/dto/DocumentSearchItem.java b/backend/src/main/java/org/raddatz/familienarchiv/dto/DocumentSearchItem.java new file mode 100644 index 00000000..a2c62e0a --- /dev/null +++ b/backend/src/main/java/org/raddatz/familienarchiv/dto/DocumentSearchItem.java @@ -0,0 +1,18 @@ +package org.raddatz.familienarchiv.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import org.raddatz.familienarchiv.audit.ActivityActorDTO; +import org.raddatz.familienarchiv.model.Document; + +import java.util.List; + +public record DocumentSearchItem( + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + Document document, + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + SearchMatchData matchData, + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + int completionPercentage, + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + List contributors +) {} diff --git a/backend/src/main/java/org/raddatz/familienarchiv/dto/DocumentSearchResult.java b/backend/src/main/java/org/raddatz/familienarchiv/dto/DocumentSearchResult.java index 525dec84..764d9c12 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/dto/DocumentSearchResult.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/dto/DocumentSearchResult.java @@ -1,35 +1,16 @@ package org.raddatz.familienarchiv.dto; import io.swagger.v3.oas.annotations.media.Schema; -import org.raddatz.familienarchiv.model.Document; import java.util.List; -import java.util.Map; -import java.util.UUID; public record DocumentSearchResult( @Schema(requiredMode = Schema.RequiredMode.REQUIRED) - List documents, + List items, @Schema(requiredMode = Schema.RequiredMode.REQUIRED) - long total, - @Schema(requiredMode = Schema.RequiredMode.REQUIRED) - Map matchData + long total ) { - /** - * Creates a fully-enriched result from documents and their match overlay data. - * Absent map entries (e.g. document deleted between FTS and enrichment) are safe — - * the frontend treats a missing entry as "no match data". - */ - public static DocumentSearchResult withMatchData(List documents, Map matchData) { - return new DocumentSearchResult(documents, documents.size(), matchData); - } - - /** - * Creates a result without match data — used for filter-only searches (no text query). - * No pagination yet — the full matched set is always returned. - * When pagination is added, total must come from a DB COUNT query, not list.size(). - */ - public static DocumentSearchResult of(List documents) { - return withMatchData(documents, Map.of()); + public static DocumentSearchResult of(List items) { + return new DocumentSearchResult(items, items.size()); } } diff --git a/backend/src/test/java/org/raddatz/familienarchiv/dto/DocumentSearchResultTest.java b/backend/src/test/java/org/raddatz/familienarchiv/dto/DocumentSearchResultTest.java index 36c50a23..3673459d 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/dto/DocumentSearchResultTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/dto/DocumentSearchResultTest.java @@ -2,59 +2,52 @@ package org.raddatz.familienarchiv.dto; import io.swagger.v3.oas.annotations.media.Schema; import org.junit.jupiter.api.Test; +import org.raddatz.familienarchiv.audit.ActivityActorDTO; import org.raddatz.familienarchiv.model.Document; import org.raddatz.familienarchiv.model.DocumentStatus; import java.util.List; -import java.util.Map; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; class DocumentSearchResultTest { - private Document doc(UUID id) { - return Document.builder() - .id(id) + private DocumentSearchItem item(UUID docId) { + Document doc = Document.builder() + .id(docId) .title("Test") .originalFilename("test.pdf") .status(DocumentStatus.UPLOADED) .build(); + return new DocumentSearchItem(doc, SearchMatchData.empty(), 0, List.of()); } @Test - void withMatchData_total_equals_list_size() { + void of_total_equals_list_size() { + DocumentSearchResult result = DocumentSearchResult.of(List.of(item(UUID.randomUUID()), item(UUID.randomUUID()))); + + assertThat(result.total()).isEqualTo(2L); + } + + @Test + void of_exposes_items_with_completion_and_contributors() { UUID id = UUID.randomUUID(); - List docs = List.of(doc(id)); - Map matchData = Map.of(id, SearchMatchData.empty()); + ActivityActorDTO actor = new ActivityActorDTO("AB", "#f00", "Anna Braun"); + Document doc = Document.builder().id(id).title("T").originalFilename("t.pdf") + .status(DocumentStatus.UPLOADED).build(); + DocumentSearchItem item = new DocumentSearchItem(doc, SearchMatchData.empty(), 75, List.of(actor)); - DocumentSearchResult result = DocumentSearchResult.withMatchData(docs, matchData); + DocumentSearchResult result = DocumentSearchResult.of(List.of(item)); - assertThat(result.total()).isEqualTo(1L); + assertThat(result.items()).hasSize(1); + assertThat(result.items().get(0).completionPercentage()).isEqualTo(75); + assertThat(result.items().get(0).contributors()).containsExactly(actor); } @Test - void withMatchData_exposes_match_data_map() { - UUID id = UUID.randomUUID(); - SearchMatchData data = new SearchMatchData("snippet", List.of(), false, List.of(), List.of(), List.of(), null, List.of()); - DocumentSearchResult result = DocumentSearchResult.withMatchData(List.of(doc(id)), Map.of(id, data)); - - assertThat(result.matchData()).containsKey(id); - assertThat(result.matchData().get(id).transcriptionSnippet()).isEqualTo("snippet"); - } - - @Test - void of_factory_returns_empty_match_data() { - UUID id = UUID.randomUUID(); - DocumentSearchResult result = DocumentSearchResult.of(List.of(doc(id))); - - assertThat(result.matchData()).isEmpty(); - assertThat(result.total()).isEqualTo(1L); - } - - @Test - void documents_component_is_annotated_as_required_in_openapi_schema() throws NoSuchFieldException { - Schema schema = DocumentSearchResult.class.getDeclaredField("documents").getAnnotation(Schema.class); + void items_component_is_annotated_as_required_in_openapi_schema() throws NoSuchFieldException { + Schema schema = DocumentSearchResult.class.getDeclaredField("items").getAnnotation(Schema.class); assertThat(schema).isNotNull(); assertThat(schema.requiredMode()).isEqualTo(Schema.RequiredMode.REQUIRED); }