Skip to content

Commit 7d0bf17

Browse files
Merge pull request pauldragoslav#1 from Nayananga/main
Add support for new APIs
2 parents bc89362 + 450b270 commit 7d0bf17

23 files changed

+490
-34
lines changed

Diff for: Banking.postman_collection.json

+97-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"info": {
3-
"_postman_id": "527d1a6d-ae07-4484-947b-e7f5501c1c6b",
3+
"_postman_id": "485cda16-0d43-4aa6-ac55-116fb41d8f17",
44
"name": "Banking",
55
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
66
},
77
"item": [
88
{
9-
"name": "Account Balance",
9+
"name": "Create Account",
1010
"request": {
11-
"method": "POST",
11+
"method": "PUT",
1212
"header": [
1313
{
1414
"key": "Content-Type",
@@ -19,7 +19,7 @@
1919
],
2020
"body": {
2121
"mode": "raw",
22-
"raw": "{\n\t\"sortCode\": \"53-68-92\",\n\t\"accountNumber\": \"73084635\"\n}\n\n"
22+
"raw": "{\n\t\"bankName\": \"Bank of Ceyloan\",\n\t\"ownerName\": \"Nayananga Muhandiram\"\n}\n"
2323
},
2424
"url": {
2525
"raw": "localhost:8080/api/v1/accounts",
@@ -66,6 +66,99 @@
6666
}
6767
},
6868
"response": []
69+
},
70+
{
71+
"name": "Check Balance",
72+
"request": {
73+
"method": "POST",
74+
"header": [
75+
{
76+
"key": "Content-Type",
77+
"name": "Content-Type",
78+
"value": "application/json",
79+
"type": "text"
80+
}
81+
],
82+
"body": {
83+
"mode": "raw",
84+
"raw": "{\n\t\"sortCode\": \"35-16-67\",\n\t\"accountNumber\": \"95753174\"\n}\n"
85+
},
86+
"url": {
87+
"raw": "localhost:8080/api/v1/accounts",
88+
"host": [
89+
"localhost"
90+
],
91+
"port": "8080",
92+
"path": [
93+
"api",
94+
"v1",
95+
"accounts"
96+
]
97+
}
98+
},
99+
"response": []
100+
},
101+
{
102+
"name": "Withdraw",
103+
"request": {
104+
"method": "POST",
105+
"header": [
106+
{
107+
"key": "Content-Type",
108+
"name": "Content-Type",
109+
"type": "text",
110+
"value": "application/json"
111+
}
112+
],
113+
"body": {
114+
"mode": "raw",
115+
"raw": "{\n\t\"sortCode\": \"35-16-67\",\n\t\"accountNumber\": \"95753174\",\n \"amount\": 100.00\n}\n"
116+
},
117+
"url": {
118+
"raw": "localhost:8080/api/v1/withdraw",
119+
"host": [
120+
"localhost"
121+
],
122+
"port": "8080",
123+
"path": [
124+
"api",
125+
"v1",
126+
"withdraw"
127+
]
128+
}
129+
},
130+
"response": []
131+
},
132+
{
133+
"name": "Deposit",
134+
"request": {
135+
"method": "POST",
136+
"header": [
137+
{
138+
"key": "Content-Type",
139+
"name": "Content-Type",
140+
"type": "text",
141+
"value": "application/json"
142+
}
143+
],
144+
"body": {
145+
"mode": "raw",
146+
"raw": "{\n\t\"targetAccountNo\": \"95753174\",\n \"amount\": 1000.00\n}\n"
147+
},
148+
"url": {
149+
"raw": "localhost:8080/api/v1/deposit",
150+
"host": [
151+
"localhost"
152+
],
153+
"port": "8080",
154+
"path": [
155+
"api",
156+
"v1",
157+
"deposit"
158+
]
159+
}
160+
},
161+
"response": []
69162
}
70163
]
71164
}

