From 48dc66d4389cf14fd345bab98c79dacc2c8cc14b Mon Sep 17 00:00:00 2001 From: Amr Elsayyad Date: Sun, 14 Jul 2024 22:03:49 +0300 Subject: [PATCH] feat: use **Project Lombok** and add support for login using email or account number (#26) --- pom.xml | 17 +- .../bankingportal/config/CacheConfig.java | 5 +- .../bankingportal/config/SwaggerConfig.java | 12 +- .../config/WebSecurityConfig.java | 21 +- .../controller/AccountController.java | 107 ++--- .../controller/AuthController.java | 126 +++--- .../controller/DashboardController.java | 34 +- .../controller/GlobalExceptionHandler.java | 8 +- .../controller/UserController.java | 97 +++-- .../bankingportal/dto/AccountResponse.java | 55 +-- .../bankingportal/dto/AmountRequest.java | 25 +- .../bankingportal/dto/ErrorResponse.java | 16 +- .../dto/FundTransferRequest.java | 39 +- .../dto/GeolocationResponse.java | 250 +---------- .../bankingportal/dto/LoginRequest.java | 28 +- .../webapp/bankingportal/dto/OtpRequest.java | 11 +- .../bankingportal/dto/OtpRequestv2.java | 13 - .../dto/OtpVerificationRequest.java | 21 +- .../dto/OtpVerificationRequestv2.java | 22 - .../webapp/bankingportal/dto/PinRequest.java | 27 +- .../bankingportal/dto/PinUpdateRequest.java | 33 +- .../bankingportal/dto/TransactionDTO.java | 72 ++-- .../bankingportal/dto/UserResponse.java | 93 +---- .../webapp/bankingportal/entity/Account.java | 88 +--- .../webapp/bankingportal/entity/OtpInfo.java | 45 +- .../entity/PasswordResetToken.java | 42 +- .../webapp/bankingportal/entity/Token.java | 49 +-- .../bankingportal/entity/Transaction.java | 85 +--- .../bankingportal/entity/TransactionType.java | 3 +- .../com/webapp/bankingportal/entity/User.java | 67 +-- .../mapper/TransactionMapper.java | 15 +- .../security/JwtAuthenticationEntryPoint.java | 38 +- .../security/JwtAuthenticationFilter.java | 45 +- .../service/AccountServiceImpl.java | 69 ++-- .../service/AuthServiceImpl.java | 22 +- .../service/DashboardServiceImpl.java | 30 +- .../service/EmailServiceImpl.java | 30 +- .../service/GeolocationServiceImpl.java | 24 +- .../bankingportal/service/OtpServiceImpl.java | 86 ++-- .../service/TokenServiceImpl.java | 53 +-- .../service/TransactionServiceImpl.java | 17 +- .../bankingportal/service/UserService.java | 5 + .../service/UserServiceImpl.java | 43 +- .../webapp/bankingportal/util/JsonUtil.java | 5 +- .../bankingportal/util/LoggedinUser.java | 14 +- .../bankingportal/util/ValidationUtil.java | 12 +- .../bankingportal/AccountControllerTests.java | 390 +++++------------- .../bankingportal/AccountServiceTests.java | 82 ++-- .../com/webapp/bankingportal/BaseTest.java | 84 ++-- .../bankingportal/DashboardServiceTests.java | 17 +- .../bankingportal/EmailServiceMock.java | 31 -- .../GreenMailJavaMailSender.java | 122 +++--- .../com/webapp/bankingportal/TestUtil.java | 183 -------- .../bankingportal/TokenServiceTests.java | 34 +- .../bankingportal/UserControllerTests.java | 208 ++++------ 55 files changed, 937 insertions(+), 2233 deletions(-) delete mode 100644 src/main/java/com/webapp/bankingportal/dto/OtpRequestv2.java delete mode 100644 src/main/java/com/webapp/bankingportal/dto/OtpVerificationRequestv2.java delete mode 100644 src/test/java/com/webapp/bankingportal/EmailServiceMock.java delete mode 100644 src/test/java/com/webapp/bankingportal/TestUtil.java diff --git a/pom.xml b/pom.xml index 16157e2..9c3fc99 100644 --- a/pom.xml +++ b/pom.xml @@ -19,9 +19,14 @@ - com.icegreen - greenmail-junit5 - 2.0.0-alpha-2 + org.projectlombok + lombok + 1.18.34 + + + com.icegreen + greenmail-junit5 + 2.0.0-alpha-2 com.googlecode.libphonenumber @@ -47,7 +52,6 @@ org.springframework.boot spring-boot-starter-data-jpa - org.springframework.boot spring-boot-starter-validation @@ -92,7 +96,6 @@ springdoc-openapi-starter-webmvc-ui 2.0.2 - org.springframework.boot spring-boot-starter-actuator @@ -120,7 +123,6 @@ spring-boot-starter-test test - org.springframework.boot spring-boot-devtools @@ -160,7 +162,4 @@ - - - \ No newline at end of file diff --git a/src/main/java/com/webapp/bankingportal/config/CacheConfig.java b/src/main/java/com/webapp/bankingportal/config/CacheConfig.java index 2e699cf..6aaee39 100644 --- a/src/main/java/com/webapp/bankingportal/config/CacheConfig.java +++ b/src/main/java/com/webapp/bankingportal/config/CacheConfig.java @@ -11,7 +11,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; - +import lombok.val; @Configuration @EnableCaching @@ -19,7 +19,7 @@ public class CacheConfig { @Bean public CacheManager cacheManager() { - CaffeineCacheManager cacheManager = new CaffeineCacheManager(); + val cacheManager = new CaffeineCacheManager(); cacheManager.setCacheNames(List.of("otpAttempts")); // Define the cache name cacheManager.setCaffeine(caffeineConfig()); return cacheManager; @@ -31,4 +31,5 @@ public Caffeine caffeineConfig() { .maximumSize(100) // Maximum of 100 entries in the cache .recordStats(); // For monitoring cache statistics (optional) } + } diff --git a/src/main/java/com/webapp/bankingportal/config/SwaggerConfig.java b/src/main/java/com/webapp/bankingportal/config/SwaggerConfig.java index 70ccb55..6d42e71 100644 --- a/src/main/java/com/webapp/bankingportal/config/SwaggerConfig.java +++ b/src/main/java/com/webapp/bankingportal/config/SwaggerConfig.java @@ -7,6 +7,9 @@ import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; + +import lombok.val; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -15,7 +18,7 @@ public class SwaggerConfig { @Bean public OpenAPI customOpenAPI() { - final String securitySchemeName = "Bearer"; + val securitySchemeName = "Bearer"; return new OpenAPI() .info(new Info().title("Banking Portal API") .description("This is auth service use for validate the user.") @@ -32,8 +35,7 @@ public OpenAPI customOpenAPI() { .name(securitySchemeName) .type(SecurityScheme.Type.HTTP) .scheme("bearer") - .bearerFormat("JWT") - ) - ); + .bearerFormat("JWT"))); } -} \ No newline at end of file + +} diff --git a/src/main/java/com/webapp/bankingportal/config/WebSecurityConfig.java b/src/main/java/com/webapp/bankingportal/config/WebSecurityConfig.java index 80440f3..7ac2bea 100644 --- a/src/main/java/com/webapp/bankingportal/config/WebSecurityConfig.java +++ b/src/main/java/com/webapp/bankingportal/config/WebSecurityConfig.java @@ -22,9 +22,12 @@ import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + @Configuration @EnableWebSecurity @EnableMethodSecurity +@RequiredArgsConstructor public class WebSecurityConfig { private static final String[] PUBLIC_URLS = { @@ -41,14 +44,9 @@ public class WebSecurityConfig { "/actuator/**" }; - @Autowired - private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; - - @Autowired - private JwtAuthenticationFilter jwtAuthenticationFilter; - - @Autowired - private TokenService tokenService; + private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + private final JwtAuthenticationFilter jwtAuthenticationFilter; + private final TokenService tokenService; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { @@ -56,19 +54,19 @@ public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception } @Bean - public PasswordEncoder passwordEncoder() { + PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean - public AuthenticationManager authenticationManager( + AuthenticationManager authenticationManager( AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf.disable()) .authorizeHttpRequests(requests -> requests .requestMatchers(PUBLIC_URLS).permitAll() @@ -90,4 +88,5 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti return http.build(); } + } diff --git a/src/main/java/com/webapp/bankingportal/controller/AccountController.java b/src/main/java/com/webapp/bankingportal/controller/AccountController.java index 221cbc3..fc63577 100644 --- a/src/main/java/com/webapp/bankingportal/controller/AccountController.java +++ b/src/main/java/com/webapp/bankingportal/controller/AccountController.java @@ -1,11 +1,5 @@ package com.webapp.bankingportal.controller; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -17,116 +11,99 @@ import com.webapp.bankingportal.dto.FundTransferRequest; import com.webapp.bankingportal.dto.PinRequest; import com.webapp.bankingportal.dto.PinUpdateRequest; -import com.webapp.bankingportal.dto.TransactionDTO; import com.webapp.bankingportal.service.AccountService; import com.webapp.bankingportal.service.TransactionService; +import com.webapp.bankingportal.util.JsonUtil; import com.webapp.bankingportal.util.LoggedinUser; +import lombok.RequiredArgsConstructor; +import lombok.val; + @RestController @RequestMapping("/api/account") +@RequiredArgsConstructor public class AccountController { - @Autowired - private AccountService accountService; - - @Autowired - private TransactionService transactionService; + private final AccountService accountService; + private final TransactionService transactionService; @GetMapping("/pin/check") - public ResponseEntity checkAccountPIN() { - boolean isPINValid = accountService.isPinCreated(LoggedinUser.getAccountNumber()); - - Map result = new HashMap<>(); - result.put("hasPIN", isPINValid); + public ResponseEntity checkAccountPIN() { + val isPINValid = accountService.isPinCreated(LoggedinUser.getAccountNumber()); + String response; if (isPINValid) { - result.put("msg", "PIN Created"); - + response = "{\"hasPIN\": true, \"msg\": \"PIN Created\"}"; } else { - result.put("msg", "PIN Not Created"); + response = "{\"hasPIN\": false, \"msg\": \"PIN Not Created\"}"; } - return new ResponseEntity<>(result, HttpStatus.OK); + return ResponseEntity.ok(response); } @PostMapping("/pin/create") - public ResponseEntity createPIN(@RequestBody PinRequest pinRequest) { + public ResponseEntity createPIN(@RequestBody PinRequest pinRequest) { accountService.createPin( LoggedinUser.getAccountNumber(), - pinRequest.getPassword(), - pinRequest.getPin()); - - Map response = new HashMap<>(); - response.put("msg", "PIN created successfully"); + pinRequest.password(), + pinRequest.pin()); - return new ResponseEntity<>(response, HttpStatus.OK); + return ResponseEntity.ok("{\"msg\": \"PIN created successfully\"}"); } @PostMapping("/pin/update") - public ResponseEntity updatePIN(@RequestBody PinUpdateRequest pinUpdateRequest) { + public ResponseEntity updatePIN(@RequestBody PinUpdateRequest pinUpdateRequest) { accountService.updatePin( LoggedinUser.getAccountNumber(), - pinUpdateRequest.getOldPin(), - pinUpdateRequest.getPassword(), - pinUpdateRequest.getNewPin()); + pinUpdateRequest.oldPin(), + pinUpdateRequest.password(), + pinUpdateRequest.newPin()); - Map response = new HashMap<>(); - response.put("msg", "PIN updated successfully"); - - return new ResponseEntity<>(response, HttpStatus.OK); + return ResponseEntity.ok("{\"msg\": \"PIN updated successfully\"}"); } @PostMapping("/deposit") - public ResponseEntity cashDeposit(@RequestBody AmountRequest amountRequest) { + public ResponseEntity cashDeposit(@RequestBody AmountRequest amountRequest) { accountService.cashDeposit( LoggedinUser.getAccountNumber(), - amountRequest.getPin(), - amountRequest.getAmount()); - - Map response = new HashMap<>(); - response.put("msg", "Cash deposited successfully"); + amountRequest.pin(), + amountRequest.amount()); - return new ResponseEntity<>(response, HttpStatus.OK); + return ResponseEntity.ok("{\"msg\": \"Cash deposited successfully\"}"); } @PostMapping("/withdraw") - public ResponseEntity cashWithdrawal(@RequestBody AmountRequest amountRequest) { + public ResponseEntity cashWithdrawal(@RequestBody AmountRequest amountRequest) { accountService.cashWithdrawal( LoggedinUser.getAccountNumber(), - amountRequest.getPin(), - amountRequest.getAmount()); - - Map response = new HashMap<>(); - response.put("msg", "Cash withdrawn successfully"); + amountRequest.pin(), + amountRequest.amount()); - return new ResponseEntity<>(response, HttpStatus.OK); + return ResponseEntity.ok("{\"msg\": \"Cash withdrawn successfully\"}"); } @PostMapping("/fund-transfer") - public ResponseEntity fundTransfer(@RequestBody FundTransferRequest fundTransferRequest) { + public ResponseEntity fundTransfer(@RequestBody FundTransferRequest fundTransferRequest) { if (LoggedinUser.getAccountNumber() - .equals(fundTransferRequest.getTargetAccountNumber())) { - return new ResponseEntity<>( - "Source and target account cannot be the same", - HttpStatus.BAD_REQUEST); + .equals(fundTransferRequest.targetAccountNumber())) { + return ResponseEntity.badRequest() + .body("Source and target account cannot be the same"); } accountService.fundTransfer( LoggedinUser.getAccountNumber(), - fundTransferRequest.getTargetAccountNumber(), - fundTransferRequest.getPin(), - fundTransferRequest.getAmount()); + fundTransferRequest.targetAccountNumber(), + fundTransferRequest.pin(), + fundTransferRequest.amount()); - Map response = new HashMap<>(); - response.put("msg", "Fund transferred successfully"); - - return new ResponseEntity<>(response, HttpStatus.OK); + return ResponseEntity.ok("{\"msg\": \"Fund transferred successfully\"}"); } @GetMapping("/transactions") - public ResponseEntity> getAllTransactionsByAccountNumber() { - List transactions = transactionService + public ResponseEntity getAllTransactionsByAccountNumber() { + val transactions = transactionService .getAllTransactionsByAccountNumber(LoggedinUser.getAccountNumber()); - return ResponseEntity.ok(transactions); + return ResponseEntity.ok(JsonUtil.toJson(transactions)); } + } diff --git a/src/main/java/com/webapp/bankingportal/controller/AuthController.java b/src/main/java/com/webapp/bankingportal/controller/AuthController.java index 3330144..cff6e2e 100644 --- a/src/main/java/com/webapp/bankingportal/controller/AuthController.java +++ b/src/main/java/com/webapp/bankingportal/controller/AuthController.java @@ -1,29 +1,25 @@ package com.webapp.bankingportal.controller; -import java.util.concurrent.CompletableFuture; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import com.webapp.bankingportal.dto.OtpRequestv2; -import com.webapp.bankingportal.dto.OtpVerificationRequestv2; +import com.webapp.bankingportal.dto.OtpRequest; +import com.webapp.bankingportal.dto.OtpVerificationRequest; import com.webapp.bankingportal.dto.ResetPasswordRequest; -import com.webapp.bankingportal.entity.User; +import com.webapp.bankingportal.exception.UnauthorizedException; import com.webapp.bankingportal.service.AuthService; import com.webapp.bankingportal.service.OtpService; import com.webapp.bankingportal.service.UserService; -import com.webapp.bankingportal.util.ValidationUtil; + +import lombok.val; +import lombok.extern.slf4j.Slf4j; @RestController @RequestMapping("/api/auth") +@Slf4j public class AuthController { - private static final Logger logger = LoggerFactory.getLogger(AuthController.class); - private final OtpService otpService; private final UserService userService; private final AuthService authService; @@ -35,39 +31,27 @@ public AuthController(OtpService otpService, UserService userService, AuthServic } @PostMapping("/password-reset/send-otp") - public ResponseEntity sendOtpForPasswordReset(@RequestBody OtpRequestv2 otpRequest) { - final String identifier = otpRequest.getIdentifier(); - logger.info("Received OTP request for identifier: {}", identifier); - - User user; - if (ValidationUtil.isValidEmail(identifier)) { - user = userService.getUserByEmail(identifier).get(); - } else if (ValidationUtil.isValidAccountNumber(identifier)) { - user = userService.getUserByAccountNumber(identifier).get(); - } else { - logger.warn("Invalid identifier provided: {}", identifier); - return ResponseEntity.badRequest().body("Invalid identifier provided"); - } + public ResponseEntity sendOtpForPasswordReset(@RequestBody OtpRequest otpRequest) { + val identifier = otpRequest.identifier(); + log.info("Received OTP request for identifier: {}", identifier); - if (user == null) { - logger.warn("User not found for identifier: {}", identifier); - return ResponseEntity.badRequest().body("User not found for the given identifier"); - } + val user = userService.getUserByIdentifier(identifier) + .orElseThrow(() -> new UnauthorizedException("User not found for the given identifier")); // Generate and send OTP - final String accountNumber = user.getAccount().getAccountNumber(); - logger.info("Generating OTP for account number: {}", accountNumber); - final String generatedOtp = otpService.generateOTP(accountNumber); - final CompletableFuture emailSendingFuture = otpService.sendOTPByEmail( + val accountNumber = user.getAccount().getAccountNumber(); + log.info("Generating OTP for account number: {}", accountNumber); + val generatedOtp = otpService.generateOTP(accountNumber); + val emailSendingFuture = otpService.sendOTPByEmail( user.getEmail(), user.getName(), accountNumber, generatedOtp); - final ResponseEntity successResponse = ResponseEntity + val successResponse = ResponseEntity .ok(String.format("{\"message\": \"OTP sent successfully to: %s\"}", user.getEmail())); - final ResponseEntity failureResponse = ResponseEntity.internalServerError() + val failureResponse = ResponseEntity.internalServerError() .body(String.format("{\"message\": \"Failed to send OTP to: %s\"}", user.getEmail())); return emailSendingFuture.thenApply(result -> successResponse) @@ -76,42 +60,31 @@ public ResponseEntity sendOtpForPasswordReset(@RequestBody OtpRequestv2 @PostMapping("/password-reset/verify-otp") public ResponseEntity verifyOtpAndIssueResetToken( - @RequestBody OtpVerificationRequestv2 otpVerificationRequest) { - String identifier = otpVerificationRequest.getIdentifier(); - String otp = otpVerificationRequest.getOtp(); - logger.info("Received OTP verification request for identifier: {}", identifier); + @RequestBody OtpVerificationRequest otpVerificationRequest) { + val identifier = otpVerificationRequest.identifier(); + val otp = otpVerificationRequest.otp(); + log.info("Received OTP verification request for identifier: {}", identifier); if (identifier == null || identifier.isEmpty()) { - logger.warn("Missing identifier in OTP verification request"); + log.warn("Missing identifier in OTP verification request"); return ResponseEntity.badRequest().body("Missing account number"); } if (otp == null || otp.isEmpty()) { - logger.warn("Missing OTP in OTP verification request"); + log.warn("Missing OTP in OTP verification request"); return ResponseEntity.badRequest().body("Missing OTP"); } - User user; - if (ValidationUtil.isValidEmail(identifier)) { - user = userService.getUserByEmail(identifier).get(); - } else if (ValidationUtil.isValidAccountNumber(identifier)) { - user = userService.getUserByAccountNumber(identifier).get(); - } else { - logger.warn("Invalid identifier provided: {}", identifier); - return ResponseEntity.badRequest().body("Invalid identifier provided"); - } + val user = userService.getUserByIdentifier(identifier) + .orElseThrow(() -> new UnauthorizedException("User not found for the given identifier")); - if (user == null) { - logger.warn("User not found for identifier: {}", identifier); - return ResponseEntity.badRequest().body("User not found for the given identifier"); - } - String accountNumber = user.getAccount().getAccountNumber(); - logger.info("Validating OTP for account number: {}", accountNumber); + val accountNumber = user.getAccount().getAccountNumber(); + log.info("Validating OTP for account number: {}", accountNumber); // Validate OTP - boolean isValidOtp = otpService.validateOTP(accountNumber, otp); + val isValidOtp = otpService.validateOTP(accountNumber, otp); if (!isValidOtp) { - logger.warn("Invalid OTP provided for account number: {}", accountNumber); + log.warn("Invalid OTP provided for account number: {}", accountNumber); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid OTP"); } @@ -119,36 +92,28 @@ public ResponseEntity verifyOtpAndIssueResetToken( try { resetToken = authService.generatePasswordResetToken(user); } catch (IllegalArgumentException e) { - logger.error("Error generating password reset token: {}", e.getMessage()); + log.error("Error generating password reset token: {}", e.getMessage()); return ResponseEntity.badRequest().body(e.getMessage()); } - String jsonResponse = "{\"passwordResetToken\": \"" + resetToken + "\"}"; - logger.info("Password reset token issued successfully for user: {}", user.getId()); + log.info("Password reset token issued successfully for account: {}", accountNumber); - return ResponseEntity.ok(jsonResponse.toString()); + return ResponseEntity.ok("{\"passwordResetToken\": \"" + resetToken + "\"}"); } @PostMapping("/password-reset") public ResponseEntity resetPassword(@RequestBody ResetPasswordRequest resetPasswordRequest) { - String identifier = resetPasswordRequest.identifier(); - String resetToken = resetPasswordRequest.resetToken(); - String newPassword = resetPasswordRequest.newPassword(); - logger.info("Received password reset request for identifier: {}", identifier); - - User user; - if (ValidationUtil.isValidEmail(identifier)) { - user = userService.getUserByEmail(identifier).get(); - } else if (ValidationUtil.isValidAccountNumber(identifier)) { - user = userService.getUserByAccountNumber(identifier).get(); - } else { - logger.warn("Invalid identifier provided: {}", identifier); - return ResponseEntity.badRequest().body("Invalid identifier provided"); - } + val identifier = resetPasswordRequest.identifier(); + val resetToken = resetPasswordRequest.resetToken(); + val newPassword = resetPasswordRequest.newPassword(); + log.info("Received password reset request for identifier: {}", identifier); + + val user = userService.getUserByIdentifier(identifier) + .orElseThrow(() -> new UnauthorizedException("User not found for the given identifier")); - boolean isValidResetToken = authService.verifyPasswordResetToken(resetToken, user); + val isValidResetToken = authService.verifyPasswordResetToken(resetToken, user); if (!isValidResetToken) { - logger.warn("Invalid reset token provided for user: {}", user.getId()); + log.warn("Invalid reset token provided for user: {}", user.getId()); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid reset token"); } @@ -157,16 +122,17 @@ public ResponseEntity resetPassword(@RequestBody ResetPasswordRequest re try { passwordResetSuccessful = userService.resetPassword(user, newPassword); } catch (Exception e) { - logger.error("Error resetting password for user: {}", user.getId(), e); + log.error("Error resetting password for user: {}", user.getId(), e); return ResponseEntity.internalServerError().body("Failed to reset password"); } if (passwordResetSuccessful) { - logger.info("Password reset successfully for user: {}", user.getId()); + log.info("Password reset successfully for user: {}", user.getId()); return ResponseEntity.status(HttpStatus.OK).body("{\"message\": \"Password reset successfully\"}"); } else { - logger.error("Failed to reset password for user: {}", user.getId()); + log.error("Failed to reset password for user: {}", user.getId()); return ResponseEntity.internalServerError().body("Failed to reset password"); } } + } diff --git a/src/main/java/com/webapp/bankingportal/controller/DashboardController.java b/src/main/java/com/webapp/bankingportal/controller/DashboardController.java index 85bc022..658e0e7 100644 --- a/src/main/java/com/webapp/bankingportal/controller/DashboardController.java +++ b/src/main/java/com/webapp/bankingportal/controller/DashboardController.java @@ -1,39 +1,37 @@ package com.webapp.bankingportal.controller; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.webapp.bankingportal.dto.AccountResponse; -import com.webapp.bankingportal.dto.UserResponse; - import com.webapp.bankingportal.service.DashboardService; +import com.webapp.bankingportal.util.JsonUtil; import com.webapp.bankingportal.util.LoggedinUser; +import lombok.RequiredArgsConstructor; +import lombok.val; + @RestController @RequestMapping("/api/dashboard") +@RequiredArgsConstructor public class DashboardController { - @Autowired - private DashboardService dashboardService; + private final DashboardService dashboardService; @GetMapping("/user") - public ResponseEntity getUserDetails() { - String accountNumber = LoggedinUser.getAccountNumber(); - UserResponse userResponse = dashboardService.getUserDetails(accountNumber); - return ResponseEntity.ok(userResponse); + public ResponseEntity getUserDetails() { + val accountNumber = LoggedinUser.getAccountNumber(); + val userResponse = dashboardService.getUserDetails(accountNumber); + return ResponseEntity.ok(JsonUtil.toJson(userResponse)); } @GetMapping("/account") - public ResponseEntity getAccountDetails() { - String accountNumber = LoggedinUser.getAccountNumber(); - AccountResponse accountResponse = dashboardService.getAccountDetails(accountNumber); - return ResponseEntity.ok(accountResponse); + public ResponseEntity getAccountDetails() { + val accountNumber = LoggedinUser.getAccountNumber(); + val accountResponse = dashboardService.getAccountDetails(accountNumber); + return ResponseEntity.ok(JsonUtil.toJson(accountResponse)); } - - - -} \ No newline at end of file + +} diff --git a/src/main/java/com/webapp/bankingportal/controller/GlobalExceptionHandler.java b/src/main/java/com/webapp/bankingportal/controller/GlobalExceptionHandler.java index 6c61410..6236f4a 100644 --- a/src/main/java/com/webapp/bankingportal/controller/GlobalExceptionHandler.java +++ b/src/main/java/com/webapp/bankingportal/controller/GlobalExceptionHandler.java @@ -100,13 +100,13 @@ public ResponseEntity handleUserInvalidException(UserInvalidException ex @ExceptionHandler(PasswordResetException.class) public ResponseEntity handlePasswordResetException(PasswordResetException ex, WebRequest request) { - String errorMessage = ex.getMessage(); - return new ResponseEntity<>("{\"message\": \"" + errorMessage + "\"}", HttpStatus.INTERNAL_SERVER_ERROR); + return ResponseEntity.internalServerError() + .body("{\"message\": \"" + ex.getMessage() + "\"}"); } @ExceptionHandler(Exception.class) public ResponseEntity handleGlobalException(Exception ex, WebRequest request) { - String errorMessage = "An unexpected error occurred: " + ex.getMessage(); - return new ResponseEntity<>("{\"message\": \"" + errorMessage + "\"}", HttpStatus.INTERNAL_SERVER_ERROR); + return ResponseEntity.internalServerError() + .body("{\"message\": \"An unexpected error occurred: " + ex.getMessage() + "\"}"); } } diff --git a/src/main/java/com/webapp/bankingportal/controller/UserController.java b/src/main/java/com/webapp/bankingportal/controller/UserController.java index 4272725..bcca2c8 100644 --- a/src/main/java/com/webapp/bankingportal/controller/UserController.java +++ b/src/main/java/com/webapp/bankingportal/controller/UserController.java @@ -1,15 +1,9 @@ package com.webapp.bankingportal.controller; -import java.util.concurrent.CompletableFuture; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -25,6 +19,7 @@ import com.webapp.bankingportal.dto.UserResponse; import com.webapp.bankingportal.entity.User; import com.webapp.bankingportal.exception.InvalidTokenException; +import com.webapp.bankingportal.exception.UnauthorizedException; import com.webapp.bankingportal.service.TokenService; import com.webapp.bankingportal.service.OtpService; import com.webapp.bankingportal.service.UserService; @@ -33,8 +28,12 @@ import jakarta.servlet.http.HttpServletRequest; +import lombok.val; +import lombok.extern.slf4j.Slf4j; + @RestController @RequestMapping("/api/users") +@Slf4j public class UserController { private final AuthenticationManager authenticationManager; @@ -43,9 +42,6 @@ public class UserController { private final UserService userService; private final OtpService otpService; - private static final Logger logger = LoggerFactory.getLogger( - UserController.class); - public UserController( UserService userService, AuthenticationManager authenticationManager, @@ -62,63 +58,69 @@ public UserController( @PostMapping("/register") public ResponseEntity registerUser(@RequestBody User user) { - User registeredUser = userService.registerUser(user); - UserResponse userResponse = new UserResponse(registeredUser); + val registeredUser = userService.registerUser(user); + val userResponse = new UserResponse(registeredUser); - return ResponseEntity.ok(userResponse.toString()); + return ResponseEntity.ok(JsonUtil.toJson(userResponse)); } @PostMapping("/login") public ResponseEntity login(@RequestBody LoginRequest loginRequest, HttpServletRequest request) throws InvalidTokenException { - final String accountNumber = loginRequest.getAccountNumber(); + val identifier = loginRequest.identifier(); + log.info("Received login request for identifier: {}", identifier); - logger.info("Authenticating Account: {}", accountNumber); + val user = userService.getUserByIdentifier(identifier) + .orElseThrow(() -> new UnauthorizedException("User not found for the given identifier")); + + val accountNumber = user.getAccount().getAccountNumber(); + + log.info("Authenticating Account: {}", accountNumber); authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( accountNumber, - loginRequest.getPassword())); + loginRequest.password())); - logger.info("Account: {} authenticated successfully", accountNumber); + log.info("Account: {} authenticated successfully", accountNumber); userService.sendLoginNotificationEmail( userService.getUserByAccountNumber(accountNumber).get(), request.getRemoteAddr()); - final UserDetails userDetails = userDetailsService + val userDetails = userDetailsService .loadUserByUsername(accountNumber); - final String token = tokenService.generateToken(userDetails); + val token = tokenService.generateToken(userDetails); tokenService.saveToken(token); - logger.info("Account: {} logged in successfully", accountNumber); + log.info("Account: {} logged in successfully", accountNumber); return ResponseEntity.ok("{ \"token\": \"" + token + "\" }"); } @PostMapping("/generate-otp") public ResponseEntity generateOtp(@RequestBody OtpRequest otpRequest) { - final String accountNumber = otpRequest.getAccountNumber(); - if (!userService.doesAccountExist(accountNumber)) { - return ResponseEntity.badRequest() - .body("User not found for the given account number"); - } + val identifier = otpRequest.identifier(); + log.info("Received OTP request for identifier: {}", identifier); - final User user = userService.getUserByAccountNumber(accountNumber).get(); + val user = userService.getUserByIdentifier(identifier) + .orElseThrow(() -> new UnauthorizedException("User not found for the given identifier")); // Generate and send OTP - final String generatedOtp = otpService.generateOTP(accountNumber); - final CompletableFuture emailSendingFuture = otpService.sendOTPByEmail( + val accountNumber = user.getAccount().getAccountNumber(); + log.info("Generating OTP for account number: {}", accountNumber); + val generatedOtp = otpService.generateOTP(accountNumber); + val emailSendingFuture = otpService.sendOTPByEmail( user.getEmail(), user.getName(), accountNumber, generatedOtp); - final ResponseEntity successResponse = ResponseEntity + val successResponse = ResponseEntity .ok(String.format("{\"message\": \"OTP sent successfully to: %s\"}", user.getEmail())); - final ResponseEntity failureResponse = ResponseEntity.internalServerError() + val failureResponse = ResponseEntity.internalServerError() .body(String.format("{\"message\": \"Failed to send OTP to: %s\"}", user.getEmail())); return emailSendingFuture.thenApply(result -> successResponse) @@ -130,27 +132,36 @@ public ResponseEntity verifyOtpAndLogin( @RequestBody OtpVerificationRequest otpVerificationRequest) throws InvalidTokenException { - String accountNumber = otpVerificationRequest.getAccountNumber(); - String otp = otpVerificationRequest.getOtp(); + val identifier = otpVerificationRequest.identifier(); + val otp = otpVerificationRequest.otp(); + log.info("Received OTP verification request for identifier: {}", identifier); - if (accountNumber == null || accountNumber.isEmpty()) { + if (identifier == null || identifier.isEmpty()) { + log.warn("Missing identifier in OTP verification request"); return ResponseEntity.badRequest().body("Missing account number"); } if (otp == null || otp.isEmpty()) { + log.warn("Missing OTP in OTP verification request"); return ResponseEntity.badRequest().body("Missing OTP"); } + val user = userService.getUserByIdentifier(identifier) + .orElseThrow(() -> new UnauthorizedException("User not found for the given identifier")); + + val accountNumber = user.getAccount().getAccountNumber(); + log.info("Validating OTP for account number: {}", accountNumber); + // Validate OTP against the stored OTP in the database - boolean isValidOtp = otpService.validateOTP(accountNumber, otp); + val isValidOtp = otpService.validateOTP(accountNumber, otp); if (!isValidOtp) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED) .body("OTP has expired"); } // If OTP is valid, generate and return a token - UserDetails userDetails = userDetailsService.loadUserByUsername(accountNumber); - String token = tokenService.generateToken(userDetails); + val userDetails = userDetailsService.loadUserByUsername(accountNumber); + val token = tokenService.generateToken(userDetails); tokenService.saveToken(token); return ResponseEntity.ok("{ \"token\": \"" + token + "\" }"); @@ -158,20 +169,20 @@ public ResponseEntity verifyOtpAndLogin( @PostMapping("/update") public ResponseEntity updateUser(@RequestBody User user) { - String accountNumber = LoggedinUser.getAccountNumber(); + val accountNumber = LoggedinUser.getAccountNumber(); - logger.info("Authenticating account: {} ...", accountNumber); + log.info("Authenticating account: {} ...", accountNumber); authenticationManager.authenticate(new UsernamePasswordAuthenticationToken( accountNumber, user.getPassword())); - logger.info("Account: {} authenticated successfully", accountNumber); + log.info("Account: {} authenticated successfully", accountNumber); - logger.info("Updating account: {} ...", accountNumber); - User updatedUser = userService.updateUser(user); + log.info("Updating account: {} ...", accountNumber); + val updatedUser = userService.updateUser(user); - logger.info("Account: {} is updated successfully", accountNumber); + log.info("Account: {} is updated successfully", accountNumber); - UserResponse userResponse = new UserResponse(updatedUser); + val userResponse = new UserResponse(updatedUser); return ResponseEntity.ok(JsonUtil.toJson(userResponse)); } @@ -184,7 +195,7 @@ public ModelAndView logout(@RequestHeader("Authorization") String token) tokenService.validateToken(token); tokenService.invalidateToken(token); - logger.info("User logged out successfully {}", + log.info("User logged out successfully {}", tokenService.getUsernameFromToken(token)); return new ModelAndView("redirect:/logout"); diff --git a/src/main/java/com/webapp/bankingportal/dto/AccountResponse.java b/src/main/java/com/webapp/bankingportal/dto/AccountResponse.java index 712386c..e9a314d 100644 --- a/src/main/java/com/webapp/bankingportal/dto/AccountResponse.java +++ b/src/main/java/com/webapp/bankingportal/dto/AccountResponse.java @@ -1,51 +1,28 @@ package com.webapp.bankingportal.dto; +import com.webapp.bankingportal.entity.Account; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor public class AccountResponse { + private String accountNumber; private double balance; private String accountType; private String branch; private String ifscCode; - // Getters and setters - - public String getAccountNumber() { - return accountNumber; - } - - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; + public AccountResponse(Account account) { + this.accountNumber = account.getAccountNumber(); + this.balance = account.getBalance(); + this.accountType = account.getAccountType(); + this.branch = account.getBranch(); + this.ifscCode = account.getIfscCode(); } - public double getBalance() { - return balance; - } - - public void setBalance(double balance) { - this.balance = balance; - } - - public String getAccountType() { - return accountType; - } - - public void setAccountType(String accountType) { - this.accountType = accountType; - } - - public String getBranch() { - return branch; - } - - public void setBranch(String branch) { - this.branch = branch; - } - - public String getIfscCode() { - return ifscCode; - } - - public void setIfscCode(String ifscCode) { - this.ifscCode =ifscCode; - } } diff --git a/src/main/java/com/webapp/bankingportal/dto/AmountRequest.java b/src/main/java/com/webapp/bankingportal/dto/AmountRequest.java index 0c824d1..3b1e98f 100644 --- a/src/main/java/com/webapp/bankingportal/dto/AmountRequest.java +++ b/src/main/java/com/webapp/bankingportal/dto/AmountRequest.java @@ -1,27 +1,4 @@ package com.webapp.bankingportal.dto; -public class AmountRequest { - private String accountNumber; - private String pin; - private double amount; - public String getAccountNumber() { - return accountNumber; - } - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; - } - public String getPin() { - return pin; - } - public void setPin(String pin) { - this.pin = pin; - } - public double getAmount() { - return amount; - } - public void setAmount(double amount) { - this.amount = amount; - } - - +public record AmountRequest(String accountNumber, String pin, double amount) { } diff --git a/src/main/java/com/webapp/bankingportal/dto/ErrorResponse.java b/src/main/java/com/webapp/bankingportal/dto/ErrorResponse.java index ea7a312..e312a5e 100644 --- a/src/main/java/com/webapp/bankingportal/dto/ErrorResponse.java +++ b/src/main/java/com/webapp/bankingportal/dto/ErrorResponse.java @@ -1,18 +1,4 @@ package com.webapp.bankingportal.dto; -public class ErrorResponse { - - private String message; - - public ErrorResponse(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } +public record ErrorResponse(String message) { } diff --git a/src/main/java/com/webapp/bankingportal/dto/FundTransferRequest.java b/src/main/java/com/webapp/bankingportal/dto/FundTransferRequest.java index 5e746f3..dc4a983 100644 --- a/src/main/java/com/webapp/bankingportal/dto/FundTransferRequest.java +++ b/src/main/java/com/webapp/bankingportal/dto/FundTransferRequest.java @@ -1,41 +1,4 @@ package com.webapp.bankingportal.dto; -public class FundTransferRequest { - - private String sourceAccountNumber; - private String targetAccountNumber; - private double amount; - private String pin; - - public String getSourceAccountNumber() { - return sourceAccountNumber; - } - - public void setSourceAccountNumber(String sourceAccountNumber) { - this.sourceAccountNumber = sourceAccountNumber; - } - - public String getTargetAccountNumber() { - return targetAccountNumber; - } - - public void setTargetAccountNumber(String targetAccountNumber) { - this.targetAccountNumber = targetAccountNumber; - } - - public double getAmount() { - return amount; - } - - public void setAmount(double amount) { - this.amount = amount; - } - - public String getPin() { - return pin; - } - - public void setPin(String pin) { - this.pin = pin; - } +public record FundTransferRequest(String sourceAccountNumber, String targetAccountNumber, double amount, String pin) { } diff --git a/src/main/java/com/webapp/bankingportal/dto/GeolocationResponse.java b/src/main/java/com/webapp/bankingportal/dto/GeolocationResponse.java index 4058561..7a8c33f 100644 --- a/src/main/java/com/webapp/bankingportal/dto/GeolocationResponse.java +++ b/src/main/java/com/webapp/bankingportal/dto/GeolocationResponse.java @@ -1,36 +1,28 @@ package com.webapp.bankingportal.dto; import com.fasterxml.jackson.annotation.JsonProperty; + +import lombok.Data; + import java.util.Map; import java.util.List; +@Data public class GeolocationResponse { + @Data public static class City { + @JsonProperty("geoname_id") private int geonameId; private Map names; - // Getters and setters - public int getGeonameId() { - return geonameId; - } - - public void setGeonameId(int geonameId) { - this.geonameId = geonameId; - } - - public Map getNames() { - return names; - } - - public void setNames(Map names) { - this.names = names; - } } + @Data public static class Continent { + private String code; @JsonProperty("geoname_id") @@ -38,33 +30,11 @@ public static class Continent { private Map names; - // Getters and setters - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public int getGeonameId() { - return geonameId; - } - - public void setGeonameId(int geonameId) { - this.geonameId = geonameId; - } - - public Map getNames() { - return names; - } - - public void setNames(Map names) { - this.names = names; - } } + @Data public static class Country { + @JsonProperty("geoname_id") private int geonameId; @@ -76,41 +46,11 @@ public static class Country { private Map names; - // Getters and setters - public int getGeonameId() { - return geonameId; - } - - public void setGeonameId(int geonameId) { - this.geonameId = geonameId; - } - - public boolean isInEuropeanUnion() { - return isInEuropeanUnion; - } - - public void setInEuropeanUnion(boolean isInEuropeanUnion) { - this.isInEuropeanUnion = isInEuropeanUnion; - } - - public String getIsoCode() { - return isoCode; - } - - public void setIsoCode(String isoCode) { - this.isoCode = isoCode; - } - - public Map getNames() { - return names; - } - - public void setNames(Map names) { - this.names = names; - } } + @Data public static class Location { + private double latitude; private double longitude; @@ -120,54 +60,18 @@ public static class Location { @JsonProperty("weather_code") private String weatherCode; - // Getters and setters - public double getLatitude() { - return latitude; - } - - public void setLatitude(double latitude) { - this.latitude = latitude; - } - - public double getLongitude() { - return longitude; - } - - public void setLongitude(double longitude) { - this.longitude = longitude; - } - - public String getTimeZone() { - return timeZone; - } - - public void setTimeZone(String timeZone) { - this.timeZone = timeZone; - } - - public String getWeatherCode() { - return weatherCode; - } - - public void setWeatherCode(String weatherCode) { - this.weatherCode = weatherCode; - } } + @Data public static class Postal { - private String code; - // Getters and setters - public String getCode() { - return code; - } + private String code; - public void setCode(String code) { - this.code = code; - } } + @Data public static class Subdivision { + @JsonProperty("geoname_id") private int geonameId; @@ -176,33 +80,11 @@ public static class Subdivision { private Map names; - // Getters and setters - public int getGeonameId() { - return geonameId; - } - - public void setGeonameId(int geonameId) { - this.geonameId = geonameId; - } - - public String getIsoCode() { - return isoCode; - } - - public void setIsoCode(String isoCode) { - this.isoCode = isoCode; - } - - public Map getNames() { - return names; - } - - public void setNames(Map names) { - this.names = names; - } } + @Data public static class Traits { + @JsonProperty("autonomous_system_number") private int autonomousSystemNumber; @@ -217,46 +99,6 @@ public static class Traits { @JsonProperty("user_type") private String userType; - // Getters and setters - public int getAutonomousSystemNumber() { - return autonomousSystemNumber; - } - - public void setAutonomousSystemNumber(int autonomousSystemNumber) { - this.autonomousSystemNumber = autonomousSystemNumber; - } - - public String getAutonomousSystemOrganization() { - return autonomousSystemOrganization; - } - - public void setAutonomousSystemOrganization(String autonomousSystemOrganization) { - this.autonomousSystemOrganization = autonomousSystemOrganization; - } - - public String getConnectionType() { - return connectionType; - } - - public void setConnectionType(String connectionType) { - this.connectionType = connectionType; - } - - public String getIsp() { - return isp; - } - - public void setIsp(String isp) { - this.isp = isp; - } - - public String getUserType() { - return userType; - } - - public void setUserType(String userType) { - this.userType = userType; - } } private City city; @@ -267,60 +109,4 @@ public void setUserType(String userType) { private List subdivisions; private Traits traits; - // Getters and setters - public City getCity() { - return city; - } - - public void setCity(City city) { - this.city = city; - } - - public Continent getContinent() { - return continent; - } - - public void setContinent(Continent continent) { - this.continent = continent; - } - - public Country getCountry() { - return country; - } - - public void setCountry(Country country) { - this.country = country; - } - - public Location getLocation() { - return location; - } - - public void setLocation(Location location) { - this.location = location; - } - - public Postal getPostal() { - return postal; - } - - public void setPostal(Postal postal) { - this.postal = postal; - } - - public List getSubdivisions() { - return subdivisions; - } - - public void setSubdivisions(List subdivisions) { - this.subdivisions = subdivisions; - } - - public Traits getTraits() { - return traits; - } - - public void setTraits(Traits traits) { - this.traits = traits; - } } diff --git a/src/main/java/com/webapp/bankingportal/dto/LoginRequest.java b/src/main/java/com/webapp/bankingportal/dto/LoginRequest.java index f3e5f91..c9e689a 100644 --- a/src/main/java/com/webapp/bankingportal/dto/LoginRequest.java +++ b/src/main/java/com/webapp/bankingportal/dto/LoginRequest.java @@ -1,28 +1,4 @@ package com.webapp.bankingportal.dto; -public class LoginRequest { - private String accountNumber; - private String password; - private boolean useOtp; - - public String getAccountNumber() { - return accountNumber; - } - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; - } - public String getPassword() { - return password; - } - public void setPassword(String password) { - this.password = password; - } - public boolean isUseOtp() { - return useOtp; - } - public void setUseOtp(boolean useOtp) { - this.useOtp = useOtp; - } - - -} \ No newline at end of file +public record LoginRequest(String identifier, String password, boolean useOtp) { +} diff --git a/src/main/java/com/webapp/bankingportal/dto/OtpRequest.java b/src/main/java/com/webapp/bankingportal/dto/OtpRequest.java index 4144131..2e6401b 100644 --- a/src/main/java/com/webapp/bankingportal/dto/OtpRequest.java +++ b/src/main/java/com/webapp/bankingportal/dto/OtpRequest.java @@ -1,13 +1,4 @@ package com.webapp.bankingportal.dto; -public class OtpRequest { - private String accountNumber; - - public String getAccountNumber() { - return accountNumber; - } - - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; - } +public record OtpRequest(String identifier) { } diff --git a/src/main/java/com/webapp/bankingportal/dto/OtpRequestv2.java b/src/main/java/com/webapp/bankingportal/dto/OtpRequestv2.java deleted file mode 100644 index a788f40..0000000 --- a/src/main/java/com/webapp/bankingportal/dto/OtpRequestv2.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.webapp.bankingportal.dto; - -public class OtpRequestv2 { - private String identifier; // Can be either email or account number - - public String getIdentifier() { - return identifier; - } - - public void setIdentifier(String identifier) { - this.identifier = identifier; - } -} diff --git a/src/main/java/com/webapp/bankingportal/dto/OtpVerificationRequest.java b/src/main/java/com/webapp/bankingportal/dto/OtpVerificationRequest.java index 7bb60ad..d091f14 100644 --- a/src/main/java/com/webapp/bankingportal/dto/OtpVerificationRequest.java +++ b/src/main/java/com/webapp/bankingportal/dto/OtpVerificationRequest.java @@ -1,23 +1,4 @@ package com.webapp.bankingportal.dto; -public class OtpVerificationRequest { - private String accountNumber; - private String otp; - - public String getAccountNumber() { - return accountNumber; - } - - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; - } - - public String getOtp() { - return otp; - } - - public void setOtp(String otp) { - this.otp = otp; - } - +public record OtpVerificationRequest(String identifier, String otp) { } diff --git a/src/main/java/com/webapp/bankingportal/dto/OtpVerificationRequestv2.java b/src/main/java/com/webapp/bankingportal/dto/OtpVerificationRequestv2.java deleted file mode 100644 index 7016ce2..0000000 --- a/src/main/java/com/webapp/bankingportal/dto/OtpVerificationRequestv2.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.webapp.bankingportal.dto; - -public class OtpVerificationRequestv2 { - private String identifier; // Can be either email or account number - private String otp; - - public String getIdentifier() { - return identifier; - } - - public void setIdentifier(String identifier) { - this.identifier = identifier; - } - - public String getOtp() { - return otp; - } - - public void setOtp(String otp) { - this.otp = otp; - } -} diff --git a/src/main/java/com/webapp/bankingportal/dto/PinRequest.java b/src/main/java/com/webapp/bankingportal/dto/PinRequest.java index 0496f77..197f0f4 100644 --- a/src/main/java/com/webapp/bankingportal/dto/PinRequest.java +++ b/src/main/java/com/webapp/bankingportal/dto/PinRequest.java @@ -1,29 +1,4 @@ package com.webapp.bankingportal.dto; -public class PinRequest { - private String accountNumber; - private String pin; - private String password ; - - public String getAccountNumber() { - return accountNumber; - } - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; - } - public String getPin() { - return pin; - } - public void setPin(String pin) { - this.pin = pin; - } - public String getPassword() { - return password; - } - public void setPassword(String password) { - this.password = password; - } - - // Add getters and setters +public record PinRequest(String accountNumber, String pin, String password) { } - diff --git a/src/main/java/com/webapp/bankingportal/dto/PinUpdateRequest.java b/src/main/java/com/webapp/bankingportal/dto/PinUpdateRequest.java index b8bd9ba..f6151ef 100644 --- a/src/main/java/com/webapp/bankingportal/dto/PinUpdateRequest.java +++ b/src/main/java/com/webapp/bankingportal/dto/PinUpdateRequest.java @@ -1,35 +1,4 @@ package com.webapp.bankingportal.dto; -public class PinUpdateRequest { - private String accountNumber; - private String oldPin; - private String newPin; - private String password; - - public String getAccountNumber() { - return accountNumber; - } - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; - } - public String getOldPin() { - return oldPin; - } - public void setOldPin(String oldPin) { - this.oldPin = oldPin; - } - public String getNewPin() { - return newPin; - } - public void setNewPin(String newPin) { - this.newPin = newPin; - } - public String getPassword() { - return password; - } - public void setPassword(String password) { - this.password = password; - } - - // Add getters and setters +public record PinUpdateRequest(String accountNumber, String oldPin, String newPin, String password) { } diff --git a/src/main/java/com/webapp/bankingportal/dto/TransactionDTO.java b/src/main/java/com/webapp/bankingportal/dto/TransactionDTO.java index 3ccee96..270563a 100644 --- a/src/main/java/com/webapp/bankingportal/dto/TransactionDTO.java +++ b/src/main/java/com/webapp/bankingportal/dto/TransactionDTO.java @@ -2,54 +2,40 @@ import java.util.Date; +import com.webapp.bankingportal.entity.Transaction; import com.webapp.bankingportal.entity.TransactionType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.val; + +@Data +@NoArgsConstructor +@AllArgsConstructor public class TransactionDTO { - private Long id; + + private Long id; private double amount; private TransactionType transactionType; private Date transactionDate; private String sourceAccountNumber; private String targetAccountNumber; - - - public Long getId() { - return id; - } - public void setId(Long id) { - this.id = id; - } - public double getAmount() { - return amount; - } - public void setAmount(double amount) { - this.amount = amount; - } - public TransactionType getTransactionType() { - return transactionType; - } - public void setTransactionType(TransactionType transactionType) { - this.transactionType = transactionType; - } - public Date getTransactionDate() { - return transactionDate; - } - public void setTransactionDate(Date transactionDate) { - this.transactionDate = transactionDate; - } - public String getSourceAccountNumber() { - return sourceAccountNumber; - } - public void setSourceAccountNumber(String sourceAccountNumber) { - this.sourceAccountNumber = sourceAccountNumber; - } - public String getTargetAccountNumber() { - return targetAccountNumber; - } - public void setTargetAccountNumber(String targetAccountNumber) { - this.targetAccountNumber = targetAccountNumber; - } - - - // getters and setters -} \ No newline at end of file + + public TransactionDTO(Transaction transaction) { + this.id = transaction.getId(); + this.amount = transaction.getAmount(); + this.transactionType = transaction.getTransactionType(); + this.transactionDate = transaction.getTransactionDate(); + this.sourceAccountNumber = transaction.getSourceAccount().getAccountNumber(); + + val targetAccount = transaction.getTargetAccount(); + var targetAccountNumber = "N/A"; + if (targetAccount != null) { + targetAccountNumber = targetAccount.getAccountNumber(); + } + + this.targetAccountNumber = targetAccountNumber; + } + +} diff --git a/src/main/java/com/webapp/bankingportal/dto/UserResponse.java b/src/main/java/com/webapp/bankingportal/dto/UserResponse.java index acd5177..2895c90 100644 --- a/src/main/java/com/webapp/bankingportal/dto/UserResponse.java +++ b/src/main/java/com/webapp/bankingportal/dto/UserResponse.java @@ -2,6 +2,11 @@ import com.webapp.bankingportal.entity.User; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data public class UserResponse { private String name; @@ -14,9 +19,6 @@ public class UserResponse { private String branch; private String accountType; - public UserResponse() { - } - public UserResponse(User user) { this.name = user.getName(); this.email = user.getEmail(); @@ -29,89 +31,4 @@ public UserResponse(User user) { this.accountType = user.getAccount().getAccountType(); } - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getCountryCode() { - return countryCode; - } - - public void setCountryCode(String countryCode) { - this.countryCode = countryCode; - } - - public String getPhoneNumber() { - return phoneNumber; - } - - public void setPhoneNumber(String phoneNumber) { - this.phoneNumber = phoneNumber; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public String getAccountNumber() { - return accountNumber; - } - - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; - } - - public String getIfscCode() { - return ifscCode; - } - - public void setIfscCode(String ifscCode) { - this.ifscCode = ifscCode; - } - - public String getBranch() { - return branch; - } - - public void setBranch(String branch) { - this.branch = branch; - } - - public String getAccountType() { - return accountType; - } - - public void setAccountType(String accountType) { - this.accountType = accountType; - } - - @Override - public String toString() { - return "{ \"name\": \"" + name - + "\", \"email\": \"" + email - + "\", \"address\": \"" + address - + "\", \"countryCode\": \"" + countryCode - + "\", \"phoneNumber\": \"" + phoneNumber - + "\", \"accountNumber\": \"" + accountNumber - + "\", \"ifscCode\": \"" + ifscCode - + "\", \"branch\": \"" + branch - + "\", \"accountType\": \"" + accountType + "\" }"; - } - } diff --git a/src/main/java/com/webapp/bankingportal/entity/Account.java b/src/main/java/com/webapp/bankingportal/entity/Account.java index 7452e94..fad777a 100644 --- a/src/main/java/com/webapp/bankingportal/entity/Account.java +++ b/src/main/java/com/webapp/bankingportal/entity/Account.java @@ -14,8 +14,10 @@ import jakarta.persistence.OneToOne; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import lombok.Data; @Entity +@Data public class Account { @Id @@ -43,90 +45,4 @@ public class Account { @OneToMany(mappedBy = "account", cascade = CascadeType.ALL) private List tokens = new ArrayList<>(); - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getAccountNumber() { - return accountNumber; - } - - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; - } - - public double getBalance() { - return balance; - } - - public void setBalance(double balance) { - this.balance = balance; - } - - public String getAccountType() { - return accountType; - } - - public void setAccountType(String accountType) { - this.accountType = accountType; - } - - public String getBranch() { - return branch; - } - - public void setBranch(String branch) { - this.branch = branch; - } - - public String getIfscCode() { - return ifscCode; - } - - public void setIfscCode(String ifscCode) { - this.ifscCode = ifscCode; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - public List getTokens() { - return tokens; - } - - public void setTokens(List tokens) { - this.tokens = tokens; - } - - @Override - public String toString() { - return "Account [id=" + id + ", accountNumber=" + accountNumber + ", balance=" + balance + ", accountType=" - + accountType + ", branch=" + branch + ", ifscCode=" + ifscCode + ", user=" + user + "]"; - } - - public String getPin() { - return Pin; - } - - public void setPin(String pin) { - this.Pin = pin; - } - - public String getAccountStatus() { - return accountStatus; - } - - public void setAccountStatus(String accountStatus) { - this.accountStatus = accountStatus; - } - } diff --git a/src/main/java/com/webapp/bankingportal/entity/OtpInfo.java b/src/main/java/com/webapp/bankingportal/entity/OtpInfo.java index 02ae5a0..f9c27a4 100644 --- a/src/main/java/com/webapp/bankingportal/entity/OtpInfo.java +++ b/src/main/java/com/webapp/bankingportal/entity/OtpInfo.java @@ -8,52 +8,31 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import lombok.Data; +import lombok.NoArgsConstructor; + @Entity +@NoArgsConstructor +@Data public class OtpInfo { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true) private String accountNumber; - + @Column private String otp; @Column private LocalDateTime generatedAt; - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getAccountNumber() { - return accountNumber; - } - - public void setAccountNumber(String accountNumber) { - this.accountNumber = accountNumber; - } - - public String getOtp() { - return otp; - } - - public void setOtp(String otp) { - this.otp = otp; - } - - public LocalDateTime getGeneratedAt() { - return generatedAt; - } - - public void setGeneratedAt(LocalDateTime generatedAt) { - this.generatedAt = generatedAt; - } + public OtpInfo(String accountNumber, String otp, LocalDateTime generatedAt) { + this.accountNumber = accountNumber; + this.otp = otp; + this.generatedAt = generatedAt; + } - } diff --git a/src/main/java/com/webapp/bankingportal/entity/PasswordResetToken.java b/src/main/java/com/webapp/bankingportal/entity/PasswordResetToken.java index 136f081..78e496a 100644 --- a/src/main/java/com/webapp/bankingportal/entity/PasswordResetToken.java +++ b/src/main/java/com/webapp/bankingportal/entity/PasswordResetToken.java @@ -11,11 +11,16 @@ import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Table; +import lombok.Data; +import lombok.NoArgsConstructor; + import java.io.Serializable; import java.time.LocalDateTime; @Entity @Table(name = "passwordresettoken") +@Data +@NoArgsConstructor public class PasswordResetToken implements Serializable { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "passwordresettoken_sequence") @@ -32,45 +37,14 @@ public class PasswordResetToken implements Serializable { @Column(nullable = false) private LocalDateTime expiryDateTime; - public PasswordResetToken() { - } - public PasswordResetToken(String token, User user, LocalDateTime expiryDateTime) { this.token = token; this.user = user; this.expiryDateTime = expiryDateTime; } - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - - public LocalDateTime getExpiryDateTime() { - return expiryDateTime; - } - - public void setExpiryDateTime(LocalDateTime expiryDateTime) { - this.expiryDateTime = expiryDateTime; + public boolean isTokenValid() { + return getExpiryDateTime().isAfter(LocalDateTime.now()); } -} \ No newline at end of file +} diff --git a/src/main/java/com/webapp/bankingportal/entity/Token.java b/src/main/java/com/webapp/bankingportal/entity/Token.java index 78a5225..958bcc7 100644 --- a/src/main/java/com/webapp/bankingportal/entity/Token.java +++ b/src/main/java/com/webapp/bankingportal/entity/Token.java @@ -12,7 +12,12 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.NoArgsConstructor; + @Entity +@NoArgsConstructor +@Data public class Token { @Id @@ -31,55 +36,13 @@ public class Token { @NotNull @ManyToOne - @JoinColumn(name = "account_id") + @JoinColumn(name = "account_id") private Account account; - public Token() { - } - public Token(String token, Date expiryAt, Account account) { this.token = token; this.expiryAt = expiryAt; this.account = account; } - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - public Date getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(Date createdAt) { - this.createdAt = createdAt; - } - - public Date getExpiryAt() { - return expiryAt; - } - - public void setExpiryAt(Date expiryAt) { - this.expiryAt = expiryAt; - } - - public Account getAccount() { - return account; - } - - public void setAccount(Account account) { - this.account = account; - } } diff --git a/src/main/java/com/webapp/bankingportal/entity/Transaction.java b/src/main/java/com/webapp/bankingportal/entity/Transaction.java index 25a17e6..276e509 100644 --- a/src/main/java/com/webapp/bankingportal/entity/Transaction.java +++ b/src/main/java/com/webapp/bankingportal/entity/Transaction.java @@ -11,80 +11,27 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import lombok.Data; + @Entity +@Data public class Transaction { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - private double amount; - - @Enumerated(EnumType.STRING) - private TransactionType transactionType; - - private Date transactionDate; - - @ManyToOne - @JoinColumn(name = "source_account_id") - private Account sourceAccount; - - @ManyToOne - @JoinColumn(name = "target_account_id") - private Account targetAccount; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public double getAmount() { - return amount; - } - - public void setAmount(double amount) { - this.amount = amount; - } - - public TransactionType getTransactionType() { - return transactionType; - } - - public void setTransactionType(TransactionType transactionType) { - this.transactionType = transactionType; - } - - public Date getTransactionDate() { - return transactionDate; - } - - public void setTransactionDate(Date transactionDate) { - this.transactionDate = transactionDate; - } - - public Account getSourceAccount() { - return sourceAccount; - } - - public void setSourceAccount(Account sourceAccount) { - this.sourceAccount = sourceAccount; - } + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private double amount; - public Account getTargetAccount() { - return targetAccount; - } + @Enumerated(EnumType.STRING) + private TransactionType transactionType; - public void setTargetAccount(Account targetAccount) { - this.targetAccount = targetAccount; - } + private Date transactionDate; - @Override - public String toString() { - return "Transaction [id=" + id + ", amount=" + amount + ", transactionType=" + transactionType - + ", transactionDate=" + transactionDate + ", sourceAccount=" + sourceAccount + ", targetAccount=" - + targetAccount + "]"; - } + @ManyToOne + @JoinColumn(name = "source_account_id") + private Account sourceAccount; + @ManyToOne + @JoinColumn(name = "target_account_id") + private Account targetAccount; } diff --git a/src/main/java/com/webapp/bankingportal/entity/TransactionType.java b/src/main/java/com/webapp/bankingportal/entity/TransactionType.java index 30c3284..ed38f28 100644 --- a/src/main/java/com/webapp/bankingportal/entity/TransactionType.java +++ b/src/main/java/com/webapp/bankingportal/entity/TransactionType.java @@ -4,6 +4,5 @@ public enum TransactionType { CASH_WITHDRAWAL, CASH_DEPOSIT, CASH_TRANSFER, - PIN_CREATION, - PIN_UPDATE + CASH_CREDIT } diff --git a/src/main/java/com/webapp/bankingportal/entity/User.java b/src/main/java/com/webapp/bankingportal/entity/User.java index f5b6e8f..ab4203e 100644 --- a/src/main/java/com/webapp/bankingportal/entity/User.java +++ b/src/main/java/com/webapp/bankingportal/entity/User.java @@ -10,7 +10,10 @@ import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + @Entity +@Data public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -41,68 +44,4 @@ public class User { @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) private Account account; - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getCountryCode() { - return countryCode; - } - - public void setCountryCode(String countryCode) { - this.countryCode = countryCode; - } - - public String getPhoneNumber() { - return phoneNumber; - } - - public void setPhoneNumber(String phoneNumber) { - this.phoneNumber = phoneNumber; - } - - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - - public Account getAccount() { - return account; - } - - public void setAccount(Account account) { - this.account = account; - account.setUser(this); - } } diff --git a/src/main/java/com/webapp/bankingportal/mapper/TransactionMapper.java b/src/main/java/com/webapp/bankingportal/mapper/TransactionMapper.java index 8bfe9ad..0e0fff0 100644 --- a/src/main/java/com/webapp/bankingportal/mapper/TransactionMapper.java +++ b/src/main/java/com/webapp/bankingportal/mapper/TransactionMapper.java @@ -9,18 +9,7 @@ public class TransactionMapper { public TransactionDTO toDto(Transaction transaction) { - TransactionDTO dto = new TransactionDTO(); - dto.setId(transaction.getId()); - dto.setAmount(transaction.getAmount()); - dto.setTransactionType(transaction.getTransactionType()); - dto.setTransactionDate(transaction.getTransactionDate()); - dto.setSourceAccountNumber(transaction.getSourceAccount().getAccountNumber()); - if (transaction.getTargetAccount() != null) { - dto.setTargetAccountNumber(transaction.getTargetAccount().getAccountNumber()); - } else { - // Handle the case where target account is null (e.g., set a default value or do nothing) - dto.setTargetAccountNumber("N/A"); // Replace "N/A" with an appropriate default value - } - return dto; + return new TransactionDTO(transaction); } + } diff --git a/src/main/java/com/webapp/bankingportal/security/JwtAuthenticationEntryPoint.java b/src/main/java/com/webapp/bankingportal/security/JwtAuthenticationEntryPoint.java index 9c97c50..83acc5f 100644 --- a/src/main/java/com/webapp/bankingportal/security/JwtAuthenticationEntryPoint.java +++ b/src/main/java/com/webapp/bankingportal/security/JwtAuthenticationEntryPoint.java @@ -1,7 +1,6 @@ package com.webapp.bankingportal.security; import java.io.IOException; -import java.io.PrintWriter; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; @@ -11,19 +10,34 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +/** + * This class implements the AuthenticationEntryPoint interface to handle + * unauthorized access attempts. It is responsible for commencing the + * authentication scheme and sending the appropriate response when an + * unauthenticated user tries to access a secured resource. + */ @Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { - /* - * Component class serving as the entry point for JWT authentication failures. - * */ - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - PrintWriter writer = response.getWriter(); - writer.println("Access Denied !! " + authException.getMessage()); - } - + /** + * This method is called when an unauthenticated user tries to access a secured + * resource. It sets the HTTP response status to 401 (Unauthorized) and sends an + * error message. + * + * @param request The HTTP request that resulted in an + * AuthenticationException + * @param response The HTTP response + * @param authException The AuthenticationException that was thrown when trying + * to authenticate the user + * + * @throws IOException If an input or output exception occurs + * @throws ServletException If a servlet exception occurs + */ + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authException) throws IOException, ServletException { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter().println("Access Denied !! " + authException.getMessage()); + } } diff --git a/src/main/java/com/webapp/bankingportal/security/JwtAuthenticationFilter.java b/src/main/java/com/webapp/bankingportal/security/JwtAuthenticationFilter.java index e275055..bd2c0ad 100644 --- a/src/main/java/com/webapp/bankingportal/security/JwtAuthenticationFilter.java +++ b/src/main/java/com/webapp/bankingportal/security/JwtAuthenticationFilter.java @@ -2,11 +2,9 @@ import java.io.IOException; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.NonNull; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; @@ -20,23 +18,33 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.val; + /** - * JwtAuthenticationFilter is responsible for filtering incoming requests to - * authenticate JWT tokens. It extracts the JWT token from the Authorization - * header, validates it, and sets the authentication in the - * SecurityContextHolder if the token is valid. If the token is invalid or - * expired, appropriate log messages are generated. This filter is used for - * securing endpoints that require JWT authentication. + * JWT Authentication Filter + * + * This filter intercepts incoming requests to authenticate users based on JWT + * tokens. It extends OncePerRequestFilter to ensure it's executed once per + * request. */ @Component +@RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { - @Autowired - private UserDetailsService userDetailsService; - - @Autowired - private TokenService tokenService; - + private final UserDetailsService userDetailsService; + private final TokenService tokenService; + + /** + * Performs the filtering for each request + * + * @param request The HTTP request + * @param response The HTTP response + * @param filterChain The filter chain + * + * @throws ServletException If a servlet-specific error occurs + * @throws IOException If an I/O error occurs + */ @Override protected void doFilterInternal( @NonNull HttpServletRequest request, @@ -51,7 +59,7 @@ protected void doFilterInternal( return; } - String requestTokenHeader = request.getHeader("Authorization"); + val requestTokenHeader = request.getHeader("Authorization"); if (requestTokenHeader == null) { filterChain.doFilter(request, response); @@ -65,7 +73,7 @@ protected void doFilterInternal( return; } - String token = requestTokenHeader.substring(7); + val token = requestTokenHeader.substring(7); String username = null; try { @@ -78,8 +86,8 @@ protected void doFilterInternal( return; } - UserDetails userDetails = userDetailsService.loadUserByUsername(username); - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + val userDetails = userDetailsService.loadUserByUsername(username); + val authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); @@ -87,4 +95,5 @@ protected void doFilterInternal( filterChain.doFilter(request, response); } + } diff --git a/src/main/java/com/webapp/bankingportal/service/AccountServiceImpl.java b/src/main/java/com/webapp/bankingportal/service/AccountServiceImpl.java index 1447fa9..52c9bbc 100644 --- a/src/main/java/com/webapp/bankingportal/service/AccountServiceImpl.java +++ b/src/main/java/com/webapp/bankingportal/service/AccountServiceImpl.java @@ -3,10 +3,6 @@ import java.util.Date; import java.util.UUID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -22,23 +18,22 @@ import com.webapp.bankingportal.repository.AccountRepository; import com.webapp.bankingportal.repository.TransactionRepository; +import lombok.RequiredArgsConstructor; +import lombok.val; +import lombok.extern.slf4j.Slf4j; + @Service +@Slf4j +@RequiredArgsConstructor public class AccountServiceImpl implements AccountService { - @Autowired - private AccountRepository accountRepository; - - @Autowired - private TransactionRepository transactionRepository; - - @Autowired - private PasswordEncoder passwordEncoder; - - private static final Logger logger = LoggerFactory.getLogger(AccountServiceImpl.class); + private final AccountRepository accountRepository; + private final TransactionRepository transactionRepository; + private final PasswordEncoder passwordEncoder; @Override public Account createAccount(User user) { - Account account = new Account(); + val account = new Account(); account.setAccountNumber(generateUniqueAccountNumber()); account.setBalance(0.0); account.setUser(user); @@ -47,7 +42,7 @@ public Account createAccount(User user) { @Override public boolean isPinCreated(String accountNumber) { - Account account = accountRepository.findByAccountNumber(accountNumber); + val account = accountRepository.findByAccountNumber(accountNumber); if (account == null) { throw new NotFoundException("Account not found"); } @@ -66,7 +61,7 @@ private String generateUniqueAccountNumber() { } private void validatePin(String accountNumber, String pin) { - Account account = accountRepository.findByAccountNumber(accountNumber); + val account = accountRepository.findByAccountNumber(accountNumber); if (account == null) { throw new NotFoundException("Account not found"); } @@ -85,7 +80,7 @@ private void validatePin(String accountNumber, String pin) { } private void validatePassword(String accountNumber, String password) { - Account account = accountRepository.findByAccountNumber(accountNumber); + val account = accountRepository.findByAccountNumber(accountNumber); if (account == null) { throw new NotFoundException("Account not found"); } @@ -103,7 +98,7 @@ private void validatePassword(String accountNumber, String password) { public void createPin(String accountNumber, String password, String pin) { validatePassword(accountNumber, password); - Account account = accountRepository.findByAccountNumber(accountNumber); + val account = accountRepository.findByAccountNumber(accountNumber); if (account.getPin() != null) { throw new UnauthorizedException("PIN already created"); } @@ -122,18 +117,18 @@ public void createPin(String accountNumber, String password, String pin) { @Override public void updatePin(String accountNumber, String oldPin, String password, String newPin) { - logger.info("Updating PIN for account: {}", accountNumber); + log.info("Updating PIN for account: {}", accountNumber); validatePassword(accountNumber, password); validatePin(accountNumber, oldPin); - Account account = accountRepository.findByAccountNumber(accountNumber); + val account = accountRepository.findByAccountNumber(accountNumber); if (newPin == null || newPin.isEmpty()) { throw new InvalidPinException("New PIN cannot be empty"); } - if (newPin.length() != 4) { + if (!newPin.matches("[0-9]{4}")) { throw new InvalidPinException("New PIN must be 4 digits"); } @@ -160,13 +155,13 @@ public void cashDeposit(String accountNumber, String pin, double amount) { validatePin(accountNumber, pin); validateAmount(amount); - Account account = accountRepository.findByAccountNumber(accountNumber); - double currentBalance = account.getBalance(); - double newBalance = currentBalance + amount; + val account = accountRepository.findByAccountNumber(accountNumber); + val currentBalance = account.getBalance(); + val newBalance = currentBalance + amount; account.setBalance(newBalance); accountRepository.save(account); - Transaction transaction = new Transaction(); + val transaction = new Transaction(); transaction.setAmount(amount); transaction.setTransactionType(TransactionType.CASH_DEPOSIT); transaction.setTransactionDate(new Date()); @@ -179,17 +174,17 @@ public void cashWithdrawal(String accountNumber, String pin, double amount) { validatePin(accountNumber, pin); validateAmount(amount); - Account account = accountRepository.findByAccountNumber(accountNumber); - double currentBalance = account.getBalance(); + val account = accountRepository.findByAccountNumber(accountNumber); + val currentBalance = account.getBalance(); if (currentBalance < amount) { throw new InsufficientBalanceException("Insufficient balance"); } - double newBalance = currentBalance - amount; + val newBalance = currentBalance - amount; account.setBalance(newBalance); accountRepository.save(account); - Transaction transaction = new Transaction(); + val transaction = new Transaction(); transaction.setAmount(amount); transaction.setTransactionType(TransactionType.CASH_WITHDRAWAL); transaction.setTransactionDate(new Date()); @@ -202,27 +197,27 @@ public void fundTransfer(String sourceAccountNumber, String targetAccountNumber, validatePin(sourceAccountNumber, pin); validateAmount(amount); - Account targetAccount = accountRepository.findByAccountNumber(targetAccountNumber); + val targetAccount = accountRepository.findByAccountNumber(targetAccountNumber); if (targetAccount == null) { throw new NotFoundException("Target account not found"); } - Account sourceAccount = accountRepository.findByAccountNumber(sourceAccountNumber); - double sourceBalance = sourceAccount.getBalance(); + val sourceAccount = accountRepository.findByAccountNumber(sourceAccountNumber); + val sourceBalance = sourceAccount.getBalance(); if (sourceBalance < amount) { throw new InsufficientBalanceException("Insufficient balance"); } - double newSourceBalance = sourceBalance - amount; + val newSourceBalance = sourceBalance - amount; sourceAccount.setBalance(newSourceBalance); accountRepository.save(sourceAccount); - double targetBalance = targetAccount.getBalance(); - double newTargetBalance = targetBalance + amount; + val targetBalance = targetAccount.getBalance(); + val newTargetBalance = targetBalance + amount; targetAccount.setBalance(newTargetBalance); accountRepository.save(targetAccount); - Transaction transaction = new Transaction(); + val transaction = new Transaction(); transaction.setAmount(amount); transaction.setTransactionType(TransactionType.CASH_TRANSFER); transaction.setTransactionDate(new Date()); diff --git a/src/main/java/com/webapp/bankingportal/service/AuthServiceImpl.java b/src/main/java/com/webapp/bankingportal/service/AuthServiceImpl.java index 9de3739..e9d99fb 100644 --- a/src/main/java/com/webapp/bankingportal/service/AuthServiceImpl.java +++ b/src/main/java/com/webapp/bankingportal/service/AuthServiceImpl.java @@ -3,7 +3,6 @@ import java.time.LocalDateTime; import java.util.UUID; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.webapp.bankingportal.entity.PasswordResetToken; @@ -12,19 +11,21 @@ import jakarta.transaction.Transactional; -@Service +import lombok.RequiredArgsConstructor; +import lombok.val; +@Service +@RequiredArgsConstructor public class AuthServiceImpl implements AuthService { private static final int EXPIRATION_HOURS = 24; - @Autowired - private PasswordResetTokenRepository passwordResetTokenRepository; + private final PasswordResetTokenRepository passwordResetTokenRepository; @Override public String generatePasswordResetToken(User user) { // Check if there's an existing token for the user that hasn't expired - PasswordResetToken existingToken = passwordResetTokenRepository.findByUser(user); + val existingToken = passwordResetTokenRepository.findByUser(user); if (existingToken != null) { // Check if the existing token has more than 5 minutes left before expiration @@ -38,13 +39,13 @@ public String generatePasswordResetToken(User user) { } // Generate a new token - String token = UUID.randomUUID().toString(); + val token = UUID.randomUUID().toString(); // Calculate token expiry date/time - LocalDateTime expiryDateTime = LocalDateTime.now().plusHours(EXPIRATION_HOURS); + val expiryDateTime = LocalDateTime.now().plusHours(EXPIRATION_HOURS); // Save the new token in the database - PasswordResetToken resetToken = new PasswordResetToken(token, user, expiryDateTime); + val resetToken = new PasswordResetToken(token, user, expiryDateTime); passwordResetTokenRepository.save(resetToken); return token; @@ -55,10 +56,8 @@ public String generatePasswordResetToken(User user) { public boolean verifyPasswordResetToken(String token, User user) { return passwordResetTokenRepository.findByToken(token) .map(resetToken -> { - boolean isTokenValid = resetToken.getExpiryDateTime().isAfter(LocalDateTime.now()); - boolean isUserMatch = user.equals(resetToken.getUser()); deletePasswordResetToken(token); - return isTokenValid && isUserMatch; + return user.equals(resetToken.getUser()) && resetToken.isTokenValid(); }) .orElse(false); } @@ -68,4 +67,5 @@ public void deletePasswordResetToken(String token) { // Delete the token from the database passwordResetTokenRepository.deleteByToken(token); } + } diff --git a/src/main/java/com/webapp/bankingportal/service/DashboardServiceImpl.java b/src/main/java/com/webapp/bankingportal/service/DashboardServiceImpl.java index d3e03f5..37798a5 100644 --- a/src/main/java/com/webapp/bankingportal/service/DashboardServiceImpl.java +++ b/src/main/java/com/webapp/bankingportal/service/DashboardServiceImpl.java @@ -1,28 +1,26 @@ package com.webapp.bankingportal.service; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.webapp.bankingportal.dto.AccountResponse; import com.webapp.bankingportal.dto.UserResponse; -import com.webapp.bankingportal.entity.Account; -import com.webapp.bankingportal.entity.User; import com.webapp.bankingportal.exception.NotFoundException; import com.webapp.bankingportal.repository.AccountRepository; import com.webapp.bankingportal.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import lombok.val; + @Service +@RequiredArgsConstructor public class DashboardServiceImpl implements DashboardService { - @Autowired - private UserRepository userRepository; - - @Autowired - private AccountRepository accountRepository; + private final UserRepository userRepository; + private final AccountRepository accountRepository; @Override public UserResponse getUserDetails(String accountNumber) { - User user = userRepository.findByAccountAccountNumber(accountNumber) + val user = userRepository.findByAccountAccountNumber(accountNumber) .orElseThrow(() -> new NotFoundException( "User not found for the provided account number.")); @@ -31,20 +29,12 @@ public UserResponse getUserDetails(String accountNumber) { @Override public AccountResponse getAccountDetails(String accountNumber) { - Account account = accountRepository.findByAccountNumber(accountNumber); - // Check if the account exists with the provided account number + val account = accountRepository.findByAccountNumber(accountNumber); if (account == null) { throw new NotFoundException("Account not found for the provided account number."); } - // Map the account entity to AccountResponse DTO - AccountResponse accountResponse = new AccountResponse(); - accountResponse.setAccountNumber(account.getAccountNumber()); - accountResponse.setAccountType(account.getAccountType()); - accountResponse.setBalance(account.getBalance()); - accountResponse.setBranch(account.getBranch()); - accountResponse.setIfscCode(account.getIfscCode()); - - return accountResponse; + return new AccountResponse(account); } + } diff --git a/src/main/java/com/webapp/bankingportal/service/EmailServiceImpl.java b/src/main/java/com/webapp/bankingportal/service/EmailServiceImpl.java index a8741cd..4eb3b33 100644 --- a/src/main/java/com/webapp/bankingportal/service/EmailServiceImpl.java +++ b/src/main/java/com/webapp/bankingportal/service/EmailServiceImpl.java @@ -3,9 +3,6 @@ import java.io.File; import java.util.concurrent.CompletableFuture; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import org.springframework.mail.MailException; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; @@ -13,15 +10,16 @@ import org.springframework.stereotype.Service; import jakarta.mail.MessagingException; -import jakarta.mail.internet.MimeMessage; + +import lombok.val; +import lombok.extern.slf4j.Slf4j; @Service +@Slf4j public class EmailServiceImpl implements EmailService { private final JavaMailSender mailSender; - private static final Logger logger = LoggerFactory.getLogger(EmailServiceImpl.class); - public EmailServiceImpl(JavaMailSender mailSender) { this.mailSender = mailSender; } @@ -29,22 +27,22 @@ public EmailServiceImpl(JavaMailSender mailSender) { @Override @Async public CompletableFuture sendEmail(String to, String subject, String text) { - CompletableFuture future = new CompletableFuture<>(); + val future = new CompletableFuture(); try { - MimeMessage message = mailSender.createMimeMessage(); - MimeMessageHelper helper = new MimeMessageHelper(message, true); + val message = mailSender.createMimeMessage(); + val helper = new MimeMessageHelper(message, true); helper.setTo(to); // From address is automatically set by Spring Boot based on your properties helper.setSubject(subject); helper.setText(text, true); // Set the second parameter to true to send HTML content mailSender.send(message); - logger.info("Sent email to {}", to); + log.info("Sent email to {}", to); future.complete(null); } catch (MessagingException | MailException e) { - logger.error("Failed to send email to {}", to, e); + log.error("Failed to send email to {}", to, e); future.completeExceptionally(e); } @@ -79,7 +77,6 @@ public String getLoginEmailTemplate(String name, String loginTime, String loginL @Override public String getOtpLoginEmailTemplate(String name, String accountNumber, String otp) { - return "
" + "
" + "
" @@ -105,19 +102,20 @@ public String getOtpLoginEmailTemplate(String name, String accountNumber, String public void sendEmailWithAttachment(String to, String subject, String text, String attachmentFilePath) { try { - MimeMessage message = mailSender.createMimeMessage(); - MimeMessageHelper helper = new MimeMessageHelper(message, true); + val message = mailSender.createMimeMessage(); + val helper = new MimeMessageHelper(message, true); helper.setTo(to); helper.setSubject(subject); helper.setText(text, true); // Set the second parameter to true to send HTML content // Add an attachment to the email - File attachmentFile = new File(attachmentFilePath); + val attachmentFile = new File(attachmentFilePath); helper.addAttachment(attachmentFile.getName(), attachmentFile); mailSender.send(message); } catch (MessagingException e) { - logger.error("Failed to send email to {}", to, e); + log.error("Failed to send email to {}", to, e); } } + } diff --git a/src/main/java/com/webapp/bankingportal/service/GeolocationServiceImpl.java b/src/main/java/com/webapp/bankingportal/service/GeolocationServiceImpl.java index bf0d9e9..04bbfe9 100644 --- a/src/main/java/com/webapp/bankingportal/service/GeolocationServiceImpl.java +++ b/src/main/java/com/webapp/bankingportal/service/GeolocationServiceImpl.java @@ -4,9 +4,6 @@ import java.net.UnknownHostException; import java.util.concurrent.CompletableFuture; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -16,7 +13,11 @@ import com.webapp.bankingportal.dto.GeolocationResponse; import com.webapp.bankingportal.exception.GeolocationException; +import lombok.val; +import lombok.extern.slf4j.Slf4j; + @Service +@Slf4j public class GeolocationServiceImpl implements GeolocationService { @Value("${geo.api.url}") @@ -25,26 +26,24 @@ public class GeolocationServiceImpl implements GeolocationService { @Value("${geo.api.key}") private String apiKey; - private static final Logger logger = LoggerFactory.getLogger(GeolocationServiceImpl.class); - @Override @Async public CompletableFuture getGeolocation(String ip) { - CompletableFuture future = new CompletableFuture<>(); + val future = new CompletableFuture(); try { // Validate IP address InetAddress.getByName(ip); - logger.info("Getting geolocation for IP: {}", ip); + log.info("Getting geolocation for IP: {}", ip); // Call geolocation API - String url = String.format("%s/%s/?token=%s", apiUrl, ip, apiKey); - GeolocationResponse response = new RestTemplate() + val url = String.format("%s/%s/?token=%s", apiUrl, ip, apiKey); + val response = new RestTemplate() .getForObject(url, GeolocationResponse.class); if (response == null) { - logger.error("Failed to get geolocation for IP: {}", ip); + log.error("Failed to get geolocation for IP: {}", ip); future.completeExceptionally(new GeolocationException( "Failed to get geolocation for IP: " + ip)); } else { @@ -52,14 +51,15 @@ public CompletableFuture getGeolocation(String ip) { } } catch (UnknownHostException e) { - logger.error("Invalid IP address: {}", ip, e); + log.error("Invalid IP address: {}", ip, e); future.completeExceptionally(e); } catch (RestClientException e) { - logger.error("Failed to get geolocation for IP: {}", ip, e); + log.error("Failed to get geolocation for IP: {}", ip, e); future.completeExceptionally(e); } return future; } + } diff --git a/src/main/java/com/webapp/bankingportal/service/OtpServiceImpl.java b/src/main/java/com/webapp/bankingportal/service/OtpServiceImpl.java index 0cfa7d6..b3ca70e 100644 --- a/src/main/java/com/webapp/bankingportal/service/OtpServiceImpl.java +++ b/src/main/java/com/webapp/bankingportal/service/OtpServiceImpl.java @@ -5,8 +5,6 @@ import java.util.Random; import java.util.concurrent.CompletableFuture; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; @@ -16,7 +14,11 @@ import com.webapp.bankingportal.exception.OtpRetryLimitExceededException; import com.webapp.bankingportal.repository.OtpInfoRepository; +import lombok.RequiredArgsConstructor; +import lombok.val; + @Service +@RequiredArgsConstructor public class OtpServiceImpl implements OtpService { public static final int OTP_ATTEMPTS_LIMIT = 3; @@ -24,19 +26,12 @@ public class OtpServiceImpl implements OtpService { public static final int OTP_RESET_WAITING_TIME_MINUTES = 10; public static final int OTP_RETRY_LIMIT_WINDOW_MINUTES = 15; - private LocalDateTime otpLimitReachedTime = null; - - @Autowired - private EmailService emailService; - - @Autowired - private OtpInfoRepository otpInfoRepository; - - @Autowired - private UserService userService; + private final EmailService emailService; + private final OtpInfoRepository otpInfoRepository; + private final UserService userService; + private final CacheManager cacheManager; - @Autowired - private CacheManager cacheManager; + private LocalDateTime otpLimitReachedTime = null; @Override public String generateOTP(String accountNumber) { @@ -44,7 +39,7 @@ public String generateOTP(String accountNumber) { throw new AccountDoesNotExistException("Account does not exist"); } - OtpInfo existingOtpInfo = otpInfoRepository.findByAccountNumber(accountNumber); + val existingOtpInfo = otpInfoRepository.findByAccountNumber(accountNumber); if (existingOtpInfo == null) { incrementOtpAttempts(accountNumber); return generateNewOTP(accountNumber); @@ -52,8 +47,7 @@ public String generateOTP(String accountNumber) { validateOtpWithinRetryLimit(existingOtpInfo); - if (isOtpExpired(existingOtpInfo.getGeneratedAt())) { - otpInfoRepository.delete(existingOtpInfo); + if (isOtpExpired(existingOtpInfo)) { return generateNewOTP(accountNumber); } @@ -69,20 +63,20 @@ private void validateOtpWithinRetryLimit(OtpInfo otpInfo) { return; } - LocalDateTime now = LocalDateTime.now(); + val now = LocalDateTime.now(); if (otpLimitReachedTime == null) { otpLimitReachedTime = now; } - long waitingMinutes = OTP_RESET_WAITING_TIME_MINUTES - otpLimitReachedTime.until(now, ChronoUnit.MINUTES); + val waitingMinutes = OTP_RESET_WAITING_TIME_MINUTES - otpLimitReachedTime.until(now, ChronoUnit.MINUTES); throw new OtpRetryLimitExceededException( "OTP generation limit exceeded. Please try again after " + waitingMinutes + " minutes"); } private boolean isOtpRetryLimitExceeded(OtpInfo otpInfo) { - int attempts = getOtpAttempts(otpInfo.getAccountNumber()); + val attempts = getOtpAttempts(otpInfo.getAccountNumber()); if (attempts < OTP_ATTEMPTS_LIMIT) { return false; } @@ -92,13 +86,13 @@ private boolean isOtpRetryLimitExceeded(OtpInfo otpInfo) { return false; } - LocalDateTime now = LocalDateTime.now(); + val now = LocalDateTime.now(); return otpInfo.getGeneratedAt().isAfter(now.minusMinutes(OTP_RETRY_LIMIT_WINDOW_MINUTES)); } private boolean isOtpResetWaitingTimeExceeded() { - LocalDateTime now = LocalDateTime.now(); + val now = LocalDateTime.now(); return otpLimitReachedTime != null && otpLimitReachedTime.isBefore(now.minusMinutes(OTP_RESET_WAITING_TIME_MINUTES)); } @@ -108,7 +102,7 @@ private void incrementOtpAttempts(String accountNumber) { throw new AccountDoesNotExistException("Account does not exist"); } - Cache cache = cacheManager.getCache("otpAttempts"); + val cache = cacheManager.getCache("otpAttempts"); if (cache != null) { cache.put(accountNumber, getOtpAttempts(accountNumber) + 1); } @@ -116,20 +110,20 @@ private void incrementOtpAttempts(String accountNumber) { private void resetOtpAttempts(String accountNumber) { otpLimitReachedTime = null; - Cache cache = cacheManager.getCache("otpAttempts"); + val cache = cacheManager.getCache("otpAttempts"); if (cache != null) { cache.put(accountNumber, 0); } } private int getOtpAttempts(String accountNumber) { - int otpAttempts = 0; - Cache cache = cacheManager.getCache("otpAttempts"); + var otpAttempts = 0; + val cache = cacheManager.getCache("otpAttempts"); if (cache == null) { return otpAttempts; } - Integer value = cache.get(accountNumber, Integer.class); + val value = cache.get(accountNumber, Integer.class); if (value != null) { otpAttempts = value; } @@ -138,44 +132,44 @@ private int getOtpAttempts(String accountNumber) { } private String generateNewOTP(String accountNumber) { - Random random = new Random(); - int otpValue = 100_000 + random.nextInt(900_000); - String otp = String.valueOf(otpValue); + val random = new Random(); + val otpValue = 100_000 + random.nextInt(900_000); + val otp = String.valueOf(otpValue); - // Save the new OTP information in the database - OtpInfo otpInfo = new OtpInfo(); - otpInfo.setAccountNumber(accountNumber); - otpInfo.setOtp(otp); - otpInfo.setGeneratedAt(LocalDateTime.now()); - otpInfoRepository.save(otpInfo); + otpInfoRepository.save(new OtpInfo(accountNumber, otp, LocalDateTime.now())); return otp; } @Override public CompletableFuture sendOTPByEmail(String email, String name, String accountNumber, String otp) { - // Compose the email content - String subject = "OTP Verification"; - String emailText = emailService.getOtpLoginEmailTemplate(name, "xxx" + accountNumber.substring(3), otp); + val subject = "OTP Verification"; + val emailText = emailService.getOtpLoginEmailTemplate(name, "xxx" + accountNumber.substring(3), otp); - CompletableFuture emailSendingFuture = emailService.sendEmail(email, subject, emailText); + val emailSendingFuture = emailService.sendEmail(email, subject, emailText); return emailSendingFuture; } @Override public boolean validateOTP(String accountNumber, String otp) { - OtpInfo otpInfo = otpInfoRepository.findByAccountNumberAndOtp(accountNumber, otp); + val otpInfo = otpInfoRepository.findByAccountNumberAndOtp(accountNumber, otp); if (otpInfo == null) { throw new InvalidOtpException("Invalid OTP"); } - // otpInfoRepository.delete(otpInfo); - return !isOtpExpired(otpInfo.getGeneratedAt()); + return !isOtpExpired(otpInfo); } - private boolean isOtpExpired(LocalDateTime otpGeneratedAt) { - LocalDateTime now = LocalDateTime.now(); - return otpGeneratedAt.isBefore(now.minusMinutes(OTP_EXPIRY_MINUTES)); + private boolean isOtpExpired(OtpInfo otpInfo) { + val now = LocalDateTime.now(); + val generatedAt = otpInfo.getGeneratedAt(); + val expired = generatedAt.isBefore(now.minusMinutes(OTP_EXPIRY_MINUTES)); + if (expired) { + otpInfoRepository.delete(otpInfo); + } + + return expired; } + } diff --git a/src/main/java/com/webapp/bankingportal/service/TokenServiceImpl.java b/src/main/java/com/webapp/bankingportal/service/TokenServiceImpl.java index 8ccdb69..8056c9f 100644 --- a/src/main/java/com/webapp/bankingportal/service/TokenServiceImpl.java +++ b/src/main/java/com/webapp/bankingportal/service/TokenServiceImpl.java @@ -5,19 +5,13 @@ import java.util.Date; import java.util.function.Function; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.webapp.bankingportal.entity.Account; import com.webapp.bankingportal.entity.Token; -import com.webapp.bankingportal.entity.User; import com.webapp.bankingportal.exception.InvalidTokenException; import com.webapp.bankingportal.repository.AccountRepository; import com.webapp.bankingportal.repository.TokenRepository; @@ -31,30 +25,24 @@ import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.UnsupportedJwtException; +import lombok.RequiredArgsConstructor; +import lombok.val; +import lombok.extern.slf4j.Slf4j; + @Service +@Slf4j +@RequiredArgsConstructor public class TokenServiceImpl implements TokenService { - @Autowired - private UserRepository userRepository; - - @Autowired - private TokenRepository tokenRepository; - - @Autowired - private AccountRepository accountRepository; + @Value("${jwt.secret}") + private String secret; - private final long expiration; - private final String secret; + @Value("${jwt.expiration}") + private long expiration; - private static final Logger logger = LoggerFactory.getLogger(TokenServiceImpl.class); - - public TokenServiceImpl( - @Value("${jwt.secret}") String secret, - @Value("${jwt.expiration}") long expiration) { - - this.secret = secret; - this.expiration = expiration; - } + private final UserRepository userRepository; + private final TokenRepository tokenRepository; + private final AccountRepository accountRepository; @Override public String getUsernameFromToken(String token) throws InvalidTokenException { @@ -63,14 +51,14 @@ public String getUsernameFromToken(String token) throws InvalidTokenException { @Override public String generateToken(UserDetails userDetails) { - logger.info("Generating token for user: " + userDetails.getUsername()); + log.info("Generating token for user: " + userDetails.getUsername()); return doGenerateToken(userDetails, new Date(System.currentTimeMillis() + expiration)); } @Override public String generateToken(UserDetails userDetails, Date expiry) { - logger.info("Generating token for user: " + userDetails.getUsername()); + log.info("Generating token for user: " + userDetails.getUsername()); return doGenerateToken(userDetails, expiry); } @@ -83,7 +71,7 @@ private String doGenerateToken(UserDetails userDetails, Date expiry) { @Override public UserDetails loadUserByUsername(String accountNumber) throws UsernameNotFoundException { - User user = userRepository.findByAccountAccountNumber(accountNumber) + val user = userRepository.findByAccountAccountNumber(accountNumber) .orElseThrow(() -> new UsernameNotFoundException( "User not found with account number: " + accountNumber)); @@ -99,7 +87,7 @@ public Date getExpirationDateFromToken(String token) @Override public T getClaimFromToken(String token, Function claimsResolver) throws InvalidTokenException { - final Claims claims = getAllClaimsFromToken(token); + val claims = getAllClaimsFromToken(token); return claimsResolver.apply(claims); } @@ -133,12 +121,12 @@ public void saveToken(String token) throws InvalidTokenException { throw new InvalidTokenException("Token already exists"); } - Account account = accountRepository.findByAccountNumber( + val account = accountRepository.findByAccountNumber( getUsernameFromToken(token)); - logger.info("Saving token for account: " + account.getAccountNumber()); + log.info("Saving token for account: " + account.getAccountNumber()); - Token tokenObj = new Token( + val tokenObj = new Token( token, getExpirationDateFromToken(token), account); @@ -160,4 +148,5 @@ public void invalidateToken(String token) { tokenRepository.deleteByToken(token); } } + } diff --git a/src/main/java/com/webapp/bankingportal/service/TransactionServiceImpl.java b/src/main/java/com/webapp/bankingportal/service/TransactionServiceImpl.java index 4a722b2..60d0ba3 100644 --- a/src/main/java/com/webapp/bankingportal/service/TransactionServiceImpl.java +++ b/src/main/java/com/webapp/bankingportal/service/TransactionServiceImpl.java @@ -3,29 +3,28 @@ import java.util.List; import java.util.stream.Collectors; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.webapp.bankingportal.dto.TransactionDTO; -import com.webapp.bankingportal.entity.Transaction; import com.webapp.bankingportal.mapper.TransactionMapper; import com.webapp.bankingportal.repository.TransactionRepository; +import lombok.RequiredArgsConstructor; +import lombok.val; + @Service +@RequiredArgsConstructor public class TransactionServiceImpl implements TransactionService { - @Autowired - private TransactionRepository transactionRepository; - - @Autowired - private TransactionMapper transactionMapper; + private final TransactionRepository transactionRepository; + private final TransactionMapper transactionMapper; @Override public List getAllTransactionsByAccountNumber(String accountNumber) { - List transactions = transactionRepository + val transactions = transactionRepository .findBySourceAccount_AccountNumberOrTargetAccount_AccountNumber(accountNumber, accountNumber); - List transactionDTOs = transactions.parallelStream() + val transactionDTOs = transactions.parallelStream() .map(transactionMapper::toDto) .sorted((t1, t2) -> t2.getTransactionDate().compareTo(t1.getTransactionDate())) .collect(Collectors.toList()); diff --git a/src/main/java/com/webapp/bankingportal/service/UserService.java b/src/main/java/com/webapp/bankingportal/service/UserService.java index 1feac26..e3a410d 100644 --- a/src/main/java/com/webapp/bankingportal/service/UserService.java +++ b/src/main/java/com/webapp/bankingportal/service/UserService.java @@ -19,11 +19,16 @@ public interface UserService { public boolean doesAccountExist(String accountNumber); + public boolean doesIdentifierExist(String identifier); + public Optional getUserByAccountNumber(String accountNumber); public Optional getUserByEmail(String email); + public Optional getUserByIdentifier(String identifier); + public CompletableFuture sendLoginNotificationEmail(User user, String ip); public boolean resetPassword(User user, String newpassword); + } diff --git a/src/main/java/com/webapp/bankingportal/service/UserServiceImpl.java b/src/main/java/com/webapp/bankingportal/service/UserServiceImpl.java index b011d59..9c903e3 100644 --- a/src/main/java/com/webapp/bankingportal/service/UserServiceImpl.java +++ b/src/main/java/com/webapp/bankingportal/service/UserServiceImpl.java @@ -8,7 +8,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.webapp.bankingportal.entity.Account; import com.webapp.bankingportal.entity.User; import com.webapp.bankingportal.exception.PasswordResetException; import com.webapp.bankingportal.exception.UserInvalidException; @@ -17,6 +16,8 @@ import com.webapp.bankingportal.util.LoggedinUser; import com.webapp.bankingportal.util.ValidationUtil; +import lombok.val; + @Service public class UserServiceImpl implements UserService { @@ -59,9 +60,9 @@ public User registerUser(User user) { user.setCountryCode(user.getCountryCode().toUpperCase()); user.setPassword(passwordEncoder.encode(user.getPassword())); - User savedUser = saveUser(user); + val savedUser = saveUser(user); - Account account = accountService.createAccount(savedUser); + val account = accountService.createAccount(savedUser); savedUser.setAccount(account); return saveUser(savedUser); @@ -74,8 +75,8 @@ public User saveUser(User user) { @Override public User updateUser(User source) { - String accountNumber = LoggedinUser.getAccountNumber(); - User target = userRepository.findByAccountAccountNumber(accountNumber) + val accountNumber = LoggedinUser.getAccountNumber(); + val target = userRepository.findByAccountAccountNumber(accountNumber) .orElseThrow(() -> new UserInvalidException( "User with account number " + accountNumber + " does not exist")); @@ -102,6 +103,11 @@ public boolean doesAccountExist(String accountNumber) { return userRepository.findByAccountAccountNumber(accountNumber).isPresent(); } + @Override + public boolean doesIdentifierExist(String identifier) { + return doesAccountExist(identifier) || doesEmailExist(identifier); + } + @Override public Optional getUserByAccountNumber(String accountNo) { return userRepository.findByAccountAccountNumber(accountNo); @@ -112,20 +118,33 @@ public Optional getUserByEmail(String email) { return userRepository.findByEmail(email); } + @Override + public Optional getUserByIdentifier(String identifier) { + Optional user = Optional.empty(); + + if (doesEmailExist(identifier)) { + user = getUserByEmail(identifier); + } else if (doesAccountExist(identifier)) { + user = getUserByAccountNumber(identifier); + } + + return user; + } + @Override public CompletableFuture sendLoginNotificationEmail(User user, String ip) { - final String name = user.getName(); - final String email = user.getEmail(); - final String subject = "New login to OneStopBank"; - final String loginTime = new Timestamp(System.currentTimeMillis()).toString(); + val name = user.getName(); + val email = user.getEmail(); + val subject = "New login to OneStopBank"; + val loginTime = new Timestamp(System.currentTimeMillis()).toString(); return geolocationService.getGeolocation(ip).thenComposeAsync(geolocationResponse -> { - final String loginLocation = String.format("%s, %s", + val loginLocation = String.format("%s, %s", geolocationResponse.getCity().getNames().get("en"), geolocationResponse.getCountry().getNames().get("en")); - final String emailText = emailService.getLoginEmailTemplate( + val emailText = emailService.getLoginEmailTemplate( name, loginTime, loginLocation); return emailService.sendEmail(email, subject, emailText) @@ -134,7 +153,7 @@ public CompletableFuture sendLoginNotificationEmail(User user, String i }).exceptionallyComposeAsync(throwable -> { - final String emailText = emailService.getLoginEmailTemplate( + val emailText = emailService.getLoginEmailTemplate( name, loginTime, "Unknown"); return emailService.sendEmail(email, subject, emailText) diff --git a/src/main/java/com/webapp/bankingportal/util/JsonUtil.java b/src/main/java/com/webapp/bankingportal/util/JsonUtil.java index 021648e..a0bb2f5 100644 --- a/src/main/java/com/webapp/bankingportal/util/JsonUtil.java +++ b/src/main/java/com/webapp/bankingportal/util/JsonUtil.java @@ -9,7 +9,7 @@ public interface JsonUtil { - public static final Logger logger = LoggerFactory.getLogger(JsonUtil.class); + public static final Logger log = LoggerFactory.getLogger(JsonUtil.class); public static final ObjectMapper objectMapper = new ObjectMapper() .setSerializationInclusion(JsonInclude.Include.NON_NULL); @@ -23,9 +23,10 @@ public static String toJson(Object obj) { try { return objectMapper.writeValueAsString(obj); } catch (JsonProcessingException e) { - logger.error(e.getMessage(), e); + log.error(e.getMessage(), e); } return null; } + } diff --git a/src/main/java/com/webapp/bankingportal/util/LoggedinUser.java b/src/main/java/com/webapp/bankingportal/util/LoggedinUser.java index 16577d2..62bba96 100644 --- a/src/main/java/com/webapp/bankingportal/util/LoggedinUser.java +++ b/src/main/java/com/webapp/bankingportal/util/LoggedinUser.java @@ -1,11 +1,12 @@ package com.webapp.bankingportal.util; -import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import com.webapp.bankingportal.exception.NotFoundException; +import lombok.val; + public class LoggedinUser { /** @@ -16,11 +17,12 @@ public class LoggedinUser { * @throws NotFoundException If there is no user logged in. */ public static String getAccountNumber() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null) { - User principal = (User) authentication.getPrincipal(); - return principal.getUsername(); + val authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + throw new NotFoundException("No user is currently logged in."); } - throw new NotFoundException("No user is currently logged in."); + val principal = (User) authentication.getPrincipal(); + return principal.getUsername(); } + } diff --git a/src/main/java/com/webapp/bankingportal/util/ValidationUtil.java b/src/main/java/com/webapp/bankingportal/util/ValidationUtil.java index dfe82a2..af7a4d5 100644 --- a/src/main/java/com/webapp/bankingportal/util/ValidationUtil.java +++ b/src/main/java/com/webapp/bankingportal/util/ValidationUtil.java @@ -12,10 +12,11 @@ import jakarta.mail.internet.AddressException; import jakarta.mail.internet.InternetAddress; +import lombok.val; public interface ValidationUtil { - public static final Logger logger = LoggerFactory.getLogger(ValidationUtil.class); + public static final Logger log = LoggerFactory.getLogger(ValidationUtil.class); public static final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); public static boolean isValidEmail(String identifier) { @@ -23,7 +24,7 @@ public static boolean isValidEmail(String identifier) { new InternetAddress(identifier).validate(); return true; } catch (AddressException e) { - logger.warn("Invalid email address: {}", identifier); + log.warn("Invalid email address: {}", identifier); } return false; @@ -67,10 +68,10 @@ public static void validatePassword(String password) { throw new UserInvalidException("Password cannot contain any whitespace characters"); } - StringBuilder message = new StringBuilder(); + val message = new StringBuilder(); message.append("Password must contain at least "); - boolean needsComma = false; + var needsComma = false; if (!password.matches(".*[A-Z].*")) { message.append("one uppercase letter"); needsComma = true; @@ -100,7 +101,7 @@ public static void validatePassword(String password) { } if (message.length() > "Password must contain at least ".length()) { - int lastCommaIndex = message.lastIndexOf(","); + val lastCommaIndex = message.lastIndexOf(","); if (lastCommaIndex > -1) { message.replace(lastCommaIndex, lastCommaIndex + 1, " and"); } @@ -156,4 +157,5 @@ public static void validateUserDetails(User user) { validatePassword(user.getPassword()); } + } diff --git a/src/test/java/com/webapp/bankingportal/AccountControllerTests.java b/src/test/java/com/webapp/bankingportal/AccountControllerTests.java index 2ceb796..e6a8b3d 100644 --- a/src/test/java/com/webapp/bankingportal/AccountControllerTests.java +++ b/src/test/java/com/webapp/bankingportal/AccountControllerTests.java @@ -1,7 +1,5 @@ package com.webapp.bankingportal; -import java.util.HashMap; - import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; @@ -15,11 +13,13 @@ import com.webapp.bankingportal.dto.PinUpdateRequest; import com.webapp.bankingportal.util.JsonUtil; +import lombok.val; + public class AccountControllerTests extends BaseTest { @Test public void test_pin_check_without_pin() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); mockMvc.perform(MockMvcRequestBuilders .get("/api/account/pin/check") @@ -33,7 +33,7 @@ public void test_pin_check_without_pin() throws Exception { @Test public void test_pin_check_with_pin() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); mockMvc.perform(MockMvcRequestBuilders .get("/api/account/pin/check") @@ -59,12 +59,9 @@ public void test_pin_create_with_valid_password() throws Exception { @Test public void test_pin_create_with_invalid_password() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - PinRequest pinRequest = new PinRequest(); - pinRequest.setAccountNumber(userDetails.get("accountNumber")); - pinRequest.setPassword(getRandomPassword()); - pinRequest.setPin(getRandomPin()); + val pinRequest = new PinRequest(userDetails.get("accountNumber"), getRandomPin(), getRandomPassword()); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/create") @@ -78,11 +75,9 @@ public void test_pin_create_with_invalid_password() throws Exception { @Test public void test_pin_create_with_missing_password() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - PinRequest pinRequest = new PinRequest(); - pinRequest.setAccountNumber(userDetails.get("accountNumber")); - pinRequest.setPin(getRandomPin()); + val pinRequest = new PinRequest(userDetails.get("accountNumber"), getRandomPin(), null); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/create") @@ -96,11 +91,9 @@ public void test_pin_create_with_missing_password() throws Exception { @Test public void test_pin_create_with_missing_pin() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - PinRequest pinRequest = new PinRequest(); - pinRequest.setAccountNumber(userDetails.get("accountNumber")); - pinRequest.setPassword(userDetails.get("password")); + val pinRequest = new PinRequest(userDetails.get("accountNumber"), null, userDetails.get("password")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/create") @@ -114,12 +107,10 @@ public void test_pin_create_with_missing_pin() throws Exception { @Test public void test_pin_create_with_invalid_short_pin() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - PinRequest pinRequest = new PinRequest(); - pinRequest.setAccountNumber(userDetails.get("accountNumber")); - pinRequest.setPassword(userDetails.get("password")); - pinRequest.setPin(faker.number().digits(3)); + val pinRequest = new PinRequest(userDetails.get("accountNumber"), faker.number().digits(3), + userDetails.get("password")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/create") @@ -133,12 +124,10 @@ public void test_pin_create_with_invalid_short_pin() throws Exception { @Test public void test_pin_create_with_invalid_long_pin() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - PinRequest pinRequest = new PinRequest(); - pinRequest.setAccountNumber(userDetails.get("accountNumber")); - pinRequest.setPassword(userDetails.get("password")); - pinRequest.setPin(faker.number().digits(5)); + val pinRequest = new PinRequest(userDetails.get("accountNumber"), faker.number().digits(5), + userDetails.get("password")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/create") @@ -152,10 +141,7 @@ public void test_pin_create_with_invalid_long_pin() throws Exception { @Test public void test_pin_create_unauthorized_access() throws Exception { - PinRequest pinRequest = new PinRequest(); - pinRequest.setAccountNumber(getRandomAccountNumber()); - pinRequest.setPassword(getRandomPassword()); - pinRequest.setPin(getRandomPin()); + val pinRequest = new PinRequest(getRandomAccountNumber(), getRandomPin(), getRandomPassword()); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/create") @@ -166,13 +152,10 @@ public void test_pin_create_unauthorized_access() throws Exception { @Test public void test_pin_update_with_valid_data() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - PinUpdateRequest pinUpdateRequest = new PinUpdateRequest(); - pinUpdateRequest.setAccountNumber(userDetails.get("accountNumber")); - pinUpdateRequest.setPassword(userDetails.get("password")); - pinUpdateRequest.setOldPin(userDetails.get("pin")); - pinUpdateRequest.setNewPin(getRandomPin()); + val pinUpdateRequest = new PinUpdateRequest(userDetails.get("accountNumber"), userDetails.get("pin"), + getRandomPin(), userDetails.get("password")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/update") @@ -186,13 +169,10 @@ public void test_pin_update_with_valid_data() throws Exception { @Test public void test_pin_update_with_invalid_password() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - PinUpdateRequest pinUpdateRequest = new PinUpdateRequest(); - pinUpdateRequest.setAccountNumber(userDetails.get("accountNumber")); - pinUpdateRequest.setPassword(getRandomPassword()); - pinUpdateRequest.setOldPin(userDetails.get("pin")); - pinUpdateRequest.setNewPin(getRandomPin()); + val pinUpdateRequest = new PinUpdateRequest(userDetails.get("accountNumber"), userDetails.get("pin"), + getRandomPin(), getRandomPassword()); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/update") @@ -206,13 +186,10 @@ public void test_pin_update_with_invalid_password() throws Exception { @Test public void test_pin_update_with_invalid_old_pin() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - PinUpdateRequest pinUpdateRequest = new PinUpdateRequest(); - pinUpdateRequest.setAccountNumber(userDetails.get("accountNumber")); - pinUpdateRequest.setPassword(userDetails.get("password")); - pinUpdateRequest.setOldPin(getRandomPin()); - pinUpdateRequest.setNewPin(getRandomPin()); + val pinUpdateRequest = new PinUpdateRequest(userDetails.get("accountNumber"), getRandomPin(), getRandomPin(), + userDetails.get("password")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/update") @@ -226,13 +203,10 @@ public void test_pin_update_with_invalid_old_pin() throws Exception { @Test public void test_pin_update_with_invalid_new_short_pin() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - PinUpdateRequest pinUpdateRequest = new PinUpdateRequest(); - pinUpdateRequest.setAccountNumber(userDetails.get("accountNumber")); - pinUpdateRequest.setPassword(userDetails.get("password")); - pinUpdateRequest.setOldPin(userDetails.get("pin")); - pinUpdateRequest.setNewPin(faker.number().digits(3)); + val pinUpdateRequest = new PinUpdateRequest(userDetails.get("accountNumber"), userDetails.get("pin"), + faker.number().digits(3), userDetails.get("password")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/update") @@ -246,13 +220,10 @@ public void test_pin_update_with_invalid_new_short_pin() throws Exception { @Test public void test_pin_update_with_invalid_new_long_pin() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - PinUpdateRequest pinUpdateRequest = new PinUpdateRequest(); - pinUpdateRequest.setAccountNumber(userDetails.get("accountNumber")); - pinUpdateRequest.setPassword(userDetails.get("password")); - pinUpdateRequest.setOldPin(userDetails.get("pin")); - pinUpdateRequest.setNewPin(faker.number().digits(5)); + val pinUpdateRequest = new PinUpdateRequest(userDetails.get("accountNumber"), userDetails.get("pin"), + faker.number().digits(5), userDetails.get("password")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/update") @@ -266,12 +237,10 @@ public void test_pin_update_with_invalid_new_long_pin() throws Exception { @Test public void test_pin_update_with_missing_password() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - PinUpdateRequest pinUpdateRequest = new PinUpdateRequest(); - pinUpdateRequest.setAccountNumber(userDetails.get("accountNumber")); - pinUpdateRequest.setOldPin(userDetails.get("pin")); - pinUpdateRequest.setNewPin(getRandomPin()); + val pinUpdateRequest = new PinUpdateRequest(userDetails.get("accountNumber"), userDetails.get("pin"), + getRandomPin(), null); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/update") @@ -285,12 +254,10 @@ public void test_pin_update_with_missing_password() throws Exception { @Test public void test_pin_update_with_missing_old_pin() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - PinUpdateRequest pinUpdateRequest = new PinUpdateRequest(); - pinUpdateRequest.setAccountNumber(userDetails.get("accountNumber")); - pinUpdateRequest.setPassword(userDetails.get("password")); - pinUpdateRequest.setNewPin(getRandomPin()); + val pinUpdateRequest = new PinUpdateRequest(userDetails.get("accountNumber"), null, getRandomPin(), + userDetails.get("password")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/update") @@ -304,12 +271,10 @@ public void test_pin_update_with_missing_old_pin() throws Exception { @Test public void test_pin_update_with_missing_new_pin() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - PinUpdateRequest pinUpdateRequest = new PinUpdateRequest(); - pinUpdateRequest.setAccountNumber(userDetails.get("accountNumber")); - pinUpdateRequest.setPassword(userDetails.get("password")); - pinUpdateRequest.setOldPin(userDetails.get("pin")); + val pinUpdateRequest = new PinUpdateRequest(userDetails.get("accountNumber"), userDetails.get("pin"), null, + userDetails.get("password")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/update") @@ -323,11 +288,8 @@ public void test_pin_update_with_missing_new_pin() throws Exception { @Test public void test_pin_update_with_unauthorized_access() throws Exception { - PinUpdateRequest pinUpdateRequest = new PinUpdateRequest(); - pinUpdateRequest.setAccountNumber(getRandomAccountNumber()); - pinUpdateRequest.setPassword(getRandomPassword()); - pinUpdateRequest.setOldPin(getRandomPin()); - pinUpdateRequest.setNewPin(getRandomPin()); + val pinUpdateRequest = new PinUpdateRequest(getRandomAccountNumber(), getRandomPin(), getRandomPin(), + getRandomPassword()); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/update") @@ -343,12 +305,9 @@ public void test_deposit_with_valid_data() throws Exception { @Test public void test_deposit_with_invalid_pin() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(getRandomPin()); - amountRequest.setAmount(100.0); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), getRandomPin(), 100.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/deposit") @@ -362,12 +321,9 @@ public void test_deposit_with_invalid_pin() throws Exception { @Test public void test_deposit_with_negative_amount() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(userDetails.get("pin")); - amountRequest.setAmount(-100.0); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), userDetails.get("pin"), -100.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/deposit") @@ -381,12 +337,9 @@ public void test_deposit_with_negative_amount() throws Exception { @Test public void test_deposit_with_zero_amount() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(userDetails.get("pin")); - amountRequest.setAmount(0.0); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), userDetails.get("pin"), 0.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/deposit") @@ -400,12 +353,9 @@ public void test_deposit_with_zero_amount() throws Exception { @Test public void test_deposit_with_excessively_large_amount() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(userDetails.get("pin")); - amountRequest.setAmount(1000000.0); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), userDetails.get("pin"), 1000000.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/deposit") @@ -419,11 +369,9 @@ public void test_deposit_with_excessively_large_amount() throws Exception { @Test public void test_deposit_with_missing_pin() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setAmount(100.0); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), null, 100.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/deposit") @@ -435,30 +383,9 @@ public void test_deposit_with_missing_pin() throws Exception { .string("PIN cannot be empty")); } - @Test - public void test_deposit_with_missing_amount() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); - - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(userDetails.get("pin")); - - mockMvc.perform(MockMvcRequestBuilders - .post("/api/account/deposit") - .header("Authorization", "Bearer " + userDetails.get("token")) - .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.toJson(amountRequest))) - .andExpect(MockMvcResultMatchers.status().isBadRequest()) - .andExpect(MockMvcResultMatchers.content() - .string("Amount must be greater than 0")); - } - @Test public void test_deposit_with_unauthorized_access() throws Exception { - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(getRandomAccountNumber()); - amountRequest.setPin(getRandomPin()); - amountRequest.setAmount(100.0); + val amountRequest = new AmountRequest(getRandomAccountNumber(), getRandomPin(), 100.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/deposit") @@ -469,13 +396,10 @@ public void test_deposit_with_unauthorized_access() throws Exception { @Test public void test_withdraw_with_valid_pin_and_amount() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(userDetails.get("pin")); - amountRequest.setAmount(amount); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), userDetails.get("pin"), amount); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/withdraw") @@ -489,12 +413,9 @@ public void test_withdraw_with_valid_pin_and_amount() throws Exception { @Test public void test_withdraw_with_invalid_pin() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(getRandomPin()); - amountRequest.setAmount(100.0); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), getRandomPin(), 100.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/withdraw") @@ -508,12 +429,9 @@ public void test_withdraw_with_invalid_pin() throws Exception { @Test public void test_withdraw_with_negative_amount() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(userDetails.get("pin")); - amountRequest.setAmount(-100.0); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), userDetails.get("pin"), -100.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/withdraw") @@ -527,12 +445,9 @@ public void test_withdraw_with_negative_amount() throws Exception { @Test public void test_withdraw_with_zero_amount() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(userDetails.get("pin")); - amountRequest.setAmount(0.0); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), userDetails.get("pin"), 0.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/withdraw") @@ -546,13 +461,10 @@ public void test_withdraw_with_zero_amount() throws Exception { @Test public void test_withdraw_with_insufficient_funds() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(userDetails.get("pin")); - amountRequest.setAmount(amount * 2); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), userDetails.get("pin"), amount * 2); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/withdraw") @@ -566,11 +478,9 @@ public void test_withdraw_with_insufficient_funds() throws Exception { @Test public void test_withdraw_with_missing_pin() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); + val userDetails = createAndLoginUserWithPin(); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setAmount(100.0); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), null, 100.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/withdraw") @@ -582,30 +492,9 @@ public void test_withdraw_with_missing_pin() throws Exception { .string("PIN cannot be empty")); } - @Test - public void test_withdraw_with_missing_amount() throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); - - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(userDetails.get("pin")); - - mockMvc.perform(MockMvcRequestBuilders - .post("/api/account/withdraw") - .header("Authorization", "Bearer " + userDetails.get("token")) - .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.toJson(amountRequest))) - .andExpect(MockMvcResultMatchers.status().isBadRequest()) - .andExpect(MockMvcResultMatchers.content() - .string("Amount must be greater than 0")); - } - @Test public void test_withdraw_with_unauthorized_access() throws Exception { - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(getRandomAccountNumber()); - amountRequest.setPin(getRandomPin()); - amountRequest.setAmount(100.0); + val amountRequest = new AmountRequest(getRandomAccountNumber(), getRandomPin(), 100.0); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/withdraw") @@ -616,15 +505,11 @@ public void test_withdraw_with_unauthorized_access() throws Exception { @Test public void test_fund_transfer_with_valid_data() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest - .setTargetAccountNumber(createAndLoginUser().get("accountNumber")); - fundTransferRequest.setPin(userDetails.get("pin")); - fundTransferRequest.setAmount(amount); + val fundTransferRequest = new FundTransferRequest(userDetails.get("accountNumber"), + createAndLoginUser().get("accountNumber"), amount, userDetails.get("pin")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/fund-transfer") @@ -638,14 +523,11 @@ public void test_fund_transfer_with_valid_data() throws Exception { @Test public void test_fund_transfer_to_the_same_account() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest.setTargetAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest.setPin(userDetails.get("pin")); - fundTransferRequest.setAmount(amount); + val fundTransferRequest = new FundTransferRequest(userDetails.get("accountNumber"), + userDetails.get("accountNumber"), amount, userDetails.get("pin")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/fund-transfer") @@ -659,15 +541,11 @@ public void test_fund_transfer_to_the_same_account() throws Exception { @Test public void test_fund_transfer_with_invalid_source_account_pin() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest - .setTargetAccountNumber(createAndLoginUser().get("accountNumber")); - fundTransferRequest.setPin(getRandomPin()); - fundTransferRequest.setAmount(amount); + val fundTransferRequest = new FundTransferRequest(userDetails.get("accountNumber"), + createAndLoginUser().get("accountNumber"), amount, getRandomPin()); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/fund-transfer") @@ -681,14 +559,11 @@ public void test_fund_transfer_with_invalid_source_account_pin() throws Exceptio @Test public void test_fund_transfer_with_invalid_target_account() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest.setTargetAccountNumber(getRandomAccountNumber()); - fundTransferRequest.setPin(userDetails.get("pin")); - fundTransferRequest.setAmount(amount); + val fundTransferRequest = new FundTransferRequest(userDetails.get("accountNumber"), getRandomAccountNumber(), + amount, userDetails.get("pin")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/fund-transfer") @@ -702,15 +577,11 @@ public void test_fund_transfer_with_invalid_target_account() throws Exception { @Test public void test_fund_transfer_with_insufficient_funds() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest - .setTargetAccountNumber(createAndLoginUser().get("accountNumber")); - fundTransferRequest.setPin(userDetails.get("pin")); - fundTransferRequest.setAmount(amount * 2); + val fundTransferRequest = new FundTransferRequest(userDetails.get("accountNumber"), + createAndLoginUser().get("accountNumber"), amount * 2, userDetails.get("pin")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/fund-transfer") @@ -724,15 +595,11 @@ public void test_fund_transfer_with_insufficient_funds() throws Exception { @Test public void test_fund_transfer_with_negative_amount() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest - .setTargetAccountNumber(createAndLoginUser().get("accountNumber")); - fundTransferRequest.setPin(userDetails.get("pin")); - fundTransferRequest.setAmount(-amount); + val fundTransferRequest = new FundTransferRequest(userDetails.get("accountNumber"), + createAndLoginUser().get("accountNumber"), -amount, userDetails.get("pin")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/fund-transfer") @@ -746,15 +613,11 @@ public void test_fund_transfer_with_negative_amount() throws Exception { @Test public void test_fund_transfer_with_zero_amount() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest - .setTargetAccountNumber(createAndLoginUser().get("accountNumber")); - fundTransferRequest.setPin(userDetails.get("pin")); - fundTransferRequest.setAmount(0.0); + val fundTransferRequest = new FundTransferRequest(userDetails.get("accountNumber"), + createAndLoginUser().get("accountNumber"), 0.0, userDetails.get("pin")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/fund-transfer") @@ -768,14 +631,11 @@ public void test_fund_transfer_with_zero_amount() throws Exception { @Test public void test_fund_transfer_with_missing_source_account_pin() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest - .setTargetAccountNumber(createAndLoginUser().get("accountNumber")); - fundTransferRequest.setAmount(amount); + val fundTransferRequest = new FundTransferRequest(userDetails.get("accountNumber"), + createAndLoginUser().get("accountNumber"), amount, null); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/fund-transfer") @@ -789,13 +649,11 @@ public void test_fund_transfer_with_missing_source_account_pin() throws Exceptio @Test public void test_fund_transfer_with_missing_target_account() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest.setPin(userDetails.get("pin")); - fundTransferRequest.setAmount(amount); + val fundTransferRequest = new FundTransferRequest(userDetails.get("accountNumber"), null, + amount, userDetails.get("pin")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/fund-transfer") @@ -807,38 +665,13 @@ public void test_fund_transfer_with_missing_target_account() throws Exception { .string("Target account not found")); } - @Test - public void test_fund_transfer_with_missing_amount() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); - - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest - .setTargetAccountNumber(createAndLoginUser().get("accountNumber")); - fundTransferRequest.setPin(userDetails.get("pin")); - - mockMvc.perform(MockMvcRequestBuilders - .post("/api/account/fund-transfer") - .header("Authorization", "Bearer " + userDetails.get("token")) - .contentType(MediaType.APPLICATION_JSON) - .content(JsonUtil.toJson(fundTransferRequest))) - .andExpect(MockMvcResultMatchers.status().isBadRequest()) - .andExpect(MockMvcResultMatchers.content() - .string("Amount must be greater than 0")); - } - @Test public void test_fund_transfer_unauthorized_access() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); - FundTransferRequest fundTransferRequest = new FundTransferRequest(); - fundTransferRequest.setSourceAccountNumber(userDetails.get("accountNumber")); - fundTransferRequest - .setTargetAccountNumber(createAndLoginUser().get("accountNumber")); - fundTransferRequest.setPin(userDetails.get("pin")); - fundTransferRequest.setAmount(amount); + val fundTransferRequest = new FundTransferRequest(userDetails.get("accountNumber"), + createAndLoginUser().get("accountNumber"), amount, userDetails.get("pin")); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/fund-transfer") @@ -849,8 +682,8 @@ public void test_fund_transfer_unauthorized_access() throws Exception { @Test public void test_transactions_with_authorized_access() throws Exception { - double amount = 100.0; - HashMap userDetails = createAndLoginUserWithInitialBalance(amount); + val amount = 100.0; + val userDetails = createAndLoginUserWithInitialBalance(amount); mockMvc.perform(MockMvcRequestBuilders .get("/api/account/transactions") @@ -867,4 +700,5 @@ public void test_transactions_unauthorized_access() throws Exception { .get("/api/account/transactions")) .andExpect(MockMvcResultMatchers.status().isUnauthorized()); } + } diff --git a/src/test/java/com/webapp/bankingportal/AccountServiceTests.java b/src/test/java/com/webapp/bankingportal/AccountServiceTests.java index cd8de22..6c91c31 100644 --- a/src/test/java/com/webapp/bankingportal/AccountServiceTests.java +++ b/src/test/java/com/webapp/bankingportal/AccountServiceTests.java @@ -1,16 +1,13 @@ package com.webapp.bankingportal; -import java.util.HashMap; - import jakarta.validation.ConstraintViolationException; +import lombok.val; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import com.webapp.bankingportal.entity.Account; -import com.webapp.bankingportal.entity.User; import com.webapp.bankingportal.exception.InsufficientBalanceException; import com.webapp.bankingportal.exception.InvalidAmountException; import com.webapp.bankingportal.exception.InvalidPinException; @@ -25,10 +22,10 @@ public class AccountServiceTests extends BaseTest { @Test public void test_create_account_with_valid_user() { - User user = createUser(); + val user = createUser(); userRepository.save(user); - Account account = accountService.createAccount(user); + val account = accountService.createAccount(user); Assertions.assertNotNull(account); Assertions.assertNotNull(account.getAccountNumber()); @@ -43,13 +40,13 @@ public void test_create_account_with_null_user() { @Test public void test_create_pin_with_valid_details() { - HashMap accountDetails = createAccount(); + val accountDetails = createAccount(); - String pin = getRandomPin(); + val pin = getRandomPin(); accountService.createPin(accountDetails.get("accountNumber"), accountDetails.get("password"), pin); - Account account = accountRepository + val account = accountRepository .findByAccountNumber(accountDetails.get("accountNumber")); Assertions.assertTrue(passwordEncoder.matches(pin, account.getPin())); @@ -64,7 +61,7 @@ public void test_create_pin_with_invalid_account_number() { @Test public void test_create_pin_with_invalid_password() { - String accountNumber = createAccount() + val accountNumber = createAccount() .get("accountNumber"); Assertions.assertThrows(UnauthorizedException.class, () -> { @@ -74,7 +71,7 @@ public void test_create_pin_with_invalid_password() { @Test public void test_create_pin_with_existing_pin() { - HashMap accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); + val accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); Assertions.assertThrows(UnauthorizedException.class, () -> { accountService.createPin(accountDetails.get("accountNumber"), accountDetails.get("password"), @@ -85,7 +82,7 @@ public void test_create_pin_with_existing_pin() { @Test public void test_create_pin_with_missing_or_empty_pin() { - HashMap accountDetails = createAccount(); + val accountDetails = createAccount(); Assertions.assertThrows(InvalidPinException.class, () -> { accountService.createPin(accountDetails.get("accountNumber"), accountDetails.get("password"), null); @@ -98,7 +95,7 @@ public void test_create_pin_with_missing_or_empty_pin() { @Test public void test_create_pin_with_invalid_format() { - HashMap accountDetails = createAccount(); + val accountDetails = createAccount(); // Short pin Assertions.assertThrows(InvalidPinException.class, () -> { @@ -121,14 +118,14 @@ public void test_create_pin_with_invalid_format() { @Test public void test_update_pin_with_valid_details() { - HashMap accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); + val accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); - String newPin = getRandomPin(); + val newPin = getRandomPin(); accountService.updatePin(accountDetails.get("accountNumber"), accountDetails.get("pin"), accountDetails.get("password"), newPin); - Account account = accountRepository + val account = accountRepository .findByAccountNumber(accountDetails.get("accountNumber")); Assertions.assertTrue(passwordEncoder.matches(newPin, account.getPin())); @@ -143,7 +140,7 @@ public void test_update_pin_with_invalid_account_number() { @Test public void test_update_pin_with_incorrect_password() { - HashMap accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); + val accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); Assertions.assertThrows(UnauthorizedException.class, () -> { accountService.updatePin(accountDetails.get("accountNumber"), accountDetails.get("pin"), @@ -153,7 +150,7 @@ public void test_update_pin_with_incorrect_password() { @Test public void test_update_pin_with_missing_or_empty_password() { - HashMap accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); + val accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); Assertions.assertThrows(UnauthorizedException.class, () -> { accountService.updatePin(accountDetails.get("accountNumber"), accountDetails.get("pin"), null, @@ -168,7 +165,7 @@ public void test_update_pin_with_missing_or_empty_password() { @Test public void test_update_pin_with_incorrect_pin() { - HashMap accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); + val accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); Assertions.assertThrows(UnauthorizedException.class, () -> { accountService.updatePin(accountDetails.get("accountNumber"), getRandomPin(), @@ -178,7 +175,7 @@ public void test_update_pin_with_incorrect_pin() { @Test public void test_update_pin_for_account_with_no_pin() { - HashMap accountDetails = createAccount(); + val accountDetails = createAccount(); Assertions.assertThrows(UnauthorizedException.class, () -> { accountService.updatePin(accountDetails.get("accountNumber"), getRandomPin(), @@ -188,7 +185,7 @@ public void test_update_pin_for_account_with_no_pin() { @Test public void test_update_pin_with_missing_or_empty_old_pin() { - HashMap accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); + val accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); Assertions.assertThrows(UnauthorizedException.class, () -> { accountService.updatePin(accountDetails.get("accountNumber"), null, accountDetails.get("password"), @@ -203,7 +200,7 @@ public void test_update_pin_with_missing_or_empty_old_pin() { @Test public void test_update_pin_with_missing_or_empty_new_pin() { - HashMap accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); + val accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); Assertions.assertThrows(InvalidPinException.class, () -> { accountService.updatePin(accountDetails.get("accountNumber"), accountDetails.get("pin"), @@ -218,10 +215,10 @@ public void test_update_pin_with_missing_or_empty_new_pin() { @Test public void test_deposit_cash_with_valid_details() { - double balance = 1000.0; - HashMap accountDetails = createAccountWithInitialBalance(balance); + val balance = 1000.0; + val accountDetails = createAccountWithInitialBalance(balance); - Account account = accountRepository + val account = accountRepository .findByAccountNumber(accountDetails.get("accountNumber")); Assertions.assertEquals(balance, account.getBalance(), 0.01); @@ -236,7 +233,7 @@ public void test_deposit_cash_with_invalid_account_number() { @Test public void test_deposit_cash_with_invalid_pin() { - HashMap accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); + val accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); Assertions.assertThrows(UnauthorizedException.class, () -> { accountService.cashDeposit(accountDetails.get("accountNumber"), getRandomPin(), 50.0); @@ -245,7 +242,7 @@ public void test_deposit_cash_with_invalid_pin() { @Test public void test_deposit_invalid_amount() { - HashMap accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); + val accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); // Negative amount Assertions.assertThrows(InvalidAmountException.class, () -> { @@ -270,13 +267,13 @@ public void test_deposit_invalid_amount() { @Test public void test_withdraw_cash_with_valid_details() { - double balance = 1000.0; - HashMap accountDetails = createAccountWithInitialBalance(balance); + val balance = 1000.0; + val accountDetails = createAccountWithInitialBalance(balance); - double withdrawalAmount = 500.0; + val withdrawalAmount = 500.0; accountService.cashWithdrawal(accountDetails.get("accountNumber"), accountDetails.get("pin"), withdrawalAmount); - Account account = accountRepository + val account = accountRepository .findByAccountNumber(accountDetails.get("accountNumber")); Assertions.assertEquals(balance - withdrawalAmount, account.getBalance(), 0.01); @@ -284,7 +281,7 @@ public void test_withdraw_cash_with_valid_details() { @Test public void test_withdraw_insufficient_balance() { - HashMap accountDetails = createAccountWithInitialBalance(500.0); + val accountDetails = createAccountWithInitialBalance(500.0); Assertions.assertThrows(InsufficientBalanceException.class, () -> { accountService.cashWithdrawal(accountDetails.get("accountNumber"), accountDetails.get("pin"), 1000.0); @@ -293,19 +290,19 @@ public void test_withdraw_insufficient_balance() { @Test public void test_transfer_funds_with_valid_accounts() { - double sourceAccountBalance = 1000.0; - HashMap sourceAccountDetails = createAccountWithInitialBalance(sourceAccountBalance); + val sourceAccountBalance = 1000.0; + val sourceAccountDetails = createAccountWithInitialBalance(sourceAccountBalance); - double targetAccountBalance = 500.0; - HashMap targetAccountDetails = createAccountWithInitialBalance(targetAccountBalance); + val targetAccountBalance = 500.0; + val targetAccountDetails = createAccountWithInitialBalance(targetAccountBalance); - double transferAmount = 200; + val transferAmount = 200; accountService.fundTransfer(sourceAccountDetails.get("accountNumber"), targetAccountDetails.get("accountNumber"), sourceAccountDetails.get("pin"), transferAmount); - Account sourceAccount = accountRepository + val sourceAccount = accountRepository .findByAccountNumber(sourceAccountDetails.get("accountNumber")); - Account targetAccount = accountRepository + val targetAccount = accountRepository .findByAccountNumber(targetAccountDetails.get("accountNumber")); Assertions.assertEquals(sourceAccountBalance - transferAmount, sourceAccount.getBalance(), 0.01); @@ -315,7 +312,7 @@ public void test_transfer_funds_with_valid_accounts() { @Test public void test_transfer_non_existent_target_account() { - HashMap accountDetails = createAccountWithInitialBalance(500.0); + val accountDetails = createAccountWithInitialBalance(500.0); Assertions.assertThrows(NotFoundException.class, () -> { accountService.fundTransfer(accountDetails.get("accountNumber"), getRandomAccountNumber(), @@ -325,13 +322,14 @@ public void test_transfer_non_existent_target_account() { @Test public void test_transfer_funds_insufficient_balance() { - HashMap sourceAccountDetails = createAccountWithInitialBalance(500.0); + val sourceAccountDetails = createAccountWithInitialBalance(500.0); - HashMap targetAccountDetails = createAccount(); + val targetAccountDetails = createAccount(); Assertions.assertThrows(InsufficientBalanceException.class, () -> { accountService.fundTransfer(sourceAccountDetails.get("accountNumber"), targetAccountDetails.get("accountNumber"), sourceAccountDetails.get("pin"), 1000.0); }); } + } diff --git a/src/test/java/com/webapp/bankingportal/BaseTest.java b/src/test/java/com/webapp/bankingportal/BaseTest.java index cde2a8a..481973c 100644 --- a/src/test/java/com/webapp/bankingportal/BaseTest.java +++ b/src/test/java/com/webapp/bankingportal/BaseTest.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.util.Date; import java.util.HashMap; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.springframework.beans.factory.annotation.Autowired; @@ -15,7 +14,6 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.transaction.annotation.Transactional; @@ -23,24 +21,23 @@ import com.github.javafaker.Faker; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; -import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; import com.jayway.jsonpath.JsonPath; import com.webapp.bankingportal.dto.AmountRequest; import com.webapp.bankingportal.dto.LoginRequest; import com.webapp.bankingportal.dto.PinRequest; -import com.webapp.bankingportal.entity.Account; import com.webapp.bankingportal.entity.User; import com.webapp.bankingportal.repository.UserRepository; import com.webapp.bankingportal.service.AccountService; import com.webapp.bankingportal.service.TokenService; import com.webapp.bankingportal.util.JsonUtil; -import jakarta.mail.BodyPart; import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import jakarta.mail.internet.MimeMultipart; +import lombok.val; + @SpringBootTest @TestPropertySource(locations = "classpath:application-test.properties") @Transactional @@ -77,20 +74,20 @@ protected static String getRandomPassword() { } protected static String getRandomCountryCode() { - Object[] supportedRegions = phoneNumberUtil.getSupportedRegions().toArray(); - int index = faker.number().numberBetween(0, supportedRegions.length - 1); + val supportedRegions = phoneNumberUtil.getSupportedRegions().toArray(); + val index = faker.number().numberBetween(0, supportedRegions.length - 1); return supportedRegions[index].toString(); } protected static String getRandomPhoneNumber(String region) { - PhoneNumber exampleNumber = phoneNumberUtil.getExampleNumber(region); + val exampleNumber = phoneNumberUtil.getExampleNumber(region); for (int i = 0; i < 100; ++i) { - String nationalNumber = String.valueOf(exampleNumber.getNationalNumber()); - String randomPhoneNumber = faker.number().digits(nationalNumber.length()); + val nationalNumber = String.valueOf(exampleNumber.getNationalNumber()); + val randomPhoneNumber = faker.number().digits(nationalNumber.length()); try { - PhoneNumber phoneNumber = phoneNumberUtil.parse(randomPhoneNumber, region); + val phoneNumber = phoneNumberUtil.parse(randomPhoneNumber, region); if (phoneNumberUtil.isValidNumber(phoneNumber)) { return phoneNumberUtil.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164); } @@ -120,9 +117,9 @@ protected String generateToken(String username, String password, Date expiry) { } protected static User createUser() { - String countryCode = getRandomCountryCode(); - String phoneNumber = getRandomPhoneNumber(countryCode); - User user = new User(); + val countryCode = getRandomCountryCode(); + val phoneNumber = getRandomPhoneNumber(countryCode); + val user = new User(); user.setName(faker.name().fullName()); user.setPassword(getRandomPassword()); user.setEmail(faker.internet().safeEmailAddress()); @@ -133,7 +130,7 @@ protected static User createUser() { } protected User createAndRegisterUser() throws Exception { - User user = createUser(); + val user = createUser(); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/register") .contentType(MediaType.APPLICATION_JSON) @@ -144,23 +141,21 @@ protected User createAndRegisterUser() throws Exception { protected HashMap createAndLoginUser() throws Exception { - User user = createAndRegisterUser(); - String accountNumber = userRepository.findByEmail(user.getEmail()).get().getAccount().getAccountNumber(); - LoginRequest loginRequest = new LoginRequest(); - loginRequest.setAccountNumber(accountNumber); - loginRequest.setPassword(user.getPassword()); + val user = createAndRegisterUser(); + val accountNumber = userRepository.findByEmail(user.getEmail()).get().getAccount().getAccountNumber(); + val loginRequest = new LoginRequest(accountNumber, user.getPassword(), false); - MvcResult loginResult = mockMvc.perform(MockMvcRequestBuilders + val loginResult = mockMvc.perform(MockMvcRequestBuilders .post("/api/users/login") .contentType(MediaType.APPLICATION_JSON) .content(JsonUtil.toJson(loginRequest))) .andExpect(MockMvcResultMatchers.status().isOk()) .andReturn(); - String responseBody = loginResult.getResponse().getContentAsString(); + val responseBody = loginResult.getResponse().getContentAsString(); String token = JsonPath.read(responseBody, "$.token"); - HashMap userDetails = new HashMap<>(); + val userDetails = new HashMap(); userDetails.put("name", user.getName()); userDetails.put("email", user.getEmail()); userDetails.put("countryCode", user.getCountryCode()); @@ -175,14 +170,11 @@ protected HashMap createAndLoginUser() protected HashMap createAndLoginUserWithPin() throws Exception { - HashMap userDetails = createAndLoginUser(); - String accountNumber = userDetails.get("accountNumber"); - String password = userDetails.get("password"); + val userDetails = createAndLoginUser(); + val accountNumber = userDetails.get("accountNumber"); + val password = userDetails.get("password"); - PinRequest pinRequest = new PinRequest(); - pinRequest.setAccountNumber(accountNumber); - pinRequest.setPassword(password); - pinRequest.setPin(getRandomPin()); + val pinRequest = new PinRequest(accountNumber, getRandomPin(), password); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/create") @@ -192,16 +184,13 @@ protected HashMap createAndLoginUserWithPin() .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.msg").value("PIN created successfully")); - userDetails.put("pin", pinRequest.getPin()); + userDetails.put("pin", pinRequest.pin()); return userDetails; } protected HashMap createAndLoginUserWithInitialBalance(double amount) throws Exception { - HashMap userDetails = createAndLoginUserWithPin(); - AmountRequest amountRequest = new AmountRequest(); - amountRequest.setAccountNumber(userDetails.get("accountNumber")); - amountRequest.setPin(userDetails.get("pin")); - amountRequest.setAmount(amount); + val userDetails = createAndLoginUserWithPin(); + val amountRequest = new AmountRequest(userDetails.get("accountNumber"), userDetails.get("pin"), amount); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/deposit") @@ -215,20 +204,20 @@ protected HashMap createAndLoginUserWithInitialBalance(double am } protected HashMap createAccount() { - HashMap accountDetails = new HashMap<>(); - User user = createUser(); + val accountDetails = new HashMap(); + val user = createUser(); accountDetails.put("password", user.getPassword()); user.setPassword(passwordEncoder.encode(user.getPassword())); userRepository.save(user); - Account account = accountService.createAccount(user); + val account = accountService.createAccount(user); accountDetails.put("accountNumber", account.getAccountNumber()); return accountDetails; } protected HashMap createAccountWithPin(PasswordEncoder passwordEncoder, UserRepository userRepository, AccountService accountService) { - HashMap accountDetails = createAccount(); + val accountDetails = createAccount(); accountDetails.put("pin", getRandomPin()); accountService.createPin(accountDetails.get("accountNumber"), accountDetails.get("password"), accountDetails.get("pin")); @@ -236,7 +225,7 @@ protected HashMap createAccountWithPin(PasswordEncoder passwordE } protected HashMap createAccountWithInitialBalance(double amount) { - HashMap accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); + val accountDetails = createAccountWithPin(passwordEncoder, userRepository, accountService); accountService.cashDeposit(accountDetails.get("accountNumber"), accountDetails.get("pin"), amount); return accountDetails; } @@ -244,11 +233,11 @@ protected HashMap createAccountWithInitialBalance(double amount) protected static String getTextFromMimeMultipart(MimeMultipart mimeMultipart) throws MessagingException, IOException { - StringBuilder result = new StringBuilder(); - int count = mimeMultipart.getCount(); + val result = new StringBuilder(); + val count = mimeMultipart.getCount(); for (int i = 0; i < count; i++) { - BodyPart bodyPart = mimeMultipart.getBodyPart(i); + val bodyPart = mimeMultipart.getBodyPart(i); if (bodyPart.isMimeType("text/html")) { result.append(bodyPart.getContent()); break; @@ -266,13 +255,14 @@ protected static String getTextFromMimeMultipart(MimeMultipart mimeMultipart) protected static String getOtpFromEmail(MimeMessage message) throws IOException, MessagingException { - String content = getTextFromMimeMultipart((MimeMultipart) message.getContent()); - Pattern pattern = Pattern.compile("(\\d+)"); - Matcher matcher = pattern.matcher(content); + val content = getTextFromMimeMultipart((MimeMultipart) message.getContent()); + val pattern = Pattern.compile("(\\d+)"); + val matcher = pattern.matcher(content); if (matcher.find()) { return matcher.group(1); } throw new RuntimeException("OTP not found in email"); } + } diff --git a/src/test/java/com/webapp/bankingportal/DashboardServiceTests.java b/src/test/java/com/webapp/bankingportal/DashboardServiceTests.java index b2bf79d..390910e 100644 --- a/src/test/java/com/webapp/bankingportal/DashboardServiceTests.java +++ b/src/test/java/com/webapp/bankingportal/DashboardServiceTests.java @@ -4,11 +4,11 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import com.webapp.bankingportal.dto.AccountResponse; -import com.webapp.bankingportal.dto.UserResponse; import com.webapp.bankingportal.exception.NotFoundException; import com.webapp.bankingportal.service.DashboardService; +import lombok.val; + public class DashboardServiceTests extends BaseTest { @Autowired @@ -16,15 +16,15 @@ public class DashboardServiceTests extends BaseTest { @Test public void test_get_user_details_with_valid_account_number() throws Exception { - String accountNumber = createAndLoginUser().get("accountNumber"); - UserResponse userResponse = dashboardService.getUserDetails(accountNumber); + val accountNumber = createAndLoginUser().get("accountNumber"); + val userResponse = dashboardService.getUserDetails(accountNumber); Assertions.assertNotNull(userResponse); Assertions.assertEquals(accountNumber, userResponse.getAccountNumber()); } @Test public void test_get_user_details_with_invalid_account_number() throws Exception { - String accountNumber = "123456789"; + val accountNumber = "123456789"; Assertions.assertThrows(NotFoundException.class, () -> { dashboardService.getUserDetails(accountNumber); }); @@ -32,17 +32,18 @@ public void test_get_user_details_with_invalid_account_number() throws Exception @Test public void test_get_account_details_with_valid_account_number() throws Exception { - String accountNumber = createAndLoginUser().get("accountNumber"); - AccountResponse accountResponse = dashboardService.getAccountDetails(accountNumber); + val accountNumber = createAndLoginUser().get("accountNumber"); + val accountResponse = dashboardService.getAccountDetails(accountNumber); Assertions.assertNotNull(accountResponse); Assertions.assertEquals(accountNumber, accountResponse.getAccountNumber()); } @Test public void test_get_account_details_with_invalid_account_number() throws Exception { - String accountNumber = "123456789"; + val accountNumber = "123456789"; Assertions.assertThrows(NotFoundException.class, () -> { dashboardService.getAccountDetails(accountNumber); }); } + } diff --git a/src/test/java/com/webapp/bankingportal/EmailServiceMock.java b/src/test/java/com/webapp/bankingportal/EmailServiceMock.java deleted file mode 100644 index 1289477..0000000 --- a/src/test/java/com/webapp/bankingportal/EmailServiceMock.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.webapp.bankingportal; - -import java.util.concurrent.CompletableFuture; - -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; - -import com.webapp.bankingportal.service.EmailService; - -@Service -public class EmailServiceMock implements EmailService { - - @Override - @Async - public CompletableFuture sendEmail(String to, String subject, String text) { - CompletableFuture future = new CompletableFuture<>(); - future.complete(null); // Indicate that the email sending is successful - - return future; - } - - @Override - public String getLoginEmailTemplate(String name, String loginTime, String loginLocation) { - return null; - } - - @Override - public String getOtpLoginEmailTemplate(String name, String accountNumber, String otp) { - return null; - } -} diff --git a/src/test/java/com/webapp/bankingportal/GreenMailJavaMailSender.java b/src/test/java/com/webapp/bankingportal/GreenMailJavaMailSender.java index d6ba096..3d4fb65 100644 --- a/src/test/java/com/webapp/bankingportal/GreenMailJavaMailSender.java +++ b/src/test/java/com/webapp/bankingportal/GreenMailJavaMailSender.java @@ -2,12 +2,13 @@ import java.util.Date; import java.util.LinkedHashMap; -import java.util.Map; import jakarta.mail.AuthenticationFailedException; +import jakarta.mail.MessagingException; import jakarta.mail.Session; -import jakarta.mail.Transport; import jakarta.mail.internet.MimeMessage; +import lombok.val; + import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.mail.MailAuthenticationException; @@ -43,67 +44,21 @@ public class GreenMailJavaMailSender extends JavaMailSenderImpl { @Override protected void doSend(@NonNull MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) throws MailException { - Map failedMessages = new LinkedHashMap<>(); - Transport transport = null; - try { - for (int i = 0; i < mimeMessages.length; i++) { + val failedMessages = new LinkedHashMap(); - // Check transport connection first... - if (transport == null || !transport.isConnected()) { - if (transport != null) { - try { - transport.close(); - } catch (Exception ex) { - // Ignore - we're reconnecting anyway - } - transport = null; - } - try { - transport = connectTransport(); - } catch (AuthenticationFailedException ex) { - throw new MailAuthenticationException(ex); - } catch (Exception ex) { - // Effectively, all remaining messages failed... - for (int j = i; j < mimeMessages.length; j++) { - Object original = (originalMessages != null ? originalMessages[j] : mimeMessages[j]); - failedMessages.put(original, ex); - } - throw new MailSendException("Mail server connection failed", ex, failedMessages); - } - } - - // Send message via current transport... - MimeMessage mimeMessage = mimeMessages[i]; - try { - if (mimeMessage.getSentDate() == null) { - mimeMessage.setSentDate(new Date()); - } - String messageId = mimeMessage.getMessageID(); - mimeMessage.saveChanges(); - if (messageId != null) { - // Preserve explicitly specified message id... - mimeMessage.setHeader(HEADER_MESSAGE_ID, messageId); - } - GreenMailUtil.sendMimeMessage(mimeMessage); - } catch (Exception ex) { - Object original = (originalMessages != null ? originalMessages[i] : mimeMessage); - failedMessages.put(original, ex); - } - } - } finally { - try { - if (transport != null) { - transport.close(); - } - } catch (Exception ex) { - if (!failedMessages.isEmpty()) { - throw new MailSendException("Failed to close server connection after message failures", ex, - failedMessages); - } else { - throw new MailSendException("Failed to close server connection after message sending", ex); - } + try (val transport = connectTransport()) { + + for (int i = 0; i < mimeMessages.length; i++) { + val mimeMessage = mimeMessages[i]; + sendMessage(mimeMessage, originalMessages, i, failedMessages); } + + } catch (AuthenticationFailedException ex) { + throw new MailAuthenticationException(ex); + + } catch (MessagingException ex) { + handleSendException(ex, mimeMessages, originalMessages, failedMessages); } if (!failedMessages.isEmpty()) { @@ -111,6 +66,53 @@ protected void doSend(@NonNull MimeMessage[] mimeMessages, @Nullable Object[] or } } + private void sendMessage( + MimeMessage mimeMessage, Object[] originalMessages, + int index, + LinkedHashMap failedMessages) { + + try { + prepareMimeMessage(mimeMessage); + GreenMailUtil.sendMimeMessage(mimeMessage); + + } catch (MessagingException ex) { + Object original = mimeMessage; + if (originalMessages != null) { + original = originalMessages[index]; + } + failedMessages.put(original, ex); + } + } + + private void prepareMimeMessage(MimeMessage mimeMessage) throws MessagingException { + if (mimeMessage.getSentDate() == null) { + mimeMessage.setSentDate(new Date()); + } + mimeMessage.saveChanges(); + + val messageId = mimeMessage.getMessageID(); + if (messageId != null) { + mimeMessage.setHeader(HEADER_MESSAGE_ID, messageId); + } + } + + private void handleSendException( + Exception ex, + MimeMessage[] mimeMessages, + Object[] originalMessages, + LinkedHashMap failedMessages) throws MailSendException { + + for (int j = 0; j < mimeMessages.length; j++) { + Object original = mimeMessages[j]; + if (originalMessages != null) { + original = originalMessages[j]; + } + failedMessages.put(original, ex); + } + + throw new MailSendException("Mail server connection failed", ex, failedMessages); + } + public static MimeMessage[] getReceivedMessages() { return greenMail.getReceivedMessages(); } diff --git a/src/test/java/com/webapp/bankingportal/TestUtil.java b/src/test/java/com/webapp/bankingportal/TestUtil.java deleted file mode 100644 index 4a98e94..0000000 --- a/src/test/java/com/webapp/bankingportal/TestUtil.java +++ /dev/null @@ -1,183 +0,0 @@ -package com.webapp.bankingportal; - -import java.util.HashMap; - -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -import org.springframework.test.web.servlet.result.MockMvcResultMatchers; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.javafaker.Faker; -import com.google.i18n.phonenumbers.NumberParseException; -import com.google.i18n.phonenumbers.PhoneNumberUtil; -import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; -import com.jayway.jsonpath.JsonPath; - -import com.webapp.bankingportal.dto.LoginRequest; -import com.webapp.bankingportal.dto.PinRequest; -import com.webapp.bankingportal.entity.User; -import com.webapp.bankingportal.repository.UserRepository; - -public class TestUtil { - - private MockMvc mockMvc; - private UserRepository userRepository; - - public static final int MIN_PASSWORD_LENGTH = 8; - public static final int MAX_PASSWORD_LENGTH = 127; - - public static final Faker faker = new Faker(); - public static final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); - public static final ObjectMapper objectMapper = new ObjectMapper(); - static { - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - } - - public TestUtil(MockMvc mockMvc, UserRepository userRepository) { - this.mockMvc = mockMvc; - this.userRepository = userRepository; - } - - public static String getRandomAccountNumber() { - return faker.lorem().characters( - 6, - false, - true); - } - - public static String getRandomPassword() { - return "!" + faker.internet().password( - MAX_PASSWORD_LENGTH - 2, - MAX_PASSWORD_LENGTH - 1, - true, - true); - } - - public static String getRandomCountryCode() { - Object[] supportedRegions = phoneNumberUtil.getSupportedRegions().toArray(); - int index = faker.number().numberBetween(0, supportedRegions.length - 1); - - return supportedRegions[index].toString(); - } - - public static String getRandomPhoneNumber(String region) { - PhoneNumber exampleNumber = phoneNumberUtil.getExampleNumber(region); - - for (int i = 0; i < 100; ++i) { - String nationalNumber = String.valueOf(exampleNumber.getNationalNumber()); - String randomPhoneNumber = faker.number().digits(nationalNumber.length()); - - try { - PhoneNumber phoneNumber = phoneNumberUtil.parse( - randomPhoneNumber, region); - - if (phoneNumberUtil.isValidNumber(phoneNumber)) { - return phoneNumberUtil.format(phoneNumber, - PhoneNumberUtil.PhoneNumberFormat.E164); - } - } catch (NumberParseException e) { - // Continue to next attempt if parsing fails - } - } - - return phoneNumberUtil.format(exampleNumber, - PhoneNumberUtil.PhoneNumberFormat.E164); - } - - public static String getRandomOtp() { - return faker.number().digits(6); - } - - public static String getRandomPin() { - return faker.number().digits(4); - } - - public static User createUser() { - String countryCode = getRandomCountryCode(); - String phoneNumber = getRandomPhoneNumber(countryCode); - User user = new User(); - - user.setName(faker.name().fullName()); - user.setPassword(getRandomPassword()); - user.setEmail(faker.internet().safeEmailAddress()); - user.setAddress(faker.address().fullAddress()); - user.setCountryCode(countryCode); - user.setPhoneNumber(phoneNumber); - - return user; - } - - public User createAndRegisterUser() throws Exception { - User user = createUser(); - - mockMvc.perform(MockMvcRequestBuilders - .post("/api/users/register") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(user))) - .andExpect(MockMvcResultMatchers.status().isOk()); - - return user; - } - - public HashMap createAndLoginUser() throws Exception { - User user = createAndRegisterUser(); - String accountNumber = userRepository - .findByEmail(user.getEmail()) - .get() - .getAccount() - .getAccountNumber(); - - LoginRequest loginRequest = new LoginRequest(); - loginRequest.setAccountNumber(accountNumber); - loginRequest.setPassword(user.getPassword()); - - MvcResult loginResult = mockMvc.perform(MockMvcRequestBuilders - .post("/api/users/login") - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(loginRequest))) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andReturn(); - - String responseBody = loginResult.getResponse().getContentAsString(); - String token = JsonPath.read(responseBody, "$.token"); - - HashMap userDetails = new HashMap<>(); - userDetails.put("name", user.getName()); - userDetails.put("email", user.getEmail()); - userDetails.put("countryCode", user.getCountryCode()); - userDetails.put("phoneNumber", user.getPhoneNumber()); - userDetails.put("address", user.getAddress()); - userDetails.put("accountNumber", accountNumber); - userDetails.put("password", user.getPassword()); - userDetails.put("token", token); - - return userDetails; - } - - public HashMap createAndLoginUserWithPin() throws Exception { - HashMap userDetails = createAndLoginUser(); - String accountNumber = userDetails.get("accountNumber"); - String password = userDetails.get("password"); - - PinRequest pinRequest = new PinRequest(); - pinRequest.setAccountNumber(accountNumber); - pinRequest.setPassword(password); - pinRequest.setPin(TestUtil.getRandomPin()); - - mockMvc.perform(MockMvcRequestBuilders - .post("/api/account/pin/create") - .header("Authorization", "Bearer " + userDetails.get("token")) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(pinRequest))) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.jsonPath("$.msg") - .value("PIN created successfully")); - - userDetails.put("pin", pinRequest.getPin()); - - return userDetails; - } -} diff --git a/src/test/java/com/webapp/bankingportal/TokenServiceTests.java b/src/test/java/com/webapp/bankingportal/TokenServiceTests.java index 563c884..e675944 100644 --- a/src/test/java/com/webapp/bankingportal/TokenServiceTests.java +++ b/src/test/java/com/webapp/bankingportal/TokenServiceTests.java @@ -1,7 +1,6 @@ package com.webapp.bankingportal; import java.util.Date; -import java.util.HashMap; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -14,6 +13,8 @@ import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; +import lombok.val; + public class TokenServiceTests extends BaseTest { @Autowired @@ -21,14 +22,14 @@ public class TokenServiceTests extends BaseTest { @Test public void test_validate_token_with_valid_token() throws Exception { - String token = createAndLoginUser().get("token"); + val token = createAndLoginUser().get("token"); tokenService.validateToken(token); tokenService.invalidateToken(token); } @Test public void test_validate_token_with_invalid_token() throws Exception { - String token = generateToken(getRandomAccountNumber(), getRandomPassword()); + val token = generateToken(getRandomAccountNumber(), getRandomPassword()); Assertions.assertThrows(InvalidTokenException.class, () -> tokenService.validateToken(token), @@ -37,7 +38,7 @@ public void test_validate_token_with_invalid_token() throws Exception { @Test public void test_invalidate_token_with_valid_token() throws Exception { - String token = createAndLoginUser().get("token"); + val token = createAndLoginUser().get("token"); Assertions.assertNotNull(tokenRepository.findByToken(token)); tokenService.invalidateToken(token); @@ -46,14 +47,14 @@ public void test_invalidate_token_with_valid_token() throws Exception { @Test public void test_invalidate_token_with_invalid_token() throws Exception { - String token = generateToken(getRandomAccountNumber(), getRandomPassword()); + val token = generateToken(getRandomAccountNumber(), getRandomPassword()); tokenService.invalidateToken(token); } @Test public void test_save_token_with_valid_token() throws Exception { - HashMap accountDetails = createAccount(); - String token = generateToken( + val accountDetails = createAccount(); + val token = generateToken( accountDetails.get("accountNumber"), accountDetails.get("password")); @@ -66,7 +67,7 @@ public void test_save_token_with_valid_token() throws Exception { @Test public void test_save_token_with_duplicate_token() throws Exception { - String token = createAndLoginUser().get("token"); + val token = createAndLoginUser().get("token"); Assertions.assertNotNull(tokenRepository.findByToken(token)); Assertions.assertThrows(InvalidTokenException.class, () -> tokenService.saveToken(token), @@ -78,17 +79,17 @@ public void test_save_token_with_duplicate_token() throws Exception { @Test public void test_get_username_from_token_with_valid_token() throws Exception { - HashMap userDetails = createAndLoginUser(); - String token = userDetails.get("token"); - String accountNumber = userDetails.get("accountNumber"); - String username = tokenService.getUsernameFromToken(token); + val userDetails = createAndLoginUser(); + val token = userDetails.get("token"); + val accountNumber = userDetails.get("accountNumber"); + val username = tokenService.getUsernameFromToken(token); Assertions.assertEquals(accountNumber, username); } @Test public void test_get_username_from_token_with_expired_token() { - String token = generateToken( + val token = generateToken( getRandomAccountNumber(), getRandomPassword(), new Date()); Assertions.assertThrows(InvalidTokenException.class, @@ -98,7 +99,7 @@ public void test_get_username_from_token_with_expired_token() { @Test public void test_get_username_from_token_with_unsigned_token() { - String token = Jwts.builder().setSubject(getRandomAccountNumber()) + val token = Jwts.builder().setSubject(getRandomAccountNumber()) .setIssuedAt(new Date()).compact(); Assertions.assertThrows(InvalidTokenException.class, @@ -108,7 +109,7 @@ public void test_get_username_from_token_with_unsigned_token() { @Test public void test_get_username_from_token_with_malformed_token() { - String token = "malformed"; + val token = "malformed"; Assertions.assertThrows(InvalidTokenException.class, () -> tokenService.getUsernameFromToken(token), @@ -117,7 +118,7 @@ public void test_get_username_from_token_with_malformed_token() { @Test public void test_get_username_from_token_with_invalid_signature() { - String token = Jwts.builder().setSubject(getRandomAccountNumber()) + val token = Jwts.builder().setSubject(getRandomAccountNumber()) .setIssuedAt(new Date()) .signWith(SignatureAlgorithm.HS256, "invalid") .compact(); @@ -133,4 +134,5 @@ public void test_get_username_from_token_with_empty_token() { () -> tokenService.getUsernameFromToken(null), "Token is empty"); } + } diff --git a/src/test/java/com/webapp/bankingportal/UserControllerTests.java b/src/test/java/com/webapp/bankingportal/UserControllerTests.java index c70c781..e9794f2 100644 --- a/src/test/java/com/webapp/bankingportal/UserControllerTests.java +++ b/src/test/java/com/webapp/bankingportal/UserControllerTests.java @@ -1,16 +1,11 @@ package com.webapp.bankingportal; -import java.util.HashMap; - import org.hamcrest.core.StringContains; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; @@ -20,11 +15,10 @@ import com.webapp.bankingportal.dto.OtpRequest; import com.webapp.bankingportal.dto.OtpVerificationRequest; import com.webapp.bankingportal.dto.PinRequest; -import com.webapp.bankingportal.entity.User; import com.webapp.bankingportal.service.TokenService; import com.webapp.bankingportal.util.JsonUtil; -import jakarta.mail.internet.MimeMessage; +import lombok.val; public class UserControllerTests extends BaseTest { @@ -38,7 +32,7 @@ public void test_register_user_with_valid_details() throws Exception { @Test public void test_register_user_with_empty_name() throws Exception { - User user = createUser(); + val user = createUser(); user.setName(""); mockMvc.perform(MockMvcRequestBuilders @@ -52,7 +46,7 @@ public void test_register_user_with_empty_name() throws Exception { @Test public void test_register_user_with_missing_name() throws Exception { - User user = createUser(); + val user = createUser(); user.setName(null); mockMvc.perform(MockMvcRequestBuilders @@ -66,7 +60,7 @@ public void test_register_user_with_missing_name() throws Exception { @Test public void test_register_user_with_empty_email() throws Exception { - User user = createUser(); + val user = createUser(); user.setEmail(""); mockMvc.perform(MockMvcRequestBuilders @@ -80,7 +74,7 @@ public void test_register_user_with_empty_email() throws Exception { @Test public void test_register_user_with_missing_email() throws Exception { - User user = createUser(); + val user = createUser(); user.setEmail(null); mockMvc.perform(MockMvcRequestBuilders @@ -94,7 +88,7 @@ public void test_register_user_with_missing_email() throws Exception { @Test public void test_register_user_with_empty_country_code() throws Exception { - User user = createUser(); + val user = createUser(); user.setCountryCode(""); mockMvc.perform(MockMvcRequestBuilders @@ -108,7 +102,7 @@ public void test_register_user_with_empty_country_code() throws Exception { @Test public void test_register_user_with_missing_country_code() throws Exception { - User user = createUser(); + val user = createUser(); user.setCountryCode(null); mockMvc.perform(MockMvcRequestBuilders @@ -122,7 +116,7 @@ public void test_register_user_with_missing_country_code() throws Exception { @Test public void test_register_user_with_empty_phone_number() throws Exception { - User user = createUser(); + val user = createUser(); user.setPhoneNumber(""); mockMvc.perform(MockMvcRequestBuilders @@ -136,7 +130,7 @@ public void test_register_user_with_empty_phone_number() throws Exception { @Test public void test_register_user_with_missing_phone_number() throws Exception { - User user = createUser(); + val user = createUser(); user.setPhoneNumber(null); mockMvc.perform(MockMvcRequestBuilders @@ -150,7 +144,7 @@ public void test_register_user_with_missing_phone_number() throws Exception { @Test public void test_register_user_with_empty_address() throws Exception { - User user = createUser(); + val user = createUser(); user.setAddress(""); mockMvc.perform(MockMvcRequestBuilders @@ -164,7 +158,7 @@ public void test_register_user_with_empty_address() throws Exception { @Test public void test_register_user_with_missing_address() throws Exception { - User user = createUser(); + val user = createUser(); user.setAddress(null); mockMvc.perform(MockMvcRequestBuilders @@ -178,8 +172,8 @@ public void test_register_user_with_missing_address() throws Exception { @Test public void test_register_user_with_duplicate_email() throws Exception { - User user1 = createAndRegisterUser(); - User user2 = createUser(); + val user1 = createAndRegisterUser(); + val user2 = createUser(); user2.setEmail(user1.getEmail()); mockMvc.perform(MockMvcRequestBuilders @@ -193,8 +187,8 @@ public void test_register_user_with_duplicate_email() throws Exception { @Test public void test_register_user_with_duplicate_phone_number() throws Exception { - User user1 = createAndRegisterUser(); - User user2 = createUser(); + val user1 = createAndRegisterUser(); + val user2 = createUser(); user2.setPhoneNumber(user1.getPhoneNumber()); mockMvc.perform(MockMvcRequestBuilders @@ -208,7 +202,7 @@ public void test_register_user_with_duplicate_phone_number() throws Exception { @Test public void test_register_user_with_invalid_email() throws Exception { - User user = createUser(); + val user = createUser(); user.setEmail(faker.lorem().word()); mockMvc.perform(MockMvcRequestBuilders @@ -222,7 +216,7 @@ public void test_register_user_with_invalid_email() throws Exception { @Test public void test_register_user_with_invalid_country_code() throws Exception { - User user = createUser(); + val user = createUser(); user.setCountryCode(faker.lorem().word()); mockMvc.perform(MockMvcRequestBuilders @@ -236,7 +230,7 @@ public void test_register_user_with_invalid_country_code() throws Exception { @Test public void test_register_user_with_invalid_phone_number() throws Exception { - User user = createUser(); + val user = createUser(); user.setPhoneNumber(faker.number().digits(3)); mockMvc.perform(MockMvcRequestBuilders @@ -250,7 +244,7 @@ public void test_register_user_with_invalid_phone_number() throws Exception { @Test public void test_register_user_with_empty_password() throws Exception { - User user = createUser(); + val user = createUser(); user.setPassword(""); mockMvc.perform(MockMvcRequestBuilders @@ -264,7 +258,7 @@ public void test_register_user_with_empty_password() throws Exception { @Test public void test_register_user_with_missing_password() throws Exception { - User user = createUser(); + val user = createUser(); user.setPassword(null); mockMvc.perform(MockMvcRequestBuilders @@ -278,7 +272,7 @@ public void test_register_user_with_missing_password() throws Exception { @Test public void test_register_user_with_short_password() throws Exception { - User user = createUser(); + val user = createUser(); user.setPassword(faker.internet().password(1, MIN_PASSWORD_LENGTH - 1, true, true)); mockMvc.perform(MockMvcRequestBuilders @@ -292,7 +286,7 @@ public void test_register_user_with_short_password() throws Exception { @Test public void test_register_user_with_long_password() throws Exception { - User user = createUser(); + val user = createUser(); user.setPassword(faker.internet().password(MAX_PASSWORD_LENGTH + 1, MAX_PASSWORD_LENGTH * 2, true, true)); mockMvc.perform(MockMvcRequestBuilders @@ -306,7 +300,7 @@ public void test_register_user_with_long_password() throws Exception { @Test public void test_register_user_with_password_containing_whitespace() throws Exception { - User user = createUser(); + val user = createUser(); user.setPassword(faker.lorem().sentence()); mockMvc.perform(MockMvcRequestBuilders @@ -320,7 +314,7 @@ public void test_register_user_with_password_containing_whitespace() throws Exce @Test public void test_register_user_with_password_missing_uppercase_letters() throws Exception { - User user = createUser(); + val user = createUser(); user.setPassword(faker.internet().password(MAX_PASSWORD_LENGTH - 1, MAX_PASSWORD_LENGTH, false, true)); mockMvc.perform(MockMvcRequestBuilders @@ -334,7 +328,7 @@ public void test_register_user_with_password_missing_uppercase_letters() throws @Test public void test_register_user_with_password_missing_lowercase_letters() throws Exception { - User user = createUser(); + val user = createUser(); user.setPassword(faker.internet().password(MAX_PASSWORD_LENGTH - 1, MAX_PASSWORD_LENGTH, true, true) .toUpperCase()); @@ -349,7 +343,7 @@ public void test_register_user_with_password_missing_lowercase_letters() throws @Test public void test_register_user_with_password_missing_digits() throws Exception { - User user = createUser(); + val user = createUser(); user.setPassword("!" + faker.lorem().characters(MAX_PASSWORD_LENGTH - 1, true, false)); mockMvc.perform(MockMvcRequestBuilders @@ -363,7 +357,7 @@ public void test_register_user_with_password_missing_digits() throws Exception { @Test public void test_register_user_with_password_missing_special_characters() throws Exception { - User user = createUser(); + val user = createUser(); user.setPassword(faker.internet().password(MAX_PASSWORD_LENGTH - 1, MAX_PASSWORD_LENGTH, true, false)); mockMvc.perform(MockMvcRequestBuilders @@ -382,9 +376,7 @@ public void test_login_with_valid_credentials() throws Exception { @Test public void test_login_with_invalid_account_number() throws Exception { - LoginRequest loginRequest = new LoginRequest(); - loginRequest.setAccountNumber(getRandomAccountNumber()); - loginRequest.setPassword(getRandomPassword()); + val loginRequest = new LoginRequest(getRandomAccountNumber(), getRandomPassword(), false); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/login") @@ -395,16 +387,14 @@ public void test_login_with_invalid_account_number() throws Exception { @Test public void test_login_with_invalid_password() throws Exception { - User user = createAndRegisterUser(); - String accountNumber = userRepository + val user = createAndRegisterUser(); + val accountNumber = userRepository .findByEmail(user.getEmail()) .get() .getAccount() .getAccountNumber(); - LoginRequest loginRequest = new LoginRequest(); - loginRequest.setAccountNumber(accountNumber); - loginRequest.setPassword(getRandomPassword()); + val loginRequest = new LoginRequest(accountNumber, getRandomPassword(), false); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/login") @@ -415,9 +405,7 @@ public void test_login_with_invalid_password() throws Exception { @Test public void test_login_with_missing_account_number() throws Exception { - LoginRequest loginRequest = new LoginRequest(); - loginRequest.setAccountNumber(""); - loginRequest.setPassword(getRandomPassword()); + val loginRequest = new LoginRequest("", getRandomPassword(), false); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/login") @@ -428,16 +416,14 @@ public void test_login_with_missing_account_number() throws Exception { @Test public void test_login_with_missing_password() throws Exception { - User user = createAndRegisterUser(); - String accountNumber = userRepository + val user = createAndRegisterUser(); + val accountNumber = userRepository .findByEmail(user.getEmail()) .get() .getAccount() .getAccountNumber(); - LoginRequest loginRequest = new LoginRequest(); - loginRequest.setAccountNumber(accountNumber); - loginRequest.setPassword(""); + val loginRequest = new LoginRequest(accountNumber, "", false); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/login") @@ -448,15 +434,14 @@ public void test_login_with_missing_password() throws Exception { @Test public void test_generate_otp_with_valid_account_number() throws Exception { - User user = createAndRegisterUser(); - String accountNumber = userRepository + val user = createAndRegisterUser(); + val accountNumber = userRepository .findByEmail(user.getEmail()) .get() .getAccount() .getAccountNumber(); - OtpRequest otpRequest = new OtpRequest(); - otpRequest.setAccountNumber(accountNumber); + val otpRequest = new OtpRequest(accountNumber); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/generate-otp") @@ -469,43 +454,36 @@ public void test_generate_otp_with_valid_account_number() throws Exception { @Test public void test_generate_otp_with_invalid_account_number() throws Exception { - OtpRequest otpRequest = new OtpRequest(); - otpRequest.setAccountNumber(getRandomAccountNumber()); + val otpRequest = new OtpRequest(getRandomAccountNumber()); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/generate-otp") .contentType(MediaType.APPLICATION_JSON) .content(JsonUtil.toJson(otpRequest))) - .andExpect(MockMvcResultMatchers.status().isBadRequest()) - .andExpect(MockMvcResultMatchers.content() - .string("User not found for the given account number")); + .andExpect(MockMvcResultMatchers.status().isUnauthorized()); } @Test public void test_generate_otp_with_missing_account_number() throws Exception { - OtpRequest otpRequest = new OtpRequest(); - otpRequest.setAccountNumber(""); + val otpRequest = new OtpRequest(""); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/generate-otp") .contentType(MediaType.APPLICATION_JSON) .content(JsonUtil.toJson(otpRequest))) - .andExpect(MockMvcResultMatchers.status().isBadRequest()) - .andExpect(MockMvcResultMatchers.content() - .string("User not found for the given account number")); + .andExpect(MockMvcResultMatchers.status().isUnauthorized()); } @Test public void test_verify_otp_with_valid_account_number_and_otp() throws Exception { - User user = createAndRegisterUser(); - String accountNumber = userRepository + val user = createAndRegisterUser(); + val accountNumber = userRepository .findByEmail(user.getEmail()) .get() .getAccount() .getAccountNumber(); - OtpRequest otpRequest = new OtpRequest(); - otpRequest.setAccountNumber(accountNumber); + val otpRequest = new OtpRequest(accountNumber); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/generate-otp") @@ -515,25 +493,20 @@ public void test_verify_otp_with_valid_account_number_and_otp() throws Exception .andExpect(MockMvcResultMatchers.jsonPath("message") .value("OTP sent successfully to: " + user.getEmail())); - MimeMessage[] receivedMessages = GreenMailJavaMailSender.getReceivedMessagesForDomain(user.getEmail()); - OtpVerificationRequest otpVerificationRequest = new OtpVerificationRequest(); - otpVerificationRequest.setAccountNumber(accountNumber); - otpVerificationRequest.setOtp(getOtpFromEmail(receivedMessages[0])); + val receivedMessages = GreenMailJavaMailSender.getReceivedMessagesForDomain(user.getEmail()); + val otpVerificationRequest = new OtpVerificationRequest(accountNumber, getOtpFromEmail(receivedMessages[0])); - MvcResult loginResult = mockMvc.perform(MockMvcRequestBuilders + val loginResult = mockMvc.perform(MockMvcRequestBuilders .post("/api/users/verify-otp") .contentType(MediaType.APPLICATION_JSON) .content(JsonUtil.toJson(otpVerificationRequest))) .andExpect(MockMvcResultMatchers.status().isOk()) .andReturn(); - String responseBody = loginResult.getResponse().getContentAsString(); - String token = JsonPath.read(responseBody, "$.token"); + val responseBody = loginResult.getResponse().getContentAsString(); + val token = JsonPath.read(responseBody, "$.token"); - PinRequest pinRequest = new PinRequest(); - pinRequest.setAccountNumber(accountNumber); - pinRequest.setPassword(user.getPassword()); - pinRequest.setPin(getRandomPin()); + val pinRequest = new PinRequest(accountNumber, getRandomPin(), user.getPassword()); mockMvc.perform(MockMvcRequestBuilders .post("/api/account/pin/create") @@ -547,16 +520,14 @@ public void test_verify_otp_with_valid_account_number_and_otp() throws Exception @Test public void test_verify_otp_with_invalid_otp() throws Exception { - User user = createAndRegisterUser(); - String accountNumber = userRepository + val user = createAndRegisterUser(); + val accountNumber = userRepository .findByEmail(user.getEmail()) .get() .getAccount() .getAccountNumber(); - OtpVerificationRequest otpVerificationRequest = new OtpVerificationRequest(); - otpVerificationRequest.setAccountNumber(accountNumber); - otpVerificationRequest.setOtp(getRandomOtp()); + val otpVerificationRequest = new OtpVerificationRequest(accountNumber, getRandomOtp()); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/verify-otp") @@ -569,8 +540,7 @@ public void test_verify_otp_with_invalid_otp() throws Exception { @Test public void test_verify_otp_with_missing_account_number() throws Exception { - OtpVerificationRequest otpVerificationRequest = new OtpVerificationRequest(); - otpVerificationRequest.setOtp(getRandomOtp()); + val otpVerificationRequest = new OtpVerificationRequest(null, getRandomOtp()); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/verify-otp") @@ -583,9 +553,7 @@ public void test_verify_otp_with_missing_account_number() throws Exception { @Test public void test_verify_otp_with_empty_account_number() throws Exception { - OtpVerificationRequest otpVerificationRequest = new OtpVerificationRequest(); - otpVerificationRequest.setAccountNumber(""); - otpVerificationRequest.setOtp(getRandomOtp()); + val otpVerificationRequest = new OtpVerificationRequest("", getRandomOtp()); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/verify-otp") @@ -598,15 +566,14 @@ public void test_verify_otp_with_empty_account_number() throws Exception { @Test public void test_verify_otp_with_missing_otp() throws Exception { - User user = createAndRegisterUser(); - String accountNumber = userRepository + val user = createAndRegisterUser(); + val accountNumber = userRepository .findByEmail(user.getEmail()) .get() .getAccount() .getAccountNumber(); - OtpVerificationRequest otpVerificationRequest = new OtpVerificationRequest(); - otpVerificationRequest.setAccountNumber(accountNumber); + val otpVerificationRequest = new OtpVerificationRequest(accountNumber, null); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/verify-otp") @@ -619,16 +586,14 @@ public void test_verify_otp_with_missing_otp() throws Exception { @Test public void test_verify_otp_with_empty_otp() throws Exception { - User user = createAndRegisterUser(); - String accountNumber = userRepository + val user = createAndRegisterUser(); + val accountNumber = userRepository .findByEmail(user.getEmail()) .get() .getAccount() .getAccountNumber(); - OtpVerificationRequest otpVerificationRequest = new OtpVerificationRequest(); - otpVerificationRequest.setAccountNumber(accountNumber); - otpVerificationRequest.setOtp(""); + val otpVerificationRequest = new OtpVerificationRequest(accountNumber, ""); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/verify-otp") @@ -641,9 +606,9 @@ public void test_verify_otp_with_empty_otp() throws Exception { @Test public void test_update_user_with_valid_details() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - User updatedUser = createUser(); + val updatedUser = createUser(); updatedUser.setPassword(userDetails.get("password")); updatedUser.setPhoneNumber(getRandomPhoneNumber(userDetails.get("countryCode"))); @@ -665,9 +630,9 @@ public void test_update_user_with_valid_details() throws Exception { @Test public void test_update_user_with_invalid_name() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - User updatedUser = createUser(); + val updatedUser = createUser(); updatedUser.setName(""); updatedUser.setPassword(userDetails.get("password")); updatedUser.setPhoneNumber(getRandomPhoneNumber(userDetails.get("countryCode"))); @@ -684,9 +649,9 @@ public void test_update_user_with_invalid_name() throws Exception { @Test public void test_update_user_with_invalid_address() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - User updatedUser = createUser(); + val updatedUser = createUser(); updatedUser.setAddress(""); updatedUser.setPassword(userDetails.get("password")); updatedUser.setPhoneNumber(getRandomPhoneNumber(userDetails.get("countryCode"))); @@ -703,9 +668,9 @@ public void test_update_user_with_invalid_address() throws Exception { @Test public void test_update_user_with_invalid_email() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - User updatedUser = createUser(); + val updatedUser = createUser(); updatedUser.setEmail(""); updatedUser.setPassword(userDetails.get("password")); updatedUser.setPhoneNumber(getRandomPhoneNumber(userDetails.get("countryCode"))); @@ -722,9 +687,9 @@ public void test_update_user_with_invalid_email() throws Exception { @Test public void test_update_user_with_invalid_phone_number() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - User updatedUser = createUser(); + val updatedUser = createUser(); updatedUser.setPhoneNumber(""); updatedUser.setPassword(userDetails.get("password")); @@ -740,9 +705,9 @@ public void test_update_user_with_invalid_phone_number() throws Exception { @Test public void test_update_user_with_invalid_password() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - User updatedUser = createUser(); + val updatedUser = createUser(); updatedUser.setPassword(""); mockMvc.perform(MockMvcRequestBuilders @@ -757,7 +722,7 @@ public void test_update_user_with_invalid_password() throws Exception { @Test public void test_update_user_without_authentication() throws Exception { - User updatedUser = createUser(); + val updatedUser = createUser(); mockMvc.perform(MockMvcRequestBuilders .post("/api/users/update") @@ -768,15 +733,15 @@ public void test_update_user_without_authentication() throws Exception { @Test public void test_logout_with_valid_token() throws Exception { - HashMap userDetails = createAndLoginUser(); + val userDetails = createAndLoginUser(); - MockHttpServletResponse response = mockMvc.perform(MockMvcRequestBuilders + val response = mockMvc.perform(MockMvcRequestBuilders .get("/api/users/logout") .header("Authorization", "Bearer " + userDetails.get("token"))) .andExpect(MockMvcResultMatchers.status().isFound()) .andReturn().getResponse(); - String redirectedUrl = response.getRedirectedUrl(); + val redirectedUrl = response.getRedirectedUrl(); if (redirectedUrl != null) { Assertions.assertEquals("/logout", redirectedUrl); mockMvc.perform(MockMvcRequestBuilders @@ -791,15 +756,15 @@ public void test_logout_with_valid_token() throws Exception { public void test_logout_with_invalid_token() throws Exception { createAndLoginUser(); - User user = createAndRegisterUser(); - String accountNumber = userRepository + val user = createAndRegisterUser(); + val accountNumber = userRepository .findByEmail(user.getEmail()) .get() .getAccount() .getAccountNumber(); - UserDetails userDetails = tokenService.loadUserByUsername(accountNumber); - String token = tokenService.generateToken(userDetails); + val userDetails = tokenService.loadUserByUsername(accountNumber); + val token = tokenService.generateToken(userDetails); mockMvc.perform(MockMvcRequestBuilders .get("/api/users/logout") @@ -809,15 +774,15 @@ public void test_logout_with_invalid_token() throws Exception { @Test public void test_logout_without_login() throws Exception { - User user = createAndRegisterUser(); - String accountNumber = userRepository + val user = createAndRegisterUser(); + val accountNumber = userRepository .findByEmail(user.getEmail()) .get() .getAccount() .getAccountNumber(); - UserDetails userDetails = tokenService.loadUserByUsername(accountNumber); - String token = tokenService.generateToken(userDetails); + val userDetails = tokenService.loadUserByUsername(accountNumber); + val token = tokenService.generateToken(userDetails); mockMvc.perform(MockMvcRequestBuilders .get("/api/users/logout") @@ -839,4 +804,5 @@ public void test_logout_without_authorization() throws Exception { .get("/api/users/logout")) .andExpect(MockMvcResultMatchers.status().isUnauthorized()); } + }