diff --git a/backend/src/main/java/org/raddatz/familienarchiv/geschichte/GeschichteController.java b/backend/src/main/java/org/raddatz/familienarchiv/geschichte/GeschichteController.java index 30b6af59..604cc034 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/geschichte/GeschichteController.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/geschichte/GeschichteController.java @@ -9,6 +9,7 @@ import org.raddatz.familienarchiv.geschichte.journeyitem.JourneyReorderDTO; import org.raddatz.familienarchiv.security.Permission; import org.raddatz.familienarchiv.security.RequirePermission; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; @@ -35,9 +36,13 @@ public class GeschichteController { @GetMapping public List list( + @Parameter(description = "Filter by status. Callers without BLOG_WRITE always receive PUBLISHED results regardless of the value passed. Callers with BLOG_WRITE requesting DRAFT receive only their own unpublished stories.") @RequestParam(required = false) GeschichteStatus status, + @Parameter(description = "AND-filter: story must include all supplied person IDs.") @RequestParam(name = "personId", required = false) List personIds, + @Parameter(description = "Filter to stories containing this document.") @RequestParam(required = false) UUID documentId, + @Parameter(description = "Maximum results to return. Values ≤ 0 default to 50. Clamped at 200.") @RequestParam(required = false, defaultValue = "50") int limit) { return geschichteService.list( status, diff --git a/backend/src/test/java/org/raddatz/familienarchiv/geschichte/GeschichteControllerTest.java b/backend/src/test/java/org/raddatz/familienarchiv/geschichte/GeschichteControllerTest.java index caf1a515..853c6e86 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/geschichte/GeschichteControllerTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/geschichte/GeschichteControllerTest.java @@ -100,6 +100,43 @@ class GeschichteControllerTest { verify(geschichteService).list(any(), eq(List.of(a, b)), any(), anyInt()); } + @Test + @WithMockUser(authorities = "READ_ALL") + void list_passesDocumentIdFilterToService() throws Exception { + UUID documentId = UUID.randomUUID(); + when(geschichteService.list(any(), any(), eq(documentId), anyInt())) + .thenReturn(List.of()); + + mockMvc.perform(get("/api/geschichten").param("documentId", documentId.toString())) + .andExpect(status().isOk()); + + verify(geschichteService).list(any(), any(), eq(documentId), anyInt()); + } + + @Test + @WithMockUser(authorities = "READ_ALL") + void list_passesLimitToService() throws Exception { + when(geschichteService.list(any(), any(), any(), eq(5))) + .thenReturn(List.of()); + + mockMvc.perform(get("/api/geschichten").param("limit", "5")) + .andExpect(status().isOk()); + + verify(geschichteService).list(any(), any(), any(), eq(5)); + } + + @Test + @WithMockUser(authorities = "READ_ALL") + void list_passesStatusFilterToService() throws Exception { + when(geschichteService.list(eq(GeschichteStatus.PUBLISHED), any(), any(), anyInt())) + .thenReturn(List.of()); + + mockMvc.perform(get("/api/geschichten").param("status", "PUBLISHED")) + .andExpect(status().isOk()); + + verify(geschichteService).list(eq(GeschichteStatus.PUBLISHED), any(), any(), anyInt()); + } + // ─── GET /api/geschichten/{id} ─────────────────────────────────────────── @Test diff --git a/frontend/.prettierignore b/frontend/.prettierignore index 0902f412..2b6ddd63 100644 --- a/frontend/.prettierignore +++ b/frontend/.prettierignore @@ -19,6 +19,7 @@ bun.lockb /src/lib/paraglide/ /src/lib/paraglide_bak*/ /src/paraglide/ +/src.main/ /project.inlang/ # Test artifacts diff --git a/frontend/src/lib/generated/api.ts b/frontend/src/lib/generated/api.ts index 5b49d7d7..7c33f707 100644 --- a/frontend/src/lib/generated/api.ts +++ b/frontend/src/lib/generated/api.ts @@ -3712,9 +3712,13 @@ export interface operations { list: { parameters: { query?: { + /** @description Filter by status. Callers without BLOG_WRITE always receive PUBLISHED results regardless of the value passed. Callers with BLOG_WRITE requesting DRAFT receive only their own unpublished stories. */ status?: "DRAFT" | "PUBLISHED"; + /** @description AND-filter: story must include all supplied person IDs. */ personId?: string[]; + /** @description Filter to stories containing this document. */ documentId?: string; + /** @description Maximum results to return. Values ≤ 0 default to 50. Clamped at 200. */ limit?: number; }; header?: never;