Diff for: README.md

+37-2
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,53 @@ Example project demonstrating the use of Java and Spring-boot to build a microse
33

44
## Running locally
55
```
6-
./mvnw clean package
6+
./mvnw clean install -DskipTests=true
7+
```
8+
9+
```
710
java -jar target/Banking-0.0.1.jar
811
```
912

1013
## Running on Docker
1114
```
1215
docker build -t "spring-boot:banking" .
16+
```
17+
18+
```
1319
docker run -p 8080:8080 spring-boot:banking
1420
```
1521

1622
## Testing
17-
1. Import the Postman collection file into the application or copy the request body from there
23+
Import the Postman collection file into the application or copy the request body from there
24+
25+
### How to test
26+
1. Create account
27+
> Use create account API to create an account by providing a `bankName` and `ownerName`
28+
>
29+
![Create Account](screenshots/create_account.png)
30+
31+
> Make sure to write down the `sortCode` and the `accountNumber` to proceed with other APIs
32+
33+
2. Deposit Cash
34+
>Use noted `accountNumber` as `targetAccountNo` and provide amount greater than zero to deposit cash into an account
35+
36+
![Deposit cash](screenshots/deposit.png)
37+
38+
3. Check Balance
39+
>Use noted `accountNumber` and `sortCode` to check account balance
40+
41+
![Check Balance](screenshots/check_balance.png)
42+
43+
4. Withdraw Cash
44+
>Use noted `accountNumber` and `sortCode` and `amount` grater than zero to withdraw cash from an account
45+
46+
![Withdraw cash](screenshots/withdraw.png)
47+
48+
5. Check Balance again to verify withdrawal
49+
50+
![Check Balance](screenshots/check_balance_2.png)
51+
52+
1853

1954
### Extensions
2055
1. Use of persisted database

Diff for: mvnw

100644100755
File mode changed.

Diff for: pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
<artifactId>spring-boot-starter-web</artifactId>
3333
</dependency>
3434

35+
<dependency>
36+
<groupId>com.github.mifmif</groupId>
37+
<artifactId>generex</artifactId>
38+
<version>1.0.2</version>
39+
</dependency>
3540
<dependency>
3641
<groupId>com.h2database</groupId>
3742
<artifactId>h2</artifactId>

Diff for: screenshots/check_balance.png

118 KB
Loading

Diff for: screenshots/check_balance_2.png

118 KB
Loading

Diff for: screenshots/create_account.png

117 KB
Loading

Diff for: screenshots/deposit.png

102 KB
Loading

Diff for: screenshots/withdraw.png

104 KB
Loading

