diff --git a/src/main/java/com/bravo/user/controller/PaymentController.java b/src/main/java/com/bravo/user/controller/PaymentController.java new file mode 100644 index 0000000..f8cfe1f --- /dev/null +++ b/src/main/java/com/bravo/user/controller/PaymentController.java @@ -0,0 +1,41 @@ +package com.bravo.user.controller; + +import com.bravo.user.model.dto.PaymentDto; +import com.bravo.user.service.PaymentService; +import com.bravo.user.validator.UserValidator; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@Tag(name = "Payment", description = "User payments") +@RequestMapping(value = "/payment") +@Slf4j +public class PaymentController { + + private final PaymentService paymentService; + private final UserValidator userValidator; + + public PaymentController(PaymentService paymentService, UserValidator userValidator) { + this.paymentService = paymentService; + this.userValidator = userValidator; + } + + @Operation(summary = "Retrieves a list of payments by User") + @GetMapping("/retrieve/{userId}") + @ResponseBody + public List retrievePaymentsByUser(final @PathVariable String userId) { + + log.info("Retrieving payments for user Id {}", userId); + + userValidator.validateId(userId); + return paymentService.retrieveByUserId(userId); + } +} diff --git a/src/main/java/com/bravo/user/dao/repository/PaymentRepository.java b/src/main/java/com/bravo/user/dao/repository/PaymentRepository.java new file mode 100644 index 0000000..f047edc --- /dev/null +++ b/src/main/java/com/bravo/user/dao/repository/PaymentRepository.java @@ -0,0 +1,13 @@ +package com.bravo.user.dao.repository; + +import com.bravo.user.dao.model.Payment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface PaymentRepository extends JpaRepository { + + List findAllByUserId(String userId); +} diff --git a/src/main/java/com/bravo/user/service/PaymentService.java b/src/main/java/com/bravo/user/service/PaymentService.java new file mode 100644 index 0000000..ce68f2a --- /dev/null +++ b/src/main/java/com/bravo/user/service/PaymentService.java @@ -0,0 +1,37 @@ +package com.bravo.user.service; + +import com.bravo.user.dao.model.Payment; +import com.bravo.user.dao.model.mapper.ResourceMapper; +import com.bravo.user.dao.repository.PaymentRepository; +import com.bravo.user.dao.repository.UserRepository; +import com.bravo.user.exception.DataNotFoundException; +import com.bravo.user.model.dto.PaymentDto; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@Slf4j +public class PaymentService { + + private final PaymentRepository paymentRepository; + private final UserRepository userRepository; + private final ResourceMapper mapper; + + public PaymentService(PaymentRepository paymentRepository, UserRepository userRepository, ResourceMapper mapper) { + this.paymentRepository = paymentRepository; + this.userRepository = userRepository; + this.mapper = mapper; + } + + public List retrieveByUserId(String userId) { + log.info("Retrieving all payments by user Id"); + + userRepository.findById(userId).orElseThrow(() -> new DataNotFoundException("User Id does not exist. Please validate input")); + final List payments = paymentRepository.findAllByUserId(userId); + + log.info("Found {} payments for user {}", payments.size(), userId); + return mapper.convertPayments(payments); + } +} diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index c4868b9..ff028de 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -646,3 +646,11 @@ insert into address (id, user_id, line1, line2, city, state, zip) values ('42f33d30-f3f8-4743-a94e-4db11fdb747d', '008a4215-0b1d-445e-b655-a964039cbb5a', '412 Maple St', null, 'Dowagiac', 'Michigan', '49047'), ('579872ec-46f8-46b5-b809-d0724d965f0e', '00963d9b-f884-485e-9455-fcf30c6ac379', '237 Mountain Ter', 'Apt 10', 'Odenville', 'Alabama', '35120'), ('95a983d0-ba0e-4f30-afb6-667d4724b253', '00963d9b-f884-485e-9455-fcf30c6ac379', '107 Annettes Ct', null, 'Aydlett', 'North Carolina', '27916'); + +insert into payment (id, user_id, card_number, expiry_month, expiry_year, updated) values +('15040055-a640-4cea-cfb7-357669fcdefe', 'fd6d21f6-f1c2-473d-8ed7-f3f9c7550cc9', '7219038435696266', 8, 2028, {ts '2023-07-01 07:20:16.267'}), +('751c48b7-bbe4-4e94-bacb-31eb81fd2c12', 'f9096b14-daab-430f-835b-2f0d651e3559', '4390580235433205', 9, 2027, {ts '2022-02-11 09:55:48.371'}), +('6033d04d-c602-4354-a411-d376a9215c68', 'f8bbbc7a-867b-4e8d-a2a5-8b7c07a4589c', '3997869182123667', 1, 2024, {ts '2020-11-12 11:22:15.264'}), +('dd52bd06-d337-4624-d9bf-9593f2cff4c1', '06c70a5b-1950-4765-81b6-4bb2378e0991', '8439375960556557', 2, 2026, {ts '2018-05-11 17:22:15.125'}), +('1628a7c9-ed7e-4582-eac6-b3321c2fdbe1', 'd54b6b5d-de10-4c06-9837-c7457937daf8', '5929456670566065', 3, 2025, {ts '2019-04-26 06:55:27.748'}), +('6931d250-f972-4c5b-fc65-85904473de56', 'f8160123-1766-41c7-8056-2acbfd3c4f2a', '2981890323295572', 7, 2024, {ts '2022-08-08 09:11:52.428'}); \ No newline at end of file diff --git a/src/test/java/com/bravo/user/controller/PaymentControllerTest.java b/src/test/java/com/bravo/user/controller/PaymentControllerTest.java new file mode 100644 index 0000000..e8a41b5 --- /dev/null +++ b/src/test/java/com/bravo/user/controller/PaymentControllerTest.java @@ -0,0 +1,79 @@ +package com.bravo.user.controller; + +import com.bravo.user.App; +import com.bravo.user.service.PaymentService; +import com.bravo.user.util.TestUtil; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ContextConfiguration(classes = {App.class}) +@ExtendWith(SpringExtension.class) +@SpringBootTest() +@AutoConfigureMockMvc +class PaymentControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private PaymentService paymentService; + + @Test + void retrievePaymentsByUser() throws Exception { + + final String userId = "user-id"; + + when(paymentService.retrieveByUserId(userId)).thenReturn(TestUtil.getPaymentsDto()); + + final MvcResult result = this.mockMvc + .perform(get("/payment/retrieve/" + userId)) + .andExpect(status().isOk()) + .andReturn(); + + String responseBody = result.getResponse().getContentAsString(); + + assertNotNull(result); + assertNotNull(responseBody); + assertTrue(responseBody.contains("\"id\" : \"15040055-a640-4cea-cfb7-357669fcdefe\"")); + + } + + @Test + void retrievePaymentsByUserEmptyPayments() throws Exception { + + final String userId = "user-id"; + + when(paymentService.retrieveByUserId(userId)).thenReturn(Collections.emptyList()); + + final MvcResult result = this.mockMvc + .perform(get("/payment/retrieve/" + userId)) + .andExpect(status().isOk()) + .andReturn(); + + String responseBody = result.getResponse().getContentAsString(); + assertNotNull(result); + assertEquals(responseBody, "[ ]"); + } + + @Test + void retrievePaymentsByUserNotFound() throws Exception { + this.mockMvc.perform(get("/payment/retrieve/")).andExpect(status().isNotFound()); + } +} \ No newline at end of file diff --git a/src/test/java/com/bravo/user/service/PaymentServiceTest.java b/src/test/java/com/bravo/user/service/PaymentServiceTest.java new file mode 100644 index 0000000..cc7fd41 --- /dev/null +++ b/src/test/java/com/bravo/user/service/PaymentServiceTest.java @@ -0,0 +1,78 @@ +package com.bravo.user.service; + +import com.bravo.user.App; +import com.bravo.user.dao.model.mapper.ResourceMapper; +import com.bravo.user.dao.repository.PaymentRepository; +import com.bravo.user.dao.repository.UserRepository; +import com.bravo.user.exception.DataNotFoundException; +import com.bravo.user.model.dto.PaymentDto; +import com.bravo.user.util.TestUtil; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ContextConfiguration(classes = {App.class}) +@ExtendWith(SpringExtension.class) +@SpringBootTest() +class PaymentServiceTest { + + private final PaymentRepository paymentRepository = mock(PaymentRepository.class); + private final UserRepository userRepository = mock(UserRepository.class); + private final ResourceMapper resourceMapper = mock(ResourceMapper.class); + + private final PaymentService underTest = new PaymentService( + paymentRepository, + userRepository, + resourceMapper + ); + + @Test + void retrieveByUserId() { + final String userId = "user-id"; + + when(userRepository.findById(userId)).thenReturn(Optional.of(TestUtil.getUserData())); + when(paymentRepository.findAllByUserId(userId)).thenReturn(TestUtil.getPayments()); + when(resourceMapper.convertPayments(TestUtil.getPayments())).thenReturn(TestUtil.getPaymentsDto()); + + List payments = underTest.retrieveByUserId(userId); + + assertNotNull(payments); + assertEquals(payments.size(), 1); + } + + @Test + void retrieveByUserIdNotPaymentsFound() { + final String userId = "user-id"; + + when(userRepository.findById(userId)).thenReturn(Optional.of(TestUtil.getUserData())); + when(paymentRepository.findAllByUserId(userId)).thenReturn(Collections.emptyList()); + when(resourceMapper.convertPayments(Collections.emptyList())).thenReturn(Collections.emptyList()); + + List payments = underTest.retrieveByUserId(userId); + + assertNotNull(payments); + assertEquals(payments.size(), 0); + } + + @Test + void retrieveByUserIdNotFound() { + final String userId = "Not found"; + + when(userRepository.findById(userId)).thenThrow(DataNotFoundException.class); + + DataNotFoundException thrown = assertThrows(DataNotFoundException.class, () -> underTest.retrieveByUserId(userId)); + + assertNotNull(thrown); + } +} \ No newline at end of file diff --git a/src/test/java/com/bravo/user/util/TestUtil.java b/src/test/java/com/bravo/user/util/TestUtil.java new file mode 100644 index 0000000..b2ed2cf --- /dev/null +++ b/src/test/java/com/bravo/user/util/TestUtil.java @@ -0,0 +1,49 @@ +package com.bravo.user.util; + +import com.bravo.user.dao.model.Payment; +import com.bravo.user.dao.model.User; +import com.bravo.user.model.dto.PaymentDto; +import com.bravo.user.model.dto.UserSaveDto; + +import java.time.LocalDateTime; +import java.util.List; + +public class TestUtil { + + public static User getUserData() { + + UserSaveDto userDto = new UserSaveDto(); + + userDto.setFirstName("Harrison"); + userDto.setMiddleName(""); + userDto.setLastName("Ford"); + userDto.setPhoneNumber(""); + + return new User(userDto); + } + + public static List getPayments() { + Payment payment = new Payment(); + + payment.setId("15040055-a640-4cea-cfb7-357669fcdefe"); + payment.setUserId("fd6d21f6-f1c2-473d-8ed7-f3f9c7550cc9"); + payment.setCardNumber("4790580235733205"); + payment.setExpiryMonth(8); + payment.setExpiryYear(2028); + payment.setUpdated(LocalDateTime.of(2023, 1, 25, 1, 25, 0)); + + return List.of(payment); + } + + public static List getPaymentsDto() { + PaymentDto paymentDto = new PaymentDto(); + + paymentDto.setId("15040055-a640-4cea-cfb7-357669fcdefe"); + paymentDto.setExpiryMonth(8); + paymentDto.setExpiryYear(2028); + paymentDto.setUpdated(LocalDateTime.of(2023, 1, 25, 1, 25, 0)); + paymentDto.setCardNumberLast4("3205"); + + return List.of(paymentDto); + } +}