Add Swagger documentation

* Split PaymentDTO and PaymentRequest
 * Updated Converters accordingly
 * Use LocalDateTime instead of Date
This commit is contained in:
2023-05-15 14:53:59 +02:00
committed by Laurent KAP
parent fcb6bb063a
commit 2fa012a8b5
17 changed files with 263 additions and 79 deletions

View File

@@ -2,8 +2,12 @@ package net.kapcake.bankingservice.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
@@ -12,15 +16,42 @@ import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class AuthConfig {
private static final String[] AUTH_WHITELIST = {
"/v3/api-docs/**",
"/swagger-ui.html",
"/swagger-ui/**"
};
@Bean
@Order(1)
public SecurityFilterChain loginFilterChain(HttpSecurity http) throws Exception {
return http
.securityMatcher("/login", "/logout")
.csrf().disable()
.httpBasic().and()
.logout(logout -> logout
.clearAuthentication(true)
.invalidateHttpSession(true)).sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
.build();
}
@Bean
public SecurityFilterChain authenticationFilterChain(HttpSecurity http) throws Exception {
return http
.csrf().disable()
.authorizeHttpRequests(requests -> requests
.requestMatchers(AUTH_WHITELIST).permitAll()
.anyRequest().authenticated()
)
.httpBasic().and()
.httpBasic(AbstractHttpConfigurer::disable)
.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
.exceptionHandling(handler -> handler
.authenticationEntryPoint((request, response, authException) -> {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType(MediaType.APPLICATION_JSON.toString());
response.getWriter().write("{ \"error\": \"You are not authenticated.\" }");
})
)
.build();
}

View File

@@ -1,5 +1,7 @@
package net.kapcake.bankingservice.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.modelmapper.ModelMapper;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
@@ -17,4 +19,14 @@ public class BankingServiceConfig {
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build();
}
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("Banking REST service")
.description("Simple REST API to perform payments")
.version("0.0.1")
);
}
}

View File

