feat(bulk-edit): add POST /api/documents/batch-metadata endpoint

READ_ALL-gated batch endpoint returning lightweight summaries (id, title,
server PDF URL) for the bulk-edit page's left strip. Unknown IDs are silently
dropped — missing previews would be obvious to the user already.

Refs #225

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-25 14:38:08 +02:00
parent f0da033ec9
commit d251806e72
4 changed files with 114 additions and 0 deletions

View File

@@ -1016,6 +1016,41 @@ class DocumentControllerTest {
verify(documentService).applyBulkEditToDocument(eq(id2), any());
}
// ─── POST /api/documents/batch-metadata ──────────────────────────────────
@Test
void batchMetadata_returns401_whenUnauthenticated() throws Exception {
mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post("/api/documents/batch-metadata")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"ids\":[\"" + UUID.randomUUID() + "\"]}"))
.andExpect(status().isUnauthorized());
}
@Test
@WithMockUser(authorities = "READ_ALL")
void batchMetadata_returns400_whenIdsEmpty() throws Exception {
mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post("/api/documents/batch-metadata")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"ids\":[]}"))
.andExpect(status().isBadRequest());
}
@Test
@WithMockUser(authorities = "READ_ALL")
void batchMetadata_returnsSummaries_forExistingIds() throws Exception {
UUID id = UUID.randomUUID();
when(documentService.batchMetadata(any())).thenReturn(List.of(
new org.raddatz.familienarchiv.dto.DocumentBatchSummary(id, "Brief", "/api/documents/" + id + "/file")));
mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post("/api/documents/batch-metadata")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"ids\":[\"" + id + "\"]}"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].id").value(id.toString()))
.andExpect(jsonPath("$[0].title").value("Brief"))
.andExpect(jsonPath("$[0].pdfUrl").value("/api/documents/" + id + "/file"));
}
@Test
@WithMockUser(authorities = "WRITE_ALL")
void patchBulk_returnsPartialFailureShape_whenServiceThrowsForOneDocument() throws Exception {

View File

@@ -2089,6 +2089,58 @@ class DocumentServiceTest {
assertThat(doc.getDocumentLocation()).isEqualTo("NewLocation");
}
// ─── batchMetadata ───────────────────────────────────────────────────────
@Test
void batchMetadata_returnsEmpty_whenIdsIsNull() {
assertThat(documentService.batchMetadata(null)).isEmpty();
}
@Test
void batchMetadata_returnsEmpty_whenIdsIsEmpty() {
assertThat(documentService.batchMetadata(List.of())).isEmpty();
}
@Test
void batchMetadata_returnsSummariesWithPdfUrl_forExistingIds() {
UUID id1 = UUID.randomUUID();
UUID id2 = UUID.randomUUID();
Document d1 = Document.builder().id(id1).title("Brief 1").build();
Document d2 = Document.builder().id(id2).title("Brief 2").build();
when(documentRepository.findAllById(List.of(id1, id2))).thenReturn(List.of(d1, d2));
var result = documentService.batchMetadata(List.of(id1, id2));
assertThat(result).hasSize(2);
assertThat(result.get(0).id()).isEqualTo(id1);
assertThat(result.get(0).title()).isEqualTo("Brief 1");
assertThat(result.get(0).pdfUrl()).isEqualTo("/api/documents/" + id1 + "/file");
}
@Test
void batchMetadata_silentlyDropsUnknownIds() {
UUID known = UUID.randomUUID();
UUID missing = UUID.randomUUID();
Document d = Document.builder().id(known).title("Found").build();
when(documentRepository.findAllById(List.of(known, missing))).thenReturn(List.of(d));
var result = documentService.batchMetadata(List.of(known, missing));
assertThat(result).hasSize(1);
assertThat(result.get(0).id()).isEqualTo(known);
}
@Test
void batchMetadata_fallsBackToOriginalFilename_whenTitleIsNull() {
UUID id = UUID.randomUUID();
Document d = Document.builder().id(id).originalFilename("scan001.pdf").build();
when(documentRepository.findAllById(List.of(id))).thenReturn(List.of(d));
var result = documentService.batchMetadata(List.of(id));
assertThat(result.get(0).title()).isEqualTo("scan001.pdf");
}
@Test
void applyBulkEditToDocument_skipsLocationFields_whenBlankOrNull() {
UUID id = UUID.randomUUID();