-
Notifications
You must be signed in to change notification settings - Fork 39
[bravo-0013] Implement new payment API to receive payments by user id #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package com.bravo.user.controller; | ||
|
|
||
| import com.bravo.user.annotation.SwaggerController; | ||
| import com.bravo.user.model.dto.PaymentDto; | ||
| import com.bravo.user.service.PaymentService; | ||
| import java.util.List; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
| import org.springframework.web.bind.annotation.ResponseBody; | ||
|
|
||
| @SwaggerController | ||
| @RequestMapping("/payments") | ||
| @RequiredArgsConstructor | ||
| @Slf4j | ||
| public class PaymentController { | ||
|
|
||
| private final PaymentService paymentService; | ||
|
|
||
| @GetMapping | ||
| @ResponseBody | ||
| public List<PaymentDto> findPaymentByUserId(@RequestParam String userId) { | ||
| log.info("Starting to search payment for user with id {}", userId); | ||
| return paymentService.findPaymentByUserId(userId); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,10 @@ | ||
| package com.bravo.user.controller.advice; | ||
|
|
||
| import com.bravo.user.exception.PaymentNotFoundException; | ||
| import com.bravo.user.model.dto.ErrorDto; | ||
| import java.util.Set; | ||
| import java.util.stream.Collectors; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.validation.BindException; | ||
| import org.springframework.web.bind.annotation.ControllerAdvice; | ||
|
|
@@ -11,6 +13,7 @@ | |
| import org.springframework.web.bind.annotation.ResponseStatus; | ||
|
|
||
| @ControllerAdvice | ||
| @Slf4j | ||
| public class ExceptionHandlerAdvice { | ||
|
|
||
| @ExceptionHandler(value = BindException.class) | ||
|
|
@@ -31,4 +34,17 @@ public ErrorDto handleBindException(final BindException exception){ | |
| response.setStatusCode(400); | ||
| return response; | ||
| } | ||
|
|
||
| @ExceptionHandler(value = PaymentNotFoundException.class) | ||
| @ResponseBody | ||
| @ResponseStatus(value = HttpStatus.NOT_FOUND) | ||
| public ErrorDto handlePaymentNotFoundException(final PaymentNotFoundException exception){ | ||
| log.info("Payments not found, details: {}", exception.getMessage()); | ||
| var errorDto = new ErrorDto(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, thanks |
||
| errorDto.setMessage(exception.getMessage()); | ||
| errorDto.setStatusCode(HttpStatus.NOT_FOUND.value()); | ||
|
|
||
| return errorDto; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.bravo.user.dao.model.mapper; | ||
|
|
||
| import com.bravo.user.dao.model.Payment; | ||
| import com.bravo.user.model.dto.PaymentDto; | ||
| import java.util.List; | ||
| import org.mapstruct.Mapper; | ||
| import org.mapstruct.Mapping; | ||
| import org.mapstruct.Named; | ||
|
|
||
| @Mapper(componentModel = "spring") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain what
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @wheelerswebservices it means construct mapper as spring bean. Because mapper has different construction types, using factory, spring beans etc. |
||
| public interface PaymentMapper { | ||
|
|
||
| @Mapping(source = "cardNumber", target = "cardNumber", qualifiedByName = "lastFourCardDigits") | ||
| PaymentDto toPaymentDto(Payment payment); | ||
|
|
||
| @Mapping(source = "cardNumber", target = "cardNumber", qualifiedByName = "lastFourCardDigits") | ||
| List<PaymentDto> toPaymentDtoList(List<Payment> payments); | ||
|
|
||
| @Named("lastFourCardDigits") | ||
| static String lastFourCardDigits(String cardNumber) { | ||
| return cardNumber.replaceAll("\\w(?=\\w{4})", "*"); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,9 @@ | ||
| package com.bravo.user.dao.model.mapper; | ||
|
|
||
| import com.bravo.user.dao.model.Address; | ||
| import com.bravo.user.dao.model.Payment; | ||
| import com.bravo.user.dao.model.Profile; | ||
| import com.bravo.user.dao.model.User; | ||
| import com.bravo.user.model.dto.AddressDto; | ||
| import com.bravo.user.model.dto.PaymentDto; | ||
| import com.bravo.user.model.dto.ProfileDto; | ||
| import com.bravo.user.model.dto.UserReadDto; | ||
| import java.util.Collection; | ||
|
|
@@ -41,17 +39,6 @@ public AddressDto convertAddress(final Address address){ | |
| return dto; | ||
| } | ||
|
|
||
| public <T extends Collection<Payment>> List<PaymentDto> convertPayments(final T payments){ | ||
| return payments.stream().map(this::convertPayment).collect(Collectors.toList()); | ||
| } | ||
|
|
||
| public PaymentDto convertPayment(final Payment payment){ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you elaborate on why the new
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @wheelerswebservices because mapperFacade, which is used here is not the flexible one from SOLID principles perpective. |
||
| final String cardNumber = payment.getCardNumber(); | ||
| final PaymentDto dto = mapperFacade.map(payment, PaymentDto.class); | ||
| dto.setCardNumberLast4(cardNumber.substring(cardNumber.length() - 5)); | ||
| return dto; | ||
| } | ||
|
|
||
| public ProfileDto convertProfile(final Profile profile){ | ||
| return mapperFacade.map(profile, ProfileDto.class); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package com.bravo.user.dao.repository; | ||
|
|
||
| import com.bravo.user.dao.model.Payment; | ||
| import java.util.List; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| @Repository | ||
| public interface PaymentRepository extends JpaRepository<Payment, String> { | ||
| List<Payment> findByUserId(String userId); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.bravo.user.exception; | ||
|
|
||
| import java.text.MessageFormat; | ||
|
|
||
| public class PaymentNotFoundException extends RuntimeException { | ||
|
|
||
| private static final String MESSAGE = "Payment not found for user with id {0}"; | ||
|
|
||
| public PaymentNotFoundException(String userId) { | ||
| super(MessageFormat.format(MESSAGE, userId)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @wheelerswebservices regarding first: I don't think this is really matters |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,19 @@ | ||
| package com.bravo.user.model.dto; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||
| import com.fasterxml.jackson.annotation.JsonInclude.Include; | ||
| import java.util.Set; | ||
| import lombok.Data; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Data | ||
| @NoArgsConstructor | ||
| @JsonInclude(Include.NON_NULL) | ||
| public class ErrorDto { | ||
|
|
||
| private Set<String> errors; | ||
| private Class<?> exception; | ||
| private String message; | ||
| private Integer statusCode; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,18 @@ | ||
| package com.bravo.user.model.dto; | ||
|
|
||
| import java.time.LocalDateTime; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Data; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Data | ||
| @Builder | ||
| @AllArgsConstructor | ||
| @NoArgsConstructor | ||
| public class PaymentDto { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why annotate with
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Builder used to simplify construction for testing. Since we use Builder, NoArgs and AllArgs constrcustors is required for MapStruct. |
||
|
|
||
| private String id; | ||
| private String cardNumberLast4; | ||
| private String cardNumber; | ||
| private Integer expiryMonth; | ||
| private Integer expiryYear; | ||
| private LocalDateTime updated; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package com.bravo.user.service; | ||
|
|
||
| import com.bravo.user.dao.model.mapper.PaymentMapper; | ||
| import com.bravo.user.dao.repository.PaymentRepository; | ||
| import com.bravo.user.exception.PaymentNotFoundException; | ||
| import com.bravo.user.model.dto.PaymentDto; | ||
| import java.util.List; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| @Service | ||
| @Slf4j | ||
| @RequiredArgsConstructor | ||
| public class PaymentService { | ||
|
|
||
| private final PaymentRepository paymentRepository; | ||
| private final PaymentMapper paymentMapper; | ||
|
|
||
| public List<PaymentDto> findPaymentByUserId(String userId) { | ||
| var payments = paymentRepository.findByUserId(userId); | ||
|
|
||
| if(payments.isEmpty()) { | ||
| throw new PaymentNotFoundException(userId); | ||
| } | ||
|
|
||
| return paymentMapper.toPaymentDtoList(payments); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.bravo.user; | ||
|
|
||
| import com.bravo.user.config.AppConfig; | ||
| import com.fasterxml.jackson.core.type.TypeReference; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import lombok.SneakyThrows; | ||
| import lombok.experimental.UtilityClass; | ||
|
|
||
| @UtilityClass | ||
| public class TestUtils { | ||
| private static final ObjectMapper OBJECT_MAPPER = new AppConfig().objectMapperBuilder().build(); | ||
|
|
||
| @SneakyThrows | ||
| public String readFromFile(String file) { | ||
| var url = TestUtils.class.getResource(file); | ||
| return Files.readString(Path.of(url.getPath())); | ||
| } | ||
|
|
||
| @SneakyThrows | ||
| public static <T> T deserializeFromJson(String json, TypeReference<T> clazz) { | ||
| return OBJECT_MAPPER.readValue(json, clazz); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice clean up here.