Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand All @@ -80,8 +90,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/example/bankapp/BankappApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class BankappApplication {

public static void main(String[] args) {
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/com/example/bankapp/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
Expand All @@ -25,6 +26,24 @@ public static PasswordEncoder passwordEncoder() {
}

@Bean
@Order(1)
public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated()
)
.httpBasic(basic -> {})
.headers(headers -> headers
.frameOptions(frameOptions -> frameOptions.sameOrigin())
);

return http.build();
}

@Bean
@Order(2)
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
Expand Down Expand Up @@ -57,4 +76,4 @@ public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
auth.userDetailsService(accountService).passwordEncoder(passwordEncoder());

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.example.bankapp.controller;

import com.example.bankapp.dto.*;
import com.example.bankapp.model.Account;
import com.example.bankapp.model.Transaction;
import com.example.bankapp.service.AccountService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/v1")
public class BankApiController {

private final AccountService accountService;

public BankApiController(AccountService accountService) {
this.accountService = accountService;
}

@PostMapping("/deposit")
public ResponseEntity<ApiResponse<AccountResponse>> deposit(@RequestBody DepositRequest request) {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account account = accountService.findAccountByUsername(username);
accountService.deposit(account, request.getAmount());
Account updated = accountService.findAccountByUsername(username);
return ResponseEntity.ok(ApiResponse.ok("Deposit successful", AccountResponse.from(updated)));
}

@PostMapping("/withdraw")
public ResponseEntity<ApiResponse<AccountResponse>> withdraw(@RequestBody WithdrawRequest request) {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account account = accountService.findAccountByUsername(username);
accountService.withdraw(account, request.getAmount());
Account updated = accountService.findAccountByUsername(username);
return ResponseEntity.ok(ApiResponse.ok("Withdrawal successful", AccountResponse.from(updated)));
}

@PostMapping("/transfer")
public ResponseEntity<ApiResponse<AccountResponse>> transfer(@RequestBody TransferRequest request) {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account fromAccount = accountService.findAccountByUsername(username);
accountService.transferAmount(fromAccount, request.getToUsername(), request.getAmount());
Account updated = accountService.findAccountByUsername(username);
return ResponseEntity.ok(ApiResponse.ok("Transfer successful", AccountResponse.from(updated)));
}

@GetMapping("/balance")
public ResponseEntity<ApiResponse<AccountResponse>> getBalance() {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account account = accountService.findAccountByUsername(username);
return ResponseEntity.ok(ApiResponse.ok("Balance retrieved", AccountResponse.from(account)));
}

@GetMapping("/transactions")
public ResponseEntity<ApiResponse<List<TransactionResponse>>> getTransactions() {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account account = accountService.findAccountByUsername(username);
List<Transaction> transactions = accountService.getTransactionHistory(account);
List<TransactionResponse> response = transactions.stream()
.map(TransactionResponse::from)
.toList();
return ResponseEntity.ok(ApiResponse.ok("Transactions retrieved", response));
}
}
17 changes: 12 additions & 5 deletions src/main/java/com/example/bankapp/controller/BankController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.bankapp.controller;

import com.example.bankapp.exception.BankAppException;
import com.example.bankapp.model.Account;
import com.example.bankapp.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -36,7 +37,7 @@ public String registerAccount(@RequestParam String username, @RequestParam Strin
try {
accountService.registerAccount(username, password);
return "redirect:/login";
} catch (RuntimeException e) {
} catch (BankAppException e) {
model.addAttribute("error", e.getMessage());
return "register";
}
Expand All @@ -48,10 +49,16 @@ public String login() {
}

@PostMapping("/deposit")
public String deposit(@RequestParam BigDecimal amount) {
public String deposit(@RequestParam BigDecimal amount, Model model) {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Account account = accountService.findAccountByUsername(username);
accountService.deposit(account, amount);
try {
accountService.deposit(account, amount);
} catch (BankAppException e) {
model.addAttribute("error", e.getMessage());
model.addAttribute("account", account);
return "dashboard";
}
return "redirect:/dashboard";
}

Expand All @@ -62,7 +69,7 @@ public String withdraw(@RequestParam BigDecimal amount, Model model) {

try {
accountService.withdraw(account, amount);
} catch (RuntimeException e) {
} catch (BankAppException e) {
model.addAttribute("error", e.getMessage());
model.addAttribute("account", account);
return "dashboard";
Expand All @@ -86,7 +93,7 @@ public String transferAmount(@RequestParam String toUsername, @RequestParam BigD

try {
accountService.transferAmount(fromAccount, toUsername, amount);
} catch (RuntimeException e) {
} catch (BankAppException e) {
model.addAttribute("error", e.getMessage());
model.addAttribute("account", fromAccount);
return "dashboard";
Expand Down
49 changes: 49 additions & 0 deletions src/main/java/com/example/bankapp/dto/AccountResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.example.bankapp.dto;

import com.example.bankapp.model.Account;

import java.math.BigDecimal;

public class AccountResponse {

private Long id;
private String username;
private BigDecimal balance;

public AccountResponse() {
}

public AccountResponse(Long id, String username, BigDecimal balance) {
this.id = id;
this.username = username;
this.balance = balance;
}

public static AccountResponse from(Account account) {
return new AccountResponse(account.getId(), account.getUsername(), account.getBalance());
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public BigDecimal getBalance() {
return balance;
}

public void setBalance(BigDecimal balance) {
this.balance = balance;
}
}
49 changes: 49 additions & 0 deletions src/main/java/com/example/bankapp/dto/ApiResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.example.bankapp.dto;

public class ApiResponse<T> {

private boolean success;
private String message;
private T data;

public ApiResponse() {
}

public ApiResponse(boolean success, String message, T data) {
this.success = success;
this.message = message;
this.data = data;
}

public static <T> ApiResponse<T> ok(String message, T data) {
return new ApiResponse<>(true, message, data);
}

public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(false, message, null);
}

public boolean isSuccess() {
return success;
}

public void setSuccess(boolean success) {
this.success = success;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}
}
23 changes: 23 additions & 0 deletions src/main/java/com/example/bankapp/dto/DepositRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.bankapp.dto;

import java.math.BigDecimal;

public class DepositRequest {

private BigDecimal amount;

public DepositRequest() {
}

public DepositRequest(BigDecimal amount) {
this.amount = amount;
}

public BigDecimal getAmount() {
return amount;
}

public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
65 changes: 65 additions & 0 deletions src/main/java/com/example/bankapp/dto/TransactionResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.example.bankapp.dto;

import com.example.bankapp.model.Transaction;

import java.math.BigDecimal;
import java.time.LocalDateTime;

public class TransactionResponse {

private Long id;
private BigDecimal amount;
private String type;
private LocalDateTime timestamp;

public TransactionResponse() {
}

public TransactionResponse(Long id, BigDecimal amount, String type, LocalDateTime timestamp) {
this.id = id;
this.amount = amount;
this.type = type;
this.timestamp = timestamp;
}

public static TransactionResponse from(Transaction transaction) {
return new TransactionResponse(
transaction.getId(),
transaction.getAmount(),
transaction.getType(),
transaction.getTimestamp()
);
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public BigDecimal getAmount() {
return amount;
}

public void setAmount(BigDecimal amount) {
this.amount = amount;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public LocalDateTime getTimestamp() {
return timestamp;
}

public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
}
Loading