diff --git a/backend/src/main/java/org/raddatz/familienarchiv/controller/PersonController.java b/backend/src/main/java/org/raddatz/familienarchiv/controller/PersonController.java index 6210f529..de2b7498 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/controller/PersonController.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/controller/PersonController.java @@ -63,11 +63,8 @@ public class PersonController { @PostMapping @RequirePermission(Permission.WRITE_ALL) public ResponseEntity createPerson(@Valid @RequestBody PersonUpdateDTO dto) { - if (dto.getFirstName() == null || dto.getFirstName().isBlank() - || dto.getLastName() == null || dto.getLastName().isBlank()) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Vor- und Nachname sind Pflichtfelder"); - } - dto.setFirstName(dto.getFirstName().trim()); + validatePersonNames(dto); + if (dto.getFirstName() != null) dto.setFirstName(dto.getFirstName().trim()); dto.setLastName(dto.getLastName().trim()); return ResponseEntity.ok(personService.createPerson(dto)); } @@ -75,15 +72,22 @@ public class PersonController { @PutMapping("/{id}") @RequirePermission(Permission.WRITE_ALL) public ResponseEntity updatePerson(@PathVariable UUID id, @Valid @RequestBody PersonUpdateDTO dto) { - if (dto.getFirstName() == null || dto.getFirstName().isBlank() - || dto.getLastName() == null || dto.getLastName().isBlank()) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Vor- und Nachname sind Pflichtfelder"); - } - dto.setFirstName(dto.getFirstName().trim()); + validatePersonNames(dto); + if (dto.getFirstName() != null) dto.setFirstName(dto.getFirstName().trim()); dto.setLastName(dto.getLastName().trim()); return ResponseEntity.ok(personService.updatePerson(id, dto)); } + private void validatePersonNames(PersonUpdateDTO dto) { + if (dto.getLastName() == null || dto.getLastName().isBlank()) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Nachname ist Pflichtfeld"); + } + if (dto.getPersonType() == org.raddatz.familienarchiv.model.PersonType.PERSON + && (dto.getFirstName() == null || dto.getFirstName().isBlank())) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Vorname ist Pflichtfeld"); + } + } + @PostMapping("/{id}/merge") @ResponseStatus(HttpStatus.NO_CONTENT) @RequirePermission(Permission.WRITE_ALL) diff --git a/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java b/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java index 02973927..34a92dbf 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java @@ -183,19 +183,19 @@ class PersonControllerTest { @Test @WithMockUser(authorities = "WRITE_ALL") - void createPerson_returns400_whenFirstNameIsMissing() throws Exception { + void createPerson_returns400_whenPersonTypeIsPerson_andFirstNameIsMissing() throws Exception { mockMvc.perform(post("/api/persons") .contentType(MediaType.APPLICATION_JSON) - .content("{\"lastName\":\"Müller\"}")) + .content("{\"lastName\":\"Müller\",\"personType\":\"PERSON\"}")) .andExpect(status().isBadRequest()); } @Test @WithMockUser(authorities = "WRITE_ALL") - void createPerson_returns400_whenFirstNameIsBlank() throws Exception { + void createPerson_returns400_whenPersonTypeIsPerson_andFirstNameIsBlank() throws Exception { mockMvc.perform(post("/api/persons") .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\" \",\"lastName\":\"Müller\"}")) + .content("{\"firstName\":\" \",\"lastName\":\"Müller\",\"personType\":\"PERSON\"}")) .andExpect(status().isBadRequest()); } @@ -204,7 +204,7 @@ class PersonControllerTest { void createPerson_returns400_whenLastNameIsMissing() throws Exception { mockMvc.perform(post("/api/persons") .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"Hans\"}")) + .content("{\"firstName\":\"Hans\",\"personType\":\"PERSON\"}")) .andExpect(status().isBadRequest()); } @@ -213,7 +213,7 @@ class PersonControllerTest { void createPerson_returns400_whenLastNameIsBlank() throws Exception { mockMvc.perform(post("/api/persons") .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"Hans\",\"lastName\":\" \"}")) + .content("{\"firstName\":\"Hans\",\"lastName\":\" \",\"personType\":\"PERSON\"}")) .andExpect(status().isBadRequest()); } @@ -225,11 +225,24 @@ class PersonControllerTest { mockMvc.perform(post("/api/persons") .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\"}")) + .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\",\"personType\":\"PERSON\"}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.firstName").value("Hans")); } + @Test + @WithMockUser(authorities = "WRITE_ALL") + void createPerson_returns200_forInstitution_withoutFirstName() throws Exception { + Person saved = Person.builder().id(UUID.randomUUID()).lastName("Verlag GmbH").build(); + when(personService.createPerson(any(org.raddatz.familienarchiv.dto.PersonUpdateDTO.class))).thenReturn(saved); + + mockMvc.perform(post("/api/persons") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"lastName\":\"Verlag GmbH\",\"personType\":\"INSTITUTION\"}")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.lastName").value("Verlag GmbH")); + } + // ─── PUT /api/persons/{id} ──────────────────────────────────────────────── @Test @@ -242,10 +255,10 @@ class PersonControllerTest { @Test @WithMockUser(authorities = "WRITE_ALL") - void updatePerson_returns400_whenFirstNameIsBlank() throws Exception { + void updatePerson_returns400_whenPersonTypeIsPerson_andFirstNameIsBlank() throws Exception { mockMvc.perform(put("/api/persons/{id}", UUID.randomUUID()) .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"\",\"lastName\":\"Müller\"}")) + .content("{\"firstName\":\"\",\"lastName\":\"Müller\",\"personType\":\"PERSON\"}")) .andExpect(status().isBadRequest()); } @@ -254,7 +267,7 @@ class PersonControllerTest { void updatePerson_returns400_whenLastNameIsNull() throws Exception { mockMvc.perform(put("/api/persons/{id}", UUID.randomUUID()) .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"Hans\"}")) + .content("{\"firstName\":\"Hans\",\"personType\":\"PERSON\"}")) .andExpect(status().isBadRequest()); } @@ -267,7 +280,7 @@ class PersonControllerTest { mockMvc.perform(put("/api/persons/{id}", id) .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\"}")) + .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\",\"personType\":\"PERSON\"}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.lastName").value("Müller")); } @@ -317,11 +330,10 @@ class PersonControllerTest { @Test @WithMockUser(authorities = "WRITE_ALL") void updatePerson_returns400_whenLastNameIsBlank() throws Exception { - // firstName valid, lastName blank → second || operand = true → 400 UUID id = UUID.randomUUID(); mockMvc.perform(put("/api/persons/{id}", id) .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"Hans\",\"lastName\":\" \"}")) + .content("{\"firstName\":\"Hans\",\"lastName\":\" \",\"personType\":\"PERSON\"}")) .andExpect(status().isBadRequest()); } @@ -339,7 +351,7 @@ class PersonControllerTest { .contentType(MediaType.APPLICATION_JSON) .content("{\"firstName\":\"Maria\",\"lastName\":\"Raddatz\"," + "\"alias\":\"Oma Maria\",\"birthYear\":1901,\"deathYear\":1975," + - "\"notes\":\"Some notes\"}")) + "\"notes\":\"Some notes\",\"personType\":\"PERSON\"}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.firstName").value("Maria")) .andExpect(jsonPath("$.alias").value("Oma Maria")) @@ -355,7 +367,7 @@ class PersonControllerTest { UUID id = UUID.randomUUID(); mockMvc.perform(put("/api/persons/{id}", id) .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\",\"notes\":\"" + oversizedNotes + "\"}")) + .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\",\"notes\":\"" + oversizedNotes + "\",\"personType\":\"PERSON\"}")) .andExpect(status().isBadRequest()); } @@ -366,7 +378,7 @@ class PersonControllerTest { UUID id = UUID.randomUUID(); mockMvc.perform(put("/api/persons/{id}", id) .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"" + oversizedFirstName + "\",\"lastName\":\"Müller\"}")) + .content("{\"firstName\":\"" + oversizedFirstName + "\",\"lastName\":\"Müller\",\"personType\":\"PERSON\"}")) .andExpect(status().isBadRequest()); } @@ -377,7 +389,7 @@ class PersonControllerTest { void createPerson_returns403_whenUserHasOnlyReadPermission() throws Exception { mockMvc.perform(post("/api/persons") .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\"}")) + .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\",\"personType\":\"PERSON\"}")) .andExpect(status().isForbidden()); } @@ -386,7 +398,7 @@ class PersonControllerTest { void updatePerson_returns403_whenUserHasOnlyReadPermission() throws Exception { mockMvc.perform(put("/api/persons/{id}", UUID.randomUUID()) .contentType(MediaType.APPLICATION_JSON) - .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\"}")) + .content("{\"firstName\":\"Hans\",\"lastName\":\"Müller\",\"personType\":\"PERSON\"}")) .andExpect(status().isForbidden()); }