From a3d750822cf65576548a8e58dda6888497c359d8 Mon Sep 17 00:00:00 2001 From: Marcel Date: Sun, 29 Mar 2026 19:40:53 +0200 Subject: [PATCH] feat(persons): accept PersonUpdateDTO for POST /api/persons (all 6 fields) createPerson now takes PersonUpdateDTO, persisting birthYear, deathYear, notes in addition to firstName, lastName, alias. Co-Authored-By: Claude Sonnet 4.6 --- .../controller/PersonController.java | 12 ++++--- .../familienarchiv/service/PersonService.java | 14 +++++++++ .../controller/PersonControllerTest.java | 23 +++++++++++++- .../service/PersonServiceTest.java | 31 +++++++++++++++++++ 4 files changed, 74 insertions(+), 6 deletions(-) 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 9812c752..24c3de42 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/controller/PersonController.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/controller/PersonController.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; + import org.raddatz.familienarchiv.dto.PersonUpdateDTO; import org.raddatz.familienarchiv.model.Document; import org.raddatz.familienarchiv.model.Person; @@ -58,13 +59,14 @@ public class PersonController { @PostMapping @RequirePermission(Permission.WRITE_ALL) - public ResponseEntity createPerson(@RequestBody Map body) { - String firstName = body.get("firstName"); - String lastName = body.get("lastName"); - if (firstName == null || firstName.isBlank() || lastName == null || lastName.isBlank()) { + 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"); } - return ResponseEntity.ok(personService.createPerson(firstName.trim(), lastName.trim(), body.get("alias"))); + dto.setFirstName(dto.getFirstName().trim()); + dto.setLastName(dto.getLastName().trim()); + return ResponseEntity.ok(personService.createPerson(dto)); } @PutMapping("/{id}") diff --git a/backend/src/main/java/org/raddatz/familienarchiv/service/PersonService.java b/backend/src/main/java/org/raddatz/familienarchiv/service/PersonService.java index 9926ea1d..5bb41788 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/service/PersonService.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/service/PersonService.java @@ -72,6 +72,20 @@ public class PersonService { return personRepository.save(person); } + @Transactional + public Person createPerson(PersonUpdateDTO dto) { + validateYears(dto.getBirthYear(), dto.getDeathYear()); + Person person = Person.builder() + .firstName(dto.getFirstName()) + .lastName(dto.getLastName()) + .alias(dto.getAlias() == null || dto.getAlias().isBlank() ? null : dto.getAlias().trim()) + .notes(dto.getNotes() == null || dto.getNotes().isBlank() ? null : dto.getNotes().trim()) + .birthYear(dto.getBirthYear()) + .deathYear(dto.getDeathYear()) + .build(); + return personRepository.save(person); + } + private void validateYears(Integer birthYear, Integer deathYear) { if (birthYear != null && birthYear <= 0) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Geburtsjahr muss eine positive Zahl sein"); 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 cb384f69..44950607 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/controller/PersonControllerTest.java @@ -201,7 +201,7 @@ class PersonControllerTest { @WithMockUser(authorities = "WRITE_ALL") void createPerson_returns200_whenValid() throws Exception { Person saved = Person.builder().id(UUID.randomUUID()).firstName("Hans").lastName("Müller").build(); - when(personService.createPerson(eq("Hans"), eq("Müller"), any())).thenReturn(saved); + when(personService.createPerson(any(org.raddatz.familienarchiv.dto.PersonUpdateDTO.class))).thenReturn(saved); mockMvc.perform(post("/api/persons") .contentType(MediaType.APPLICATION_JSON) @@ -305,6 +305,27 @@ class PersonControllerTest { .andExpect(status().isBadRequest()); } + // ─── Phase 2.2: POST /api/persons with full PersonUpdateDTO ─────────────── + + @Test + @WithMockUser(authorities = "WRITE_ALL") + void createPerson_returns200_withAllSixFields() throws Exception { + UUID id = UUID.randomUUID(); + Person saved = Person.builder().id(id).firstName("Maria").lastName("Raddatz") + .alias("Oma Maria").birthYear(1901).deathYear(1975).notes("Some notes").build(); + when(personService.createPerson(any(org.raddatz.familienarchiv.dto.PersonUpdateDTO.class))).thenReturn(saved); + + mockMvc.perform(post("/api/persons") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"firstName\":\"Maria\",\"lastName\":\"Raddatz\"," + + "\"alias\":\"Oma Maria\",\"birthYear\":1901,\"deathYear\":1975," + + "\"notes\":\"Some notes\"}")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.firstName").value("Maria")) + .andExpect(jsonPath("$.alias").value("Oma Maria")) + .andExpect(jsonPath("$.birthYear").value(1901)); + } + // ─── Phase 1.2: @Size constraints ───────────────────────────────────────── @Test diff --git a/backend/src/test/java/org/raddatz/familienarchiv/service/PersonServiceTest.java b/backend/src/test/java/org/raddatz/familienarchiv/service/PersonServiceTest.java index c8cc2624..b9208d75 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/service/PersonServiceTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/service/PersonServiceTest.java @@ -110,6 +110,37 @@ class PersonServiceTest { assertThat(result.getAlias()).isEqualTo("Hans Müller"); } + // ─── Phase 2.1: createPerson(PersonUpdateDTO) ───────────────────────────── + + @Test + void createPerson_dto_persistsAllSixFields() { + when(personRepository.save(any())).thenAnswer(inv -> inv.getArgument(0)); + + PersonUpdateDTO dto = new PersonUpdateDTO(); + dto.setFirstName("Maria"); dto.setLastName("Raddatz"); dto.setAlias("Oma Maria"); + dto.setBirthYear(1901); dto.setDeathYear(1975); dto.setNotes("Some notes"); + + Person result = personService.createPerson(dto); + + assertThat(result.getFirstName()).isEqualTo("Maria"); + assertThat(result.getLastName()).isEqualTo("Raddatz"); + assertThat(result.getAlias()).isEqualTo("Oma Maria"); + assertThat(result.getBirthYear()).isEqualTo(1901); + assertThat(result.getDeathYear()).isEqualTo(1975); + assertThat(result.getNotes()).isEqualTo("Some notes"); + } + + @Test + void createPerson_dto_yearValidationFires_whenBirthYearNegative() { + PersonUpdateDTO dto = new PersonUpdateDTO(); + dto.setFirstName("Anna"); dto.setLastName("Test"); dto.setBirthYear(-1); + + assertThatThrownBy(() -> personService.createPerson(dto)) + .isInstanceOf(ResponseStatusException.class) + .extracting(e -> ((ResponseStatusException) e).getStatusCode().value()) + .isEqualTo(400); + } + // ─── updatePerson (alias) ───────────────────────────────────────────────── @Test