@@ -1,11 +1,16 @@
package net.kapcake.bankingservice.controllers;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import net.kapcake.bankingservice.exceptions.ValidationException;
import net.kapcake.bankingservice.model.dtos.UserUpdateDTO;
import net.kapcake.bankingservice.model.dtos.UserUpdateRequest;
import net.kapcake.bankingservice.security.UserDetailsImpl;
import net.kapcake.bankingservice.services.UserService;
import org.springframework.security.core.Authentication;
@@ -18,6 +23,7 @@ import org.springframework.web.bind.annotation.RestController;
import static net.kapcake.bankingservice.controllers.ControllerUtils.getErrorString;
@Tag(name = "Authentication controller")
@RestController
@Slf4j
public class AuthController {
@@ -27,6 +33,12 @@ public class AuthController {
this.userService = userService;
}
@Operation(summary = "Login using basic authentication to get a session cookie")
@SecurityRequirement(name = "basic")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Successful login"),
@ApiResponse(responseCode = "401", description = "Unsuccessful login")
})
@PostMapping("/login")
public void login(HttpServletRequest request) {
Authentication auth = (Authentication) request.getUserPrincipal();
@@ -34,13 +46,30 @@ public class AuthController {
log.info("User {} logged in.", user.getUsername());
}
@Operation(summary = "Logout and invalidate session")
@ApiResponse(responseCode = "204", description = "Successful logout")
@PostMapping("/logout")
public void logout() {
// Logout is handled by Spring Security
}
@Operation(summary = "Update user details")
@SecurityRequirement(name = "cookie")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Successful update. If the password was updated, the user has been logged out"),
@ApiResponse(responseCode = "400", description = "Update request is malformed"),
@ApiResponse(responseCode = "401", description = "User not authenticated")
})
@PutMapping("/update-user")
public void updateUser(HttpServletRequest request, @AuthenticationPrincipal UserDetailsImpl authenticatedUser, @RequestBody @Valid UserUpdateDTO userUpdateDTO, BindingResult bindingResult) throws ServletException {
public void updateUser(HttpServletRequest request,
@AuthenticationPrincipal UserDetailsImpl authenticatedUser,
@RequestBody @Valid UserUpdateRequest userUpdateRequest,
BindingResult bindingResult) throws ServletException {
if (bindingResult.hasErrors()) {
String errorString = getErrorString(bindingResult);
throw new ValidationException("User update request invalid: " + errorString);
}
boolean needsLogout = userService.updateUser(authenticatedUser, userUpdateDTO);
boolean needsLogout = userService.updateUser(authenticatedUser, userUpdateRequest);
if (needsLogout) {
request.logout();
}

View File

@@ -1,58 +1,119 @@
package net.kapcake.bankingservice.controllers;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Valid;
import jakarta.validation.Validator;
import net.kapcake.bankingservice.exceptions.ValidationException;
import net.kapcake.bankingservice.model.dtos.BankAccountDTO;
import net.kapcake.bankingservice.model.dtos.PaymentDTO;
import net.kapcake.bankingservice.model.dtos.PaymentFilter;
import net.kapcake.bankingservice.model.dtos.PaymentRequest;
import net.kapcake.bankingservice.security.UserDetailsImpl;
import net.kapcake.bankingservice.services.AccountService;
import net.kapcake.bankingservice.services.PaymentService;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import static net.kapcake.bankingservice.controllers.ControllerUtils.getErrorString;
@Tag(name = "Banking Service Controller")
@RestController
public class BankingServiceController {
private final AccountService accountService;
private final PaymentService paymentService;
private final Validator validator;
public BankingServiceController(AccountService accountService, PaymentService paymentService) {
public BankingServiceController(AccountService accountService, PaymentService paymentService, Validator validator) {
this.accountService = accountService;
this.paymentService = paymentService;
this.validator = validator;
}
@Operation(summary = "List user bank accounts")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "List of accounts owned by the authenticated user"),
@ApiResponse(responseCode = "401", description = "User not authenticated", content = @Content)
})
@SecurityRequirement(name = "cookie")
@GetMapping("/accounts")
public List<BankAccountDTO> getAccounts(@AuthenticationPrincipal UserDetailsImpl authenticatedUser) {
return accountService.getAccounts(authenticatedUser);
}
@Operation(summary = "Create payment")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "The payment was created successfully"),
@ApiResponse(responseCode = "400", description = "The payment request is invalid", content = @Content),
@ApiResponse(responseCode = "401", description = "User not authenticated", content = @Content)
})
@SecurityRequirement(name = "cookie")
@PostMapping("/payment")
@ResponseStatus(HttpStatus.CREATED)
public PaymentDTO createPayment(@AuthenticationPrincipal UserDetailsImpl authenticatedUser, @RequestBody @Valid PaymentDTO paymentDTO, BindingResult bindingResult) {
public PaymentDTO createPayment(@AuthenticationPrincipal UserDetailsImpl authenticatedUser,
@RequestBody @Valid PaymentRequest paymentRequest,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
String errorString = getErrorString(bindingResult);
throw new ValidationException("Payment request invalid: " + errorString);
}
return paymentService.createPayment(authenticatedUser, paymentDTO);
return paymentService.createPayment(authenticatedUser, paymentRequest);
}
@Operation(summary = "List payments")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "List of payments by the authenticated user filtered by parameter values"),
@ApiResponse(responseCode = "400", description = "The filter is invalid", content = @Content),
@ApiResponse(responseCode = "401", description = "User not authenticated", content = @Content)
})
@SecurityRequirement(name = "cookie")
@GetMapping("/payments")
public List<PaymentDTO> getPayments(@AuthenticationPrincipal UserDetailsImpl authenticatedUser, @RequestBody(required = false) @Valid PaymentFilter paymentFilter, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
String errorString = getErrorString(bindingResult);
throw new ValidationException("Payment filter is invalid: " + errorString);
public List<PaymentDTO> getPayments(@AuthenticationPrincipal UserDetailsImpl authenticatedUser,
@RequestParam(name = "beneficiaryAccountNumber", required = false)
@Parameter(description = "The start date to filter payments",example = "LU560303O43349845521")
String beneficiaryAccountNumber,
@RequestParam(name = "startDate", required = false)
@Parameter(description = "The start date to filter payments", example = "2023-05-11T12:00")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
LocalDateTime startDate,
@RequestParam(name = "endDate", required = false)
@Parameter(description = "The end date to filter payments", example = "2023-05-11T12:00")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
LocalDateTime endDate) {
PaymentFilter paymentFilter = new PaymentFilter().setStartDate(startDate).setEndDate(endDate).setBeneficiaryAccountNumber(beneficiaryAccountNumber);
final Set<ConstraintViolation<PaymentFilter>> violations = validator.validate(paymentFilter);
if (!violations.isEmpty()) {
String errorString = getErrorString(violations);
throw new ValidationException("Payment request invalid: " + errorString);
}
return paymentService.getPaymentsForUser(authenticatedUser, paymentFilter);
}
@Operation(summary = "Delete payment")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Payment successfully deleted"),
@ApiResponse(responseCode = "401", description = "User not authenticated"),
@ApiResponse(responseCode = "403", description = "The payment to be deleted does not belong to the authenticated user or has already been executed"),
@ApiResponse(responseCode = "404", description = "A payment with this id was not found")
})
@SecurityRequirement(name = "cookie")
@DeleteMapping("/payment/{id}")
public void deletePayment(@AuthenticationPrincipal UserDetailsImpl authenticatedUser, @PathVariable("id") Long id) {
public void deletePayment(@AuthenticationPrincipal UserDetailsImpl authenticatedUser,
@Parameter(required = true, description = "Id of the payment to delete", example = "1")
@PathVariable("id") Long id) {
paymentService.deletePayment(authenticatedUser, id);
}

View File

@@ -1,10 +1,12 @@
package net.kapcake.bankingservice.controllers;
import jakarta.validation.ConstraintViolation;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import java.util.List;
import java.util.Set;
public class ControllerUtils {
public static String getErrorString(BindingResult bindingResult) {
@@ -24,4 +26,23 @@ public class ControllerUtils {
builder.append("]");
return builder.toString();
}
public static <T> String getErrorString(Set<ConstraintViolation<T>> violations) {
StringBuilder builder = new StringBuilder("[");
List<ConstraintViolation<T>> allErrors = violations.stream().toList();
for (int i = 0; i < allErrors.size(); i++) {
ConstraintViolation<T> error = allErrors.get(i);
if (i != 0) {
builder.append("\n");
}
if (error.getPropertyPath() != null && !error.getPropertyPath().toString().isBlank()) {
builder.append(error.getPropertyPath())
.append(": ");
}
builder.append(error.getMessage());
}
builder.append("]");
return builder.toString();
}
}

View File

@@ -3,7 +3,7 @@ package net.kapcake.bankingservice.converters;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
public abstract class AbstractConverter<E, D> {
public abstract class AbstractConverter {
protected final ModelMapper modelMapper;
public AbstractConverter(ModelMapper modelMapper) {
@@ -11,8 +11,4 @@ public abstract class AbstractConverter<E, D> {
this.modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
}
public abstract D mapToDTO(E entity);
public abstract E mapToEntity(D dto);
}

View File

@@ -6,17 +6,15 @@ import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Component;
@Component
public class BankAccountConverter extends AbstractConverter<BankAccount, BankAccountDTO> {
public class BankAccountConverter extends AbstractConverter {
public BankAccountConverter(ModelMapper modelMapper) {
super(modelMapper);
}
@Override
public BankAccountDTO mapToDTO(BankAccount bankAccount) {
return modelMapper.map(bankAccount, BankAccountDTO.class);
}
@Override
public BankAccount mapToEntity(BankAccountDTO bankAccountDTO) {
return modelMapper.map(bankAccountDTO, BankAccount.class);
}

View File

@@ -4,6 +4,7 @@ import net.kapcake.bankingservice.exceptions.ValidationException;
import net.kapcake.bankingservice.model.dtos.PaymentDTO;
import net.kapcake.bankingservice.model.domain.BankAccount;
import net.kapcake.bankingservice.model.domain.Payment;
import net.kapcake.bankingservice.model.dtos.PaymentRequest;
import net.kapcake.bankingservice.repositories.BankAccountRepository;
import org.modelmapper.ModelMapper;
import org.modelmapper.TypeMap;
@@ -12,7 +13,7 @@ import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
public class PaymentConverter extends AbstractConverter<Payment, PaymentDTO> {
public class PaymentConverter extends AbstractConverter {
private final BankAccountRepository bankAccountRepository;
public PaymentConverter(BankAccountRepository bankAccountRepository, ModelMapper modelMapper) {
@@ -20,7 +21,6 @@ public class PaymentConverter extends AbstractConverter<Payment, PaymentDTO> {
this.bankAccountRepository = bankAccountRepository;
}
@Override
public PaymentDTO mapToDTO(Payment payment) {
TypeMap<Payment, PaymentDTO> paymentPaymentDTOTypeMap = modelMapper
.typeMap(Payment.class, PaymentDTO.class)
@@ -28,10 +28,9 @@ public class PaymentConverter extends AbstractConverter<Payment, PaymentDTO> {
return paymentPaymentDTOTypeMap.map(payment);
}
@Override
public Payment mapToEntity(PaymentDTO paymentDTO) {
Payment payment = modelMapper.map(paymentDTO, Payment.class);
Optional<BankAccount> bankAccountOptional = bankAccountRepository.findById(paymentDTO.getGiverAccount());
public Payment mapToEntity(PaymentRequest paymentRequest) {
Payment payment = modelMapper.map(paymentRequest, Payment.class);
Optional<BankAccount> bankAccountOptional = bankAccountRepository.findById(paymentRequest.getGiverAccount());
if (bankAccountOptional.isEmpty()) {
throw new ValidationException("Payment request invalid: Giver account does not exist.");
}

View File

@@ -7,7 +7,7 @@ import lombok.experimental.Accessors;
import org.hibernate.annotations.CreationTimestamp;
import java.math.BigDecimal;
import java.util.Date;
import java.time.LocalDateTime;
@Entity
@Accessors(chain = true)
@@ -31,9 +31,8 @@ public class Payment {
private String beneficiaryName;
private String communication;
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(nullable = false, updatable = false)
private Date creationDate;
private LocalDateTime creationDate;
@Enumerated(EnumType.STRING)
private PaymentStatus status;
}

View File

@@ -9,7 +9,7 @@ import net.kapcake.bankingservice.model.domain.PaymentStatus;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.time.LocalDateTime;
@Data
@Accessors(chain = true)
@@ -27,6 +27,6 @@ public class PaymentDTO implements Serializable {
@NotNull
private String beneficiaryName;
private String communication;
private Date creationDate;
private LocalDateTime creationDate;
private PaymentStatus status;
}

View File

@@ -1,28 +1,28 @@
package net.kapcake.bankingservice.model.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import lombok.Data;
import lombok.experimental.Accessors;
import net.kapcake.bankingservice.validation.AtLeastOneFieldNotEmpty;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
@Data
@Accessors(chain = true)
@AtLeastOneFieldNotEmpty(fieldNames = {"beneficiaryAccountNumber", "startDate", "endDate"})
public class PaymentFilter implements Serializable {
private String beneficiaryAccountNumber;
private Date startDate;
private Date endDate;
private LocalDateTime startDate;
private LocalDateTime endDate;
@Schema(hidden = true)
@AssertTrue(message = "Start and end date need to be in order")
public boolean isValidRange() {
if (startDate == null || endDate == null) {
return true;
} else {
return startDate.compareTo(endDate) <= 0;
return !startDate.isAfter(endDate);
}
}
}

View File

@@ -0,0 +1,27 @@
package net.kapcake.bankingservice.model.dtos;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;
import net.kapcake.bankingservice.model.domain.Currency;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@Accessors(chain = true)
public class PaymentRequest implements Serializable {
@NotNull
@DecimalMin(value = "0", inclusive = false)
private BigDecimal amount;
@NotNull
private Currency currency;
@NotNull
private Long giverAccount;
@NotNull
private String beneficiaryAccountNumber;
@NotNull
private String beneficiaryName;
private String communication;
}

View File

@@ -10,7 +10,7 @@ import java.io.Serializable;
@Data
@Accessors(chain = true)
@AtLeastOneFieldNotEmpty(fieldNames = {"password", "street", "number", "numberExtension", "postalCode", "country"})
public class UserUpdateDTO implements Serializable {
public class UserUpdateRequest implements Serializable {
@Pattern(regexp = "^[^\\s]+$")
private String password;
private String street;

View File

@@ -7,6 +7,7 @@ import net.kapcake.bankingservice.exceptions.ValidationException;
import net.kapcake.bankingservice.model.domain.*;
import net.kapcake.bankingservice.model.dtos.PaymentDTO;
import net.kapcake.bankingservice.model.dtos.PaymentFilter;
import net.kapcake.bankingservice.model.dtos.PaymentRequest;
import net.kapcake.bankingservice.repositories.BalanceRepository;
import net.kapcake.bankingservice.repositories.BankAccountRepository;
import net.kapcake.bankingservice.repositories.PaymentRepository;
@@ -16,8 +17,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -42,8 +43,8 @@ public class PaymentService {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public PaymentDTO createPayment(UserDetailsImpl authenticatedUser, PaymentDTO paymentDTO) {
Payment payment = paymentConverter.mapToEntity(paymentDTO);
public PaymentDTO createPayment(UserDetailsImpl authenticatedUser, PaymentRequest paymentRequest) {
Payment payment = paymentConverter.mapToEntity(paymentRequest);
List<BankAccount> userBankAccounts = bankAccountRepository.findAllByUsers_username(authenticatedUser.getUsername());
paymentValidator.validate(userBankAccounts, payment);
@@ -92,18 +93,18 @@ public class PaymentService {
List<Payment> filteredPayments = userPayments;
if (paymentFilter != null) {
String beneficiaryAccountNumber = paymentFilter.getBeneficiaryAccountNumber();
Date startDate = paymentFilter.getStartDate();
Date endDate = paymentFilter.getEndDate();
LocalDateTime startDate = paymentFilter.getStartDate();
LocalDateTime endDate = paymentFilter.getEndDate();
filteredPayments = userPayments.stream().filter(payment -> {
boolean filter = true;
if (beneficiaryAccountNumber != null) {
filter &= payment.getBeneficiaryAccountNumber().equals(beneficiaryAccountNumber);
}
if (startDate != null) {
filter &= payment.getCreationDate().compareTo(startDate) >= 0;
filter &= !payment.getCreationDate().isBefore(startDate);
}
if (endDate != null) {
filter &= payment.getCreationDate().compareTo(endDate) <= 0;
filter &= !payment.getCreationDate().isAfter(endDate);
}
return filter;
}).collect(Collectors.toList());

View File

@@ -1,47 +1,44 @@
package net.kapcake.bankingservice.services;
import net.kapcake.bankingservice.model.domain.User;
import net.kapcake.bankingservice.model.dtos.UserUpdateDTO;
import net.kapcake.bankingservice.model.dtos.UserUpdateRequest;
import net.kapcake.bankingservice.repositories.UserRepository;
import net.kapcake.bankingservice.security.UserDetailsImpl;
import org.modelmapper.ModelMapper;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
private final ModelMapper modelMapper;
private final PasswordEncoder passwordEncoder;
public UserService(UserRepository userRepository, ModelMapper modelMapper, PasswordEncoder passwordEncoder) {
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.modelMapper = modelMapper;
this.passwordEncoder = passwordEncoder;
}
public boolean updateUser(UserDetailsImpl authenticatedUser, UserUpdateDTO userUpdateDTO) {
public boolean updateUser(UserDetailsImpl authenticatedUser, UserUpdateRequest userUpdateRequest) {
boolean needsLogout = false;
User user = userRepository.findByUsername(authenticatedUser.getUsername()).orElseThrow();
User.UserBuilder builder = user.toBuilder();
if (userUpdateDTO.getCountry() != null) {
builder.country(userUpdateDTO.getCountry());
if (userUpdateRequest.getCountry() != null) {
builder.country(userUpdateRequest.getCountry());
}
if (userUpdateDTO.getStreet() != null) {
builder.street(userUpdateDTO.getStreet());
if (userUpdateRequest.getStreet() != null) {
builder.street(userUpdateRequest.getStreet());
}
if (userUpdateDTO.getPostalCode() != null) {
builder.postalCode(userUpdateDTO.getPostalCode());
if (userUpdateRequest.getPostalCode() != null) {
builder.postalCode(userUpdateRequest.getPostalCode());
}
if (userUpdateDTO.getNumber() != null) {
builder.number(userUpdateDTO.getNumber());
if (userUpdateRequest.getNumber() != null) {
builder.number(userUpdateRequest.getNumber());
}
if (userUpdateDTO.getNumberExtension() != null) {
builder.numberExtension(userUpdateDTO.getNumberExtension());
if (userUpdateRequest.getNumberExtension() != null) {
builder.numberExtension(userUpdateRequest.getNumberExtension());
}
if (userUpdateDTO.getPassword() != null) {
builder.password(passwordEncoder.encode(userUpdateDTO.getPassword()));
if (userUpdateRequest.getPassword() != null) {
builder.password(passwordEncoder.encode(userUpdateRequest.getPassword()));
needsLogout = true;
}

View File

@@ -4,10 +4,10 @@ import net.kapcake.bankingservice.converters.PaymentConverter;
import net.kapcake.bankingservice.exceptions.AccessDeniedException;
import net.kapcake.bankingservice.exceptions.ResourceNotFoundException;
import net.kapcake.bankingservice.exceptions.ValidationException;
import net.kapcake.bankingservice.model.domain.Currency;
import net.kapcake.bankingservice.model.domain.*;
import net.kapcake.bankingservice.model.dtos.PaymentDTO;
import net.kapcake.bankingservice.model.dtos.PaymentFilter;
import net.kapcake.bankingservice.model.dtos.PaymentRequest;
import net.kapcake.bankingservice.repositories.BalanceRepository;
import net.kapcake.bankingservice.repositories.BankAccountRepository;
import net.kapcake.bankingservice.repositories.PaymentRepository;
@@ -21,7 +21,11 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.transaction.PlatformTransactionManager;
import java.math.BigDecimal;
import java.util.*;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.verify;
@@ -73,15 +77,16 @@ class PaymentServiceTest {
@Test
void createPayment() {
UserDetailsImpl authenticatedUser = new UserDetailsImpl(user1);
PaymentRequest paymentRequest = new PaymentRequest().setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(1L);
PaymentDTO paymentDTO = new PaymentDTO().setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(1L);
Payment payment = new Payment().setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(bankAccount1);
when(bankAccountRepository.findByAccountNumber(ACCOUNT_NUMBER_2)).thenReturn(Optional.of(bankAccount2));
when(paymentConverter.mapToEntity(paymentDTO)).thenReturn(payment);
when(paymentConverter.mapToEntity(paymentRequest)).thenReturn(payment);
when(paymentRepository.save(payment)).thenReturn(payment);
when(paymentConverter.mapToDTO(payment)).thenReturn(paymentDTO);
paymentService.createPayment(authenticatedUser, paymentDTO);
paymentService.createPayment(authenticatedUser, paymentRequest);
assertEquals(balance1.getAmount(), BigDecimal.valueOf(450));
assertEquals(balance2.getAmount(), BigDecimal.valueOf(550));
@@ -91,12 +96,12 @@ class PaymentServiceTest {
void getPaymentsForUser() {
List<BankAccount> bankAccounts = List.of(bankAccount1);
UserDetailsImpl authenticatedUser = new UserDetailsImpl(user1);
Payment payment1 = new Payment().setId(1L).setCreationDate(new GregorianCalendar(2023, Calendar.MAY, 11, 12, 0).getTime()).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(bankAccount1);
PaymentDTO paymentDTO1 = new PaymentDTO().setId(1L).setCreationDate(new GregorianCalendar(2023, Calendar.MAY, 11, 12, 0).getTime()).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(1L);
Payment payment2 = new Payment().setId(2L).setCreationDate(new GregorianCalendar(2023, Calendar.MAY, 11, 12, 1).getTime()).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(bankAccount1);
PaymentDTO paymentDTO2 = new PaymentDTO().setId(2L).setCreationDate(new GregorianCalendar(2023, Calendar.MAY, 11, 12, 1).getTime()).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(1L);
Payment payment3 = new Payment().setId(3L).setCreationDate(new GregorianCalendar(2023, Calendar.MAY, 11, 12, 2).getTime()).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_3).setGiverAccount(bankAccount1);
PaymentDTO paymentDTO3 = new PaymentDTO().setId(3L).setCreationDate(new GregorianCalendar(2023, Calendar.MAY, 11, 12, 2).getTime()).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_3).setGiverAccount(1L);
Payment payment1 = new Payment().setId(1L).setCreationDate(LocalDateTime.parse("2023-05-11T12:00")).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(bankAccount1);
PaymentDTO paymentDTO1 = new PaymentDTO().setId(1L).setCreationDate(LocalDateTime.parse("2023-05-11T12:00")).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(1L);
Payment payment2 = new Payment().setId(2L).setCreationDate(LocalDateTime.parse("2023-05-11T12:01")).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(bankAccount1);
PaymentDTO paymentDTO2 = new PaymentDTO().setId(2L).setCreationDate(LocalDateTime.parse("2023-05-11T12:01")).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_2).setGiverAccount(1L);
Payment payment3 = new Payment().setId(3L).setCreationDate(LocalDateTime.parse("2023-05-11T12:02")).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_3).setGiverAccount(bankAccount1);
PaymentDTO paymentDTO3 = new PaymentDTO().setId(3L).setCreationDate(LocalDateTime.parse("2023-05-11T12:02")).setAmount(BigDecimal.valueOf(50)).setCurrency(Currency.EUR).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_3).setGiverAccount(1L);
when(bankAccountRepository.findAllByUsers_username(TEST_USER_1)).thenReturn(bankAccounts);
when(paymentRepository.findAllByGiverAccountIn(bankAccounts)).thenReturn(Arrays.asList(payment1, payment2, payment3));
@@ -126,8 +131,8 @@ class PaymentServiceTest {
}
private void getPaymentsForUsers_withAllFilter(UserDetailsImpl authenticatedUser) {
Date startDate = new GregorianCalendar(2023, Calendar.MAY, 11, 12, 1).getTime();
Date endDate = new GregorianCalendar(2023, Calendar.MAY, 11, 12, 2).getTime();
final LocalDateTime startDate = LocalDateTime.parse("2023-05-11T12:01");
final LocalDateTime endDate = LocalDateTime.parse("2023-05-11T12:02");
PaymentFilter paymentFilter = new PaymentFilter().setStartDate(startDate).setEndDate(endDate).setBeneficiaryAccountNumber(ACCOUNT_NUMBER_3);
List<PaymentDTO> paymentsForUser = paymentService.getPaymentsForUser(authenticatedUser, paymentFilter);
@@ -136,7 +141,7 @@ class PaymentServiceTest {
}
private void getPaymentsForUsers_withStartAndEndDateFilter(UserDetailsImpl authenticatedUser) {
Date date = new GregorianCalendar(2023, Calendar.MAY, 11, 12, 1).getTime();
final LocalDateTime date = LocalDateTime.parse("2023-05-11T12:01");
PaymentFilter paymentFilter = new PaymentFilter().setStartDate(date).setEndDate(date);
List<PaymentDTO> paymentsForUser = paymentService.getPaymentsForUser(authenticatedUser, paymentFilter);
@@ -145,7 +150,8 @@ class PaymentServiceTest {
}
private void getPaymentsForUsers_withEndDateFilter(UserDetailsImpl authenticatedUser) {
PaymentFilter paymentFilter = new PaymentFilter().setEndDate(new GregorianCalendar(2023, Calendar.MAY, 11, 12, 1).getTime());
final LocalDateTime endDate = LocalDateTime.parse("2023-05-11T12:01");
PaymentFilter paymentFilter = new PaymentFilter().setEndDate(endDate);
List<PaymentDTO> paymentsForUser = paymentService.getPaymentsForUser(authenticatedUser, paymentFilter);
assertEquals(2, paymentsForUser.size());
@@ -154,7 +160,8 @@ class PaymentServiceTest {
}
private void getPaymentsForUsers_withStartDateFilter(UserDetailsImpl authenticatedUser) {
PaymentFilter paymentFilter = new PaymentFilter().setStartDate(new GregorianCalendar(2023, Calendar.MAY, 11, 12, 1).getTime());
final LocalDateTime startDate = LocalDateTime.parse("2023-05-11T12:01");
PaymentFilter paymentFilter = new PaymentFilter().setStartDate(startDate);
List<PaymentDTO> paymentsForUser = paymentService.getPaymentsForUser(authenticatedUser, paymentFilter);
assertEquals(2, paymentsForUser.size());
@@ -203,4 +210,4 @@ class PaymentServiceTest {
verify(paymentRepository).deleteById(1L);
}
}
}