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..9f5eb38 --- /dev/null +++ b/src/main/java/com/bravo/user/controller/PaymentController.java @@ -0,0 +1,44 @@ +package com.bravo.user.controller; + +import java.util.List; + +import com.bravo.user.utility.PageUtil; +import org.springframework.data.domain.PageRequest; +import org.springframework.web.bind.annotation.*; + +import com.bravo.user.annotation.SwaggerController; +import com.bravo.user.model.dto.PaymentDto; +import com.bravo.user.service.PaymentService; +import com.bravo.user.validator.UserValidator; + +import javax.servlet.http.HttpServletResponse; + +@RequestMapping(value = "/payment") +@SwaggerController +public class PaymentController { + + private final PaymentService paymentService; + private final UserValidator userValidator; + + public PaymentController(PaymentService paymentService, UserValidator userValidator) { + this.paymentService = paymentService; + this.userValidator = userValidator; + } + + + @GetMapping(value = "/{userId}/retrieve") + @ResponseBody + public List retrieve( + final @PathVariable(name = "userId") String userId, + final @RequestParam(required = false) Integer page, + final @RequestParam(required = false) Integer size, + final HttpServletResponse httpResponse + ) { + + userValidator.validateId(userId); + final PageRequest pageRequest = PageUtil.createPageRequest(page, size); + return paymentService.retrieveByUserId(userId, pageRequest, httpResponse); + + } + +} diff --git a/src/main/java/com/bravo/user/dao/model/mapper/ResourceMapper.java b/src/main/java/com/bravo/user/dao/model/mapper/ResourceMapper.java index 4a78188..81efe58 100644 --- a/src/main/java/com/bravo/user/dao/model/mapper/ResourceMapper.java +++ b/src/main/java/com/bravo/user/dao/model/mapper/ResourceMapper.java @@ -48,7 +48,7 @@ public > List convertPayments(final T public PaymentDto convertPayment(final Payment payment){ final String cardNumber = payment.getCardNumber(); final PaymentDto dto = mapperFacade.map(payment, PaymentDto.class); - dto.setCardNumberLast4(cardNumber.substring(cardNumber.length() - 5)); + dto.setCardNumberLast4(cardNumber.substring(cardNumber.length() - 4)); return dto; } 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..b420fa8 --- /dev/null +++ b/src/main/java/com/bravo/user/dao/repository/PaymentRepository.java @@ -0,0 +1,18 @@ +package com.bravo.user.dao.repository; + +import com.bravo.user.dao.model.Payment; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + + +@Repository +public interface PaymentRepository extends JpaRepository, JpaSpecificationExecutor { + + // TODO Users instead uses findAll with a specification and filter. + //If we need to filter on more than just user id then this should too + //But for now this should work and be simpler. + Page findByUserId(String userID, Pageable pageRequest); +} 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..bae3219 --- /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.model.dto.PaymentDto; +import com.bravo.user.utility.PageUtil; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +@Service +public class PaymentService { + + private final PaymentRepository paymentRepository; + private final ResourceMapper resourceMapper; + + public PaymentService(PaymentRepository paymentRepository, ResourceMapper resourceMapper) { + this.paymentRepository = paymentRepository; + this.resourceMapper = resourceMapper; + + } + + public List retrieveByUserId(String userId, + final PageRequest pageRequest, + final HttpServletResponse httpResponse) { + Page paymentPage = paymentRepository.findByUserId(userId, pageRequest); + List payments = resourceMapper.convertPayments(paymentPage.getContent()); + PageUtil.updatePageHeaders(httpResponse, paymentPage, pageRequest); + return payments; + } + + +} diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index da45720..6ccd6cc 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -24,6 +24,13 @@ create table payment ( updated timestamp not null default current_timestamp() ); +insert into payment(id, user_id, card_number, expiry_month, expiry_year) values +('4f34e905-72ad-412c-82fe-4974501258d6', '4f34e905-72ad-412c-82fe-4974501258d8', '5400000000000000', 12, 2024), +('4f34e905-72ad-412c-82fe-4974501258d5', '4f34e905-72ad-412c-82fe-4974501258d8', '5400000000000001', 12, 2025), +('4f34e905-72ad-412c-82fe-4974501258d4', '4f34e905-72ad-412c-82fe-4974501258d8', '5400000000000002', 10, 2026), +('4f34e905-72ad-412c-82fe-4974501258d3', '4f34e905-72ad-412c-82fe-4974501258d8', '5400000000000003', 12, 27), +('4f34e905-72ad-412c-82fe-4974501258d2', '0865dbbc-e4b9-4a22-af03-b4d67f56f07b', '5400000000000004', 12, 2028); + create table profile ( id varchar(60) primary key, user_id varchar(60) not null, 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..112ba11 --- /dev/null +++ b/src/test/java/com/bravo/user/controller/PaymentControllerTest.java @@ -0,0 +1,90 @@ +package com.bravo.user.controller; + +import com.bravo.user.App; +import com.bravo.user.model.dto.PaymentDto; +import com.bravo.user.service.PaymentService; +import com.bravo.user.utility.PageUtil; +import org.junit.jupiter.api.BeforeEach; +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.data.domain.PageRequest; +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.ResultActions; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.verify; +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.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ContextConfiguration(classes = {App.class}) +@ExtendWith(SpringExtension.class) +@SpringBootTest() +@AutoConfigureMockMvc +public class PaymentControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private PaymentService paymentService; + + private List payments; + + @BeforeEach + public void beforeEach(){ + final List ids = IntStream + .range(1, 10) + .boxed() + .collect(Collectors.toList()); + + this.payments = ids.stream() + .map(id -> createPaymentDto(Integer.toString(id))) + .collect(Collectors.toList()); + } + + @Test + void getRetrieve() throws Exception { + when(paymentService + .retrieveByUserId(anyString(), any(PageRequest.class), any(HttpServletResponse.class))) + .thenReturn(payments); + + final ResultActions result = this.mockMvc + .perform(get("/payment/testid/retrieve")) + .andExpect(status().isOk()); + + for(int i = 0; i < payments.size(); i++){ + result.andExpect(jsonPath(String.format("$[%d].id", i)).value(payments.get(i).getId())); + } + + final PageRequest pageRequest = PageUtil.createPageRequest(null, null); + verify(paymentService).retrieveByUserId( + eq("testid"), eq(pageRequest), any(HttpServletResponse.class) + ); + } + + @Test + void getRetrieveWithIdMissing() throws Exception { + this.mockMvc.perform(get("/payment/retrieve/")) + .andExpect(status().isNotFound()); + } + + private PaymentDto createPaymentDto(final String id){ + final PaymentDto payment = new PaymentDto(); + payment.setId(id); + payment.setCardNumberLast4("0000"); + return payment; + } +} diff --git a/src/test/java/com/bravo/user/dao/model/mapper/ResourceMapperTest.java b/src/test/java/com/bravo/user/dao/model/mapper/ResourceMapperTest.java index 2b1e32c..ff1848d 100644 --- a/src/test/java/com/bravo/user/dao/model/mapper/ResourceMapperTest.java +++ b/src/test/java/com/bravo/user/dao/model/mapper/ResourceMapperTest.java @@ -1,5 +1,7 @@ package com.bravo.user.dao.model.mapper; +import com.bravo.user.dao.model.Payment; +import com.bravo.user.model.dto.PaymentDto; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -48,4 +50,15 @@ void convertAddressTest( @ConvertWith(MapperArgConverter.class) AddressDto addressDto) { Assertions.assertEquals(addressDto, resourceMapper.convertAddress(address)); } + + @ParameterizedTest + @CsvFileSource( + resources = ("/ResourceMapperTest/convertPaymentTest.csv"), + delimiter = '$', + lineSeparator = ">") + void convertPaymentTest( + @ConvertWith(MapperArgConverter.class) Payment payment, + @ConvertWith(MapperArgConverter.class) PaymentDto paymentDto) { + Assertions.assertEquals(paymentDto, resourceMapper.convertPayment(payment)); + } } \ 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..751bdf2 --- /dev/null +++ b/src/test/java/com/bravo/user/service/PaymentServiceTest.java @@ -0,0 +1,97 @@ +package com.bravo.user.service; + +import com.bravo.user.App; +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.model.dto.PaymentDto; +import com.bravo.user.utility.PageUtil; +import org.junit.jupiter.api.BeforeEach; +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.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ContextConfiguration(classes = {App.class}) +@ExtendWith(SpringExtension.class) +@SpringBootTest +public class PaymentServiceTest { + + @Autowired + private HttpServletResponse httpResponse; + + @Autowired + private PaymentService paymentService; + + @MockBean + private ResourceMapper resourceMapper; + + @MockBean + private PaymentRepository paymentRepository; + + private List dtoPayments; + + @BeforeEach + public void beforeEach() { + final List ids = IntStream + .range(1, 10) + .boxed() + .collect(Collectors.toList()); + + final Page mockPage = mock(Page.class); + when(paymentRepository.findByUserId(any(String.class), any(PageRequest.class))) + .thenReturn(mockPage); + + final List daoPayments = ids.stream() + .map(id -> createPayment(Integer.toString(id))) + .collect(Collectors.toList()); + when(mockPage.getContent()).thenReturn(daoPayments); + when(mockPage.getTotalPages()).thenReturn(9); + + this.dtoPayments = ids.stream() + .map(id -> createPaymentDto(Integer.toString(id))) + .collect(Collectors.toList()); + when(resourceMapper.convertPayments(daoPayments)).thenReturn(dtoPayments); + } + + @Test + public void retrieveByName() { + final String input = "input"; + final PageRequest pageRequest = PageUtil.createPageRequest(null, null); + final List results = paymentService.retrieveByUserId(input, pageRequest, httpResponse); + assertEquals(dtoPayments, results); + assertEquals("9", httpResponse.getHeader("page-count")); + assertEquals("1", httpResponse.getHeader("page-number")); + assertEquals("20", httpResponse.getHeader("page-size")); + + verify(paymentRepository).findByUserId(input, pageRequest); + } + + private PaymentDto createPaymentDto(final String id) { + final PaymentDto payment = new PaymentDto(); + payment.setId(id); + payment.setCardNumberLast4("0000"); + return payment; + } + + private Payment createPayment(final String id) { + final Payment payment = new Payment(); + payment.setId(id); + payment.setCardNumber("5400000000000000"); + return payment; + } +} diff --git a/src/test/resources/ResourceMapperTest/convertPaymentTest.csv b/src/test/resources/ResourceMapperTest/convertPaymentTest.csv new file mode 100644 index 0000000..23f44ac --- /dev/null +++ b/src/test/resources/ResourceMapperTest/convertPaymentTest.csv @@ -0,0 +1,15 @@ +>{ + "id":"testId", + "userId":"testUserId", + "cardNumber":"4444444444441234", + "expiryMonth":"12", + "expiryYear":"99", + "updated":"2021-07-12 12:00:00" +} +${ + "id":"testId", + "cardNumberLast4":"1234", + "expiryMonth":"12", + "expiryYear":"99", + "updated":"2021-07-12 12:00:00" +} \ No newline at end of file