Diff for: src/main/java/com/example/paul/constants/ACTION.java

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.example.paul.constants;
2+
3+
public enum ACTION {
4+
DEPOSIT,
5+
WITHDRAW
6+
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.example.paul.constants;
2+
3+
import java.util.regex.Pattern;
4+
5+
public class constants {
6+
7+
public static final String SUCCESS =
8+
"Operation completed successfully";
9+
public static final String NO_ACCOUNT_FOUND =
10+
"Unable to find an account matching this sort code and account number";
11+
public static final String INVALID_SEARCH_CRITERIA =
12+
"The provided sort code or account number did not match the expected format";
13+
14+
public static final String INSUFFICIENT_ACCOUNT_BALANCE =
15+
"Your account does not have sufficient balance";
16+
17+
public static final String SORT_CODE_PATTERN_STRING = "[0-9]{2}-[0-9]{2}-[0-9]{2}";
18+
19+
public static final String ACCOUNT_NUMBER_PATTERN_STRING = "[0-9]{8}";
20+
public static final Pattern SORT_CODE_PATTERN = Pattern.compile("^[0-9]{2}-[0-9]{2}-[0-9]{2}$");
21+
public static final Pattern ACCOUNT_NUMBER_PATTERN = Pattern.compile("^[0-9]{8}$");
22+
23+
public static final String INVALID_TRANSACTION =
24+
"Account information is invalid or transaction has been denied for your protection. Please try again.";
25+
public static final String CREATE_ACCOUNT_FAILED =
26+
"Error happened during creating new account";
27+
}

Diff for: src/main/java/com/example/paul/controllers/AccountRestController.java

+29-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.example.paul.controllers;
22

3+
import com.example.paul.constants.constants;
34
import com.example.paul.models.Account;
45
import com.example.paul.services.AccountService;
56
import com.example.paul.utils.AccountInput;
7+
import com.example.paul.utils.CreateAccountInput;
68
import com.example.paul.utils.InputValidator;
79
import org.slf4j.Logger;
810
import org.slf4j.LoggerFactory;
@@ -24,12 +26,6 @@ public class AccountRestController {
2426

2527
private static final Logger LOGGER = LoggerFactory.getLogger(AccountRestController.class);
2628

27-
private static final String INVALID_SEARCH_CRITERIA =
28-
"The provided sort code or account number did not match the expected format";
29-
30-
private static final String NO_ACCOUNT_FOUND =
31-
"Unable to find an account matching this sort code and account number";
32-
3329
private final AccountService accountService;
3430

3531
@Autowired
@@ -53,12 +49,37 @@ public ResponseEntity<?> checkAccountBalance(
5349

5450
// Return the account details, or warn that no account was found for given input
5551
if (account == null) {
56-
return new ResponseEntity<>(NO_ACCOUNT_FOUND, HttpStatus.NO_CONTENT);
52+
return new ResponseEntity<>(constants.NO_ACCOUNT_FOUND, HttpStatus.OK);
53+
} else {
54+
return new ResponseEntity<>(account, HttpStatus.OK);
55+
}
56+
} else {
57+
return new ResponseEntity<>(constants.INVALID_SEARCH_CRITERIA, HttpStatus.BAD_REQUEST);
58+
}
59+
}
60+
61+
62+
@PutMapping(value = "/accounts",
63+
consumes = MediaType.APPLICATION_JSON_VALUE,
64+
produces = MediaType.APPLICATION_JSON_VALUE)
65+
public ResponseEntity<?> createAccount(
66+
@Valid @RequestBody CreateAccountInput createAccountInput) {
67+
LOGGER.debug("Triggered AccountRestController.createAccountInput");
68+
69+
// Validate input
70+
if (InputValidator.isCreateAccountCriteriaValid(createAccountInput)) {
71+
// Attempt to retrieve the account information
72+
Account account = accountService.createAccount(
73+
createAccountInput.getBankName(), createAccountInput.getOwnerName());
74+
75+
// Return the account details, or warn that no account was found for given input
76+
if (account == null) {
77+
return new ResponseEntity<>(constants.CREATE_ACCOUNT_FAILED, HttpStatus.OK);
5778
} else {
5879
return new ResponseEntity<>(account, HttpStatus.OK);
5980
}
6081
} else {
61-
return new ResponseEntity<>(INVALID_SEARCH_CRITERIA, HttpStatus.BAD_REQUEST);
82+
return new ResponseEntity<>(constants.INVALID_SEARCH_CRITERIA, HttpStatus.BAD_REQUEST);
6283
}
6384
}
6485

Diff for: src/main/java/com/example/paul/controllers/TransactionRestController.java

+62-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.example.paul.controllers;
22

3+
import com.example.paul.constants.ACTION;
4+
import com.example.paul.models.Account;
5+
import com.example.paul.services.AccountService;
36
import com.example.paul.services.TransactionService;
4-
import com.example.paul.utils.InputValidator;
5-
import com.example.paul.utils.TransactionInput;
7+
import com.example.paul.utils.*;
68
import org.slf4j.Logger;
79
import org.slf4j.LoggerFactory;
810
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,19 +19,20 @@
1719
import java.util.HashMap;
1820
import java.util.Map;
1921

22+
import static com.example.paul.constants.constants.*;
23+
2024
@RestController
2125
@RequestMapping("api/v1")
2226
public class TransactionRestController {
2327

2428
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionRestController.class);
2529

26-
private static final String INVALID_TRANSACTION =
27-
"Account information is invalid or transaction has been denied for your protection. Please try again.";
28-
30+
private final AccountService accountService;
2931
private final TransactionService transactionService;
3032

3133
@Autowired
32-
public TransactionRestController(TransactionService transactionService) {
34+
public TransactionRestController(AccountService accountService, TransactionService transactionService) {
35+
this.accountService = accountService;
3336
this.transactionService = transactionService;
3437
}
3538

@@ -47,6 +50,59 @@ public ResponseEntity<?> makeTransfer(
4750
}
4851
}
4952

53+
@PostMapping(value = "/withdraw",
54+
consumes = MediaType.APPLICATION_JSON_VALUE,
55+
produces = MediaType.APPLICATION_JSON_VALUE)
56+
public ResponseEntity<?> withdraw(
57+
@Valid @RequestBody WithdrawInput withdrawInput) {
58+
LOGGER.debug("Triggered AccountRestController.withdrawInput");
59+
60+
// Validate input
61+
if (InputValidator.isSearchCriteriaValid(withdrawInput)) {
62+
// Attempt to retrieve the account information
63+
Account account = accountService.getAccount(
64+
withdrawInput.getSortCode(), withdrawInput.getAccountNumber());
65+
66+
// Return the account details, or warn that no account was found for given input
67+
if (account == null) {
68+
return new ResponseEntity<>(NO_ACCOUNT_FOUND, HttpStatus.OK);
69+
} else {
70+
if (transactionService.isAmountAvailable(withdrawInput.getAmount(), account.getCurrentBalance())) {
71+
transactionService.updateAccountBalance(account, withdrawInput.getAmount(), ACTION.WITHDRAW);
72+
return new ResponseEntity<>(SUCCESS, HttpStatus.OK);
73+
}
74+
return new ResponseEntity<>(INSUFFICIENT_ACCOUNT_BALANCE, HttpStatus.OK);
75+
}
76+
} else {
77+
return new ResponseEntity<>(INVALID_SEARCH_CRITERIA, HttpStatus.BAD_REQUEST);
78+
}
79+
}
80+
81+
82+
@PostMapping(value = "/deposit",
83+
consumes = MediaType.APPLICATION_JSON_VALUE,
84+
produces = MediaType.APPLICATION_JSON_VALUE)
85+
public ResponseEntity<?> deposit(
86+
@Valid @RequestBody DepositInput depositInput) {
87+
LOGGER.debug("Triggered AccountRestController.depositInput");
88+
89+
// Validate input
90+
if (InputValidator.isAccountNoValid(depositInput.getTargetAccountNo())) {
91+
// Attempt to retrieve the account information
92+
Account account = accountService.getAccount(depositInput.getTargetAccountNo());
93+
94+
// Return the account details, or warn that no account was found for given input
95+
if (account == null) {
96+
return new ResponseEntity<>(NO_ACCOUNT_FOUND, HttpStatus.OK);
97+
} else {
98+
transactionService.updateAccountBalance(account, depositInput.getAmount(), ACTION.DEPOSIT);
99+
return new ResponseEntity<>(SUCCESS, HttpStatus.OK);
100+
}
101+
} else {
102+
return new ResponseEntity<>(INVALID_SEARCH_CRITERIA, HttpStatus.BAD_REQUEST);
103+
}
104+
}
105+
50106
@ResponseStatus(HttpStatus.BAD_REQUEST)
51107
@ExceptionHandler(MethodArgumentNotValidException.class)
52108
public Map<String, String> handleValidationExceptions(

0 commit comments

Comments
 (0)