From c847b89a1fb6a9e60d8804f3d7a79a3896f83e75 Mon Sep 17 00:00:00 2001 From: alvinmarshall Date: Sun, 28 Jul 2024 11:29:10 -0400 Subject: [PATCH] chore: update customer endpoint resolves: #5 --- .../com/cheise_proj/auditing/Customer.java | 10 ++- .../auditing/CustomerController.java | 6 ++ .../com/cheise_proj/auditing/CustomerDto.java | 10 +++ .../auditing/CustomerRepository.java | 7 ++ .../cheise_proj/auditing/CustomerService.java | 17 +++- .../auditing/CustomerControllerIT.java | 55 +++++++++++++ .../cheise_proj/auditing/CustomerFixture.java | 25 ++++++ .../auditing/CustomerServiceTest.java | 79 +++++++++++++++++++ 8 files changed, 207 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/cheise_proj/auditing/Customer.java b/src/main/java/com/cheise_proj/auditing/Customer.java index df04e58..874b185 100644 --- a/src/main/java/com/cheise_proj/auditing/Customer.java +++ b/src/main/java/com/cheise_proj/auditing/Customer.java @@ -44,9 +44,17 @@ static Customer of(CustomerDto.CreateCustomer customer) { .build(); } + static Customer of(CustomerDto.UpdateCustomer customer) { + return Customer.builder() + .firstName(customer.firstName()) + .lastName(customer.lastName()) + .emailAddress(customer.emailAddress()) + .build(); + } + void setAddresses(Set customerAddresses, Customer customer) { if (customerAddresses == null) return; - this.addresses = (this.addresses == null) ? new LinkedHashSet<>() : this.addresses; + this.addresses = (this.addresses == null) ? new LinkedHashSet<>() : new LinkedHashSet<>(this.addresses); Set
addressSet = customerAddresses.stream().map(customerAddress -> Address.of(customerAddress, customer)).collect(Collectors.toSet()); this.addresses.addAll(addressSet); } diff --git a/src/main/java/com/cheise_proj/auditing/CustomerController.java b/src/main/java/com/cheise_proj/auditing/CustomerController.java index 7c5d564..fd5c104 100644 --- a/src/main/java/com/cheise_proj/auditing/CustomerController.java +++ b/src/main/java/com/cheise_proj/auditing/CustomerController.java @@ -44,4 +44,10 @@ ResponseEntity getCustomer(@PathVariable("id") Long id) { return ResponseEntity.ok(CustomerDto.toCustomer(customer)); } + @PutMapping("{id}") + ResponseEntity updateCustomer(@PathVariable("id") Long id, @RequestBody @Valid CustomerDto.UpdateCustomer input) { + Customer customer = customerService.updateCustomer(id, input); + return ResponseEntity.ok(CustomerDto.toCustomer(customer)); + } + } diff --git a/src/main/java/com/cheise_proj/auditing/CustomerDto.java b/src/main/java/com/cheise_proj/auditing/CustomerDto.java index bd6bbe6..0f7c333 100644 --- a/src/main/java/com/cheise_proj/auditing/CustomerDto.java +++ b/src/main/java/com/cheise_proj/auditing/CustomerDto.java @@ -37,6 +37,15 @@ record GetCustomer( ) implements CustomerDto { } + @Builder + record UpdateCustomer( + @NotBlank @JsonProperty String firstName, + @NotBlank @JsonProperty String lastName, + @Email @JsonProperty("email") String emailAddress, + @JsonProperty Set customerAddress + ) implements CustomerDto { + } + static GetCustomer toGetCustomer(Customer customer) { Set customerAddresses = null; if (customer.getAddresses() != null) { @@ -55,6 +64,7 @@ static GetCustomer toGetCustomer(Customer customer) { .customerAddress(customerAddresses) .build(); } + static GetCustomer toCustomer(Customer customer) { return GetCustomer.builder() .firstName(customer.getFirstName()) diff --git a/src/main/java/com/cheise_proj/auditing/CustomerRepository.java b/src/main/java/com/cheise_proj/auditing/CustomerRepository.java index ce98ec2..60a6912 100644 --- a/src/main/java/com/cheise_proj/auditing/CustomerRepository.java +++ b/src/main/java/com/cheise_proj/auditing/CustomerRepository.java @@ -4,9 +4,16 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.Optional; interface CustomerRepository extends JpaRepository { @EntityGraph(attributePaths = "addresses") @Override Page findAll(Pageable pageable); + + @EntityGraph(attributePaths = "addresses") + @Query("select c from Customer c where c.id = :id") + Optional findByIdWithAddress(Long id); } diff --git a/src/main/java/com/cheise_proj/auditing/CustomerService.java b/src/main/java/com/cheise_proj/auditing/CustomerService.java index 4787509..61447be 100644 --- a/src/main/java/com/cheise_proj/auditing/CustomerService.java +++ b/src/main/java/com/cheise_proj/auditing/CustomerService.java @@ -23,9 +23,24 @@ Page getCustomers(Pageable pageable) { return customerRepository.findAll(pageable); } - public Customer getCustomer(Long id) { + Customer getCustomer(Long id) { return customerRepository.findById(id) .orElseThrow(() -> new EntityNotFoundException("Customer with id %d not found".formatted(id))); } + Customer getCustomerWithAddress(Long id) { + return customerRepository.findByIdWithAddress(id) + .orElseThrow(() -> new EntityNotFoundException("Customer with id %d not found".formatted(id))); + } + + Customer updateCustomer(Long customerId, CustomerDto.UpdateCustomer updateCustomer) { + Customer customer = getCustomerWithAddress(customerId); + Customer newCustomer = Customer.of(updateCustomer); + customer.setFirstName(newCustomer.getFirstName()); + customer.setLastName(newCustomer.getLastName()); + customer.setEmailAddress(newCustomer.getEmailAddress()); + customer.setAddresses(updateCustomer.customerAddress(), customer); + return customerRepository.save(customer); + } + } diff --git a/src/test/java/com/cheise_proj/auditing/CustomerControllerIT.java b/src/test/java/com/cheise_proj/auditing/CustomerControllerIT.java index 7e24416..12bc5de 100644 --- a/src/test/java/com/cheise_proj/auditing/CustomerControllerIT.java +++ b/src/test/java/com/cheise_proj/auditing/CustomerControllerIT.java @@ -98,4 +98,59 @@ void getCustomer_by_id_returns_404() throws Exception { ).andExpectAll(MockMvcResultMatchers.status().isNotFound()) .andDo(result -> log.info("result: {}", result.getResponse().getContentAsString())); } + + @Test + void updateCustomer_returns_200() throws Exception { + String customerLocation = (String) mockMvc.perform(MockMvcRequestBuilders.post("/customers") + .contentType(MediaType.APPLICATION_JSON) + .content(CustomerFixture.createCustomerWithAddress(objectMapper)) + + ).andExpectAll(MockMvcResultMatchers.status().isCreated()) + .andDo(result -> log.info("result: {}", result.getResponse().getHeaderValue("location"))) + .andReturn().getResponse().getHeaderValue("location"); + + assert customerLocation != null; + mockMvc.perform(MockMvcRequestBuilders.put(customerLocation) + .contentType(MediaType.APPLICATION_JSON) + .content(CustomerFixture.updateCustomer(objectMapper)) + + ).andExpectAll(MockMvcResultMatchers.status().isOk()) + .andDo(result -> log.info("result: {}", result.getResponse().getContentAsString())); + } + + @Test + void updateCustomer_With_Address_returns_200() throws Exception { + String customerLocation = (String) mockMvc.perform(MockMvcRequestBuilders.post("/customers") + .contentType(MediaType.APPLICATION_JSON) + .content(CustomerFixture.createCustomerWithAddress(objectMapper)) + + ).andExpectAll(MockMvcResultMatchers.status().isCreated()) + .andDo(result -> log.info("result: {}", result.getResponse().getHeaderValue("location"))) + .andReturn().getResponse().getHeaderValue("location"); + + assert customerLocation != null; + mockMvc.perform(MockMvcRequestBuilders.put(customerLocation) + .contentType(MediaType.APPLICATION_JSON) + .content(CustomerFixture.updateCustomerWithAddress(objectMapper)) + + ).andExpectAll(MockMvcResultMatchers.status().isOk()) + .andDo(result -> log.info("result: {}", result.getResponse().getContentAsString())); + } + + @Test + void updateCustomer_returns_404() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post("/customers") + .contentType(MediaType.APPLICATION_JSON) + .content(CustomerFixture.createCustomerWithAddress(objectMapper)) + + ).andExpectAll(MockMvcResultMatchers.status().isCreated()) + .andDo(result -> log.info("result: {}", result.getResponse().getHeaderValue("location"))); + + mockMvc.perform(MockMvcRequestBuilders.put("/customers/10") + .contentType(MediaType.APPLICATION_JSON) + .content(CustomerFixture.updateCustomer(objectMapper)) + + ).andExpectAll(MockMvcResultMatchers.status().isNotFound()) + .andDo(result -> log.info("result: {}", result.getResponse().getContentAsString())); + } } \ No newline at end of file diff --git a/src/test/java/com/cheise_proj/auditing/CustomerFixture.java b/src/test/java/com/cheise_proj/auditing/CustomerFixture.java index 298be1e..851fb0e 100644 --- a/src/test/java/com/cheise_proj/auditing/CustomerFixture.java +++ b/src/test/java/com/cheise_proj/auditing/CustomerFixture.java @@ -33,4 +33,29 @@ static String createCustomerWithAddress(ObjectMapper mapper) throws JsonProcessi .build(); return mapper.writeValueAsString(customerDto); } + + static String updateCustomer(ObjectMapper mapper) throws JsonProcessingException { + CustomerDto.CreateCustomer customerDto = CustomerDto.CreateCustomer.builder() + .firstName("Theresia") + .lastName("Macejkovic") + .emailAddress("thres.mac@gmail.com") + .build(); + return mapper.writeValueAsString(customerDto); + } + + static String updateCustomerWithAddress(ObjectMapper mapper) throws JsonProcessingException { + CustomerDto.CreateCustomer customerDto = CustomerDto.CreateCustomer.builder() + .firstName("Vito") + .lastName("Kovacek") + .emailAddress("vito.kov.hahn@gmail.com") + .customerAddress(Set.of(CustomerDto.CustomerAddress.builder() + .city("Phillipshire") + .country("USA") + .streetAddress("68578 Champlin Mill") + .stateCode("NJ") + .zipCode("28711") + .build())) + .build(); + return mapper.writeValueAsString(customerDto); + } } diff --git a/src/test/java/com/cheise_proj/auditing/CustomerServiceTest.java b/src/test/java/com/cheise_proj/auditing/CustomerServiceTest.java index 7671a23..f40cf17 100644 --- a/src/test/java/com/cheise_proj/auditing/CustomerServiceTest.java +++ b/src/test/java/com/cheise_proj/auditing/CustomerServiceTest.java @@ -100,4 +100,83 @@ void getCustomer_throw_if_customer_not_found() { ); assertEquals("Customer with id 1 not found", exception.getMessage()); } + + @Test + void updateCustomer() { + CustomerDto.UpdateCustomer updateCustomerDto = CustomerDto.UpdateCustomer.builder() + .firstName("Update Claribel") + .lastName("Update Zieme") + .emailAddress("Update claribel.zieme@gmail.com") + .build(); + + Mockito.when(customerRepository.findByIdWithAddress(ArgumentMatchers.anyLong())) + .thenReturn(Optional.of(Customer.builder().id(1L) + .firstName("Claribel") + .lastName("Zieme") + .emailAddress("claribel.zieme@gmail.com") + .build())); + + sut.updateCustomer(1L, updateCustomerDto); + Mockito.verify(customerRepository, Mockito.atMostOnce()).save(customerArgumentCaptor.capture()); + Customer customer = customerArgumentCaptor.getValue(); + assertNotNull(customer); + assertEquals("Update Claribel", customer.getFirstName()); + assertEquals("Update Zieme", customer.getLastName()); + assertEquals("Update claribel.zieme@gmail.com", customer.getEmailAddress()); + } + + @Test + void updateCustomerWithAddress() { + CustomerDto.UpdateCustomer updateCustomerDto = CustomerDto.UpdateCustomer.builder() + .firstName("Update Claribel") + .lastName("Update Zieme") + .emailAddress("Update claribel.zieme@gmail.com") + .customerAddress(Set.of(CustomerDto.CustomerAddress.builder() + .city("Emmerichmouth") + .country("USA") + .streetAddress("Suite 290 6898 King Village") + .stateCode("PA") + .zipCode("29665") + .build())) + .build(); + + Mockito.when(customerRepository.findByIdWithAddress(ArgumentMatchers.anyLong())) + .thenReturn(Optional.of(Customer.builder().id(1L) + .firstName("Claribel") + .lastName("Zieme") + .emailAddress("claribel.zieme@gmail.com") + .addresses(Set.of(Address.builder() + .city("Risaberg") + .country("USA") + .streetAddress("942 Walker Street") + .stateCode("WV") + .zipCode("88742") + .build())) + .build())); + + sut.updateCustomer(1L, updateCustomerDto); + Mockito.verify(customerRepository, Mockito.atMostOnce()).save(customerArgumentCaptor.capture()); + Customer customer = customerArgumentCaptor.getValue(); + assertNotNull(customer); + assertEquals("Update Claribel", customer.getFirstName()); + assertEquals("Update Zieme", customer.getLastName()); + assertEquals("Update claribel.zieme@gmail.com", customer.getEmailAddress()); + assertEquals(2, customer.getAddresses().size()); + } + + @Test + void updateCustomer_throw_if_customer_not_found() { + CustomerDto.UpdateCustomer updateCustomerDto = CustomerDto.UpdateCustomer.builder() + .firstName("Update Claribel") + .lastName("Update Zieme") + .emailAddress("Update claribel.zieme@gmail.com") + .build(); + + EntityNotFoundException exception = Assertions.assertThrows( + EntityNotFoundException.class, + () -> sut.updateCustomer(1L, updateCustomerDto) + ); + assertEquals("Customer with id 1 not found", exception.getMessage()); + } + } \ No newline at end of file