diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java index d6d1381..2ef0760 100644 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -23,20 +23,20 @@ public class MavenWrapperDownloader { * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. */ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; /** * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to * use instead of the default one. */ private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; + ".mvn/wrapper/maven-wrapper.properties"; /** * Path where the maven-wrapper.jar will be saved to. */ private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; + ".mvn/wrapper/maven-wrapper.jar"; /** * Name of the property which should be used to override the default download url for the wrapper. @@ -77,7 +77,7 @@ public static void main(String args[]) { if (!outputFile.getParentFile().exists()) { if (!outputFile.getParentFile().mkdirs()) { System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); } } System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); diff --git a/README.md b/README.md index cf59569..0ddfbd9 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,34 @@ A set of interacting microservices, representing a very basic banking system. -- This project is a development of a small set of **Spring Boot** projects, with a database in-memory. +- This project is a development of a small set of **Spring Boot** projects, with a database + in-memory. + --- + ## Better Code Hub -I analysed this repository according to the clean code standards on [Better Code Hub](https://bettercodehub.com/) just to get an independent opinion of how bad the code is. Surprisingly, the compliance score is high! + +I analysed this repository according to the clean code standards +on [Better Code Hub](https://bettercodehub.com/) just to get an independent opinion of how bad the +code is. Surprisingly, the compliance score is high! ## Getting started ### Project Management -1. I have used GitHub projects to manage my tasks in the **Harvest Bank** project. [Project Link](https://github.com/mohamed-taman/Harvest-Bank/projects/1) -2. All MVP tasks are assigned to the **Harvest Bank MVP Milestone**. [Milestone Link](https://github.com/mohamed-taman/Harvest-Bank/milestone/1?closed=1) -3. I used Pull requests to manages and close my tasks. [Tasks Link](https://github.com/mohamed-taman/Harvest-Bank/issues?q=) -4. Finally, I have added releases to manage small features sprints until the final release v1.5. [Releases Link](https://github.com/mohamed-taman/Harvest-Bank/releases) + +1. I have used GitHub projects to manage my tasks in the **Harvest Bank** + project. [Project Link](https://github.com/mohamed-taman/Harvest-Bank/projects/1) +2. All MVP tasks are assigned to the **Harvest Bank MVP Milestone** + . [Milestone Link](https://github.com/mohamed-taman/Harvest-Bank/milestone/1?closed=1) +3. I used Pull requests to manages and close my + tasks. [Tasks Link](https://github.com/mohamed-taman/Harvest-Bank/issues?q=) +4. Finally, I have added releases to manage small features sprints until the final release + v1.5. [Releases Link](https://github.com/mohamed-taman/Harvest-Bank/releases) ### System components Structure + Let's explain first the system structure to understand its components: + ``` Harvest-Bank --> Parent folder. |- docs --> All docs and diagrams. @@ -33,6 +46,7 @@ Harvest-Bank --> Parent folder. |- stop-em-all.sh --> Stop all services runs in standalone mode. |- test-em-all.sh --> This will start all Microservices landscape and test them, then shutdown Microservices after test finishes (use switch start and stop) ``` + Now, as we have learned about different system components, then let's start. ### System Boundary - μServices Landscape @@ -42,27 +56,36 @@ Now, as we have learned about different system components, then let's start. ### Required software The following are the initially required software pieces: -1. **Maven**: Apache Maven is a software project management and comprehension tool, it can be downloaded from here https://maven.apache.org/download.cgi + +1. **Maven**: Apache Maven is a software project management and comprehension tool, it can be + downloaded from here https://maven.apache.org/download.cgi 1. **Git**: it can be downloaded and installed from https://git-scm.com/downloads 1. **Java, JDK 15 RC**: it can be downloaded and installed from https://jdk.java.net/15/ -1. **curl**: this command-line tool for testing HTTP-based APIs can be downloaded and installed from https://curl.haxx.se/download.html -1. **jq**: This command-line JSON processor can be downloaded and installed from https://stedolan.github.io/jq/download/ +1. **curl**: this command-line tool for testing HTTP-based APIs can be downloaded and installed + from https://curl.haxx.se/download.html +1. **jq**: This command-line JSON processor can be downloaded and installed + from https://stedolan.github.io/jq/download/ -Follow the installation guide for each software website link and check your software versions from the command line to verify that they are all installed correctly. +Follow the installation guide for each software website link and check your software versions from +the command line to verify that they are all installed correctly. ## Using an IDE -I recommend that you work with your Java code using an IDE that supports the development of Spring Boot applications such as **Spring Tool Suite** or **IntelliJ IDEA Ultimate Edition**. +I recommend that you work with your Java code using an IDE that supports the development of Spring +Boot applications such as **Spring Tool Suite** or **IntelliJ IDEA Ultimate Edition**. -So you can use the Spring Boot Dashboard to run the services, run each Microservice test case, and many more. +So you can use the Spring Boot Dashboard to run the services, run each Microservice test case, and +many more. -All you have to do is just fire up your favorit IDE **->** open or import the parent folder `Harvest-Bank`, and everything will be ready for you. +All you have to do is just fire up your favorit IDE **->** open or import the parent +folder `Harvest-Bank`, and everything will be ready for you. ## Playing With Harvest Bank Project ### Cloning It -The first thing to do is to open **git bash** command line, and then simply you can clone the project under any of your favorite places as the following: +The first thing to do is to open **git bash** command line, and then simply you can clone the +project under any of your favorite places as the following: ```bash > git clone https://github.com/mohamed-taman/Harvest-Bank.git @@ -70,13 +93,15 @@ The first thing to do is to open **git bash** command line, and then simply you ### Build & Test Them In Isolation -To build and run the test cases for each service & shared modules in the project, we need to do the following: +To build and run the test cases for each service & shared modules in the project, we need to do the +following: #### First: Build & Install Shared Dependencies > This done only for the first time or any new changes or versions of shared modules and POMs. -To build and install `bank-build-chassis`, and `bank-services-chassis` POMs, and `bank-common` shared library, from the root folder `Harvest-Bank`, run the following command: +To build and install `bank-build-chassis`, and `bank-services-chassis` POMs, and `bank-common` +shared library, from the root folder `Harvest-Bank`, run the following command: ```bash mohamed_taman:Harvest-Bank$ ./setup.sh @@ -100,14 +125,18 @@ Done successfully. Woohoo, building & installing all project modules are finished successfully. The project is ready for the next step. :) ``` + #### Second: Build & Test Microservices -Now it is the time to build our **3 microservices** and run each service unit and integration tests in isolation by running the following commands: + +Now it is the time to build our **3 microservices** and run each service unit and integration tests +in isolation by running the following commands: ```bash mohamed_taman:Harvest-Bank$ ./mvnw clean verify ``` -All build commands and test suite for each microservice should run successfully, and the final output should be like this: +All build commands and test suite for each microservice should run successfully, and the final +output should be like this: ```bash -------< org.siriusxi.blueharvest.bank:Harvest-Bank-aggregator >-------- @@ -134,13 +163,16 @@ All build commands and test suite for each microservice should run successfully, ``` ### Running Them All -Now it's the time to run all of our Microservices, and it's straightforward, just run the following commands: + +Now it's the time to run all of our Microservices, and it's straightforward, just run the following +commands: ```bash mohamed_taman:Harvest-Bank$ ./run-em-all.sh ``` -All the **services**, In-memory **databases**, will run in parallel in a detach mode, and the command output will print the following to console: +All the **services**, In-memory **databases**, will run in parallel in a detach mode, and the +command output will print the following to console: ```bash Starting [Harvest Bank] μServices .... @@ -150,21 +182,31 @@ Starting [account-service] μService.... Done Starting [customer-service] μService.... Done Starting [transaction-service] μService.... Done ``` + ### Access Bank APIs You can manually test the whole system through `Customer Service` APIs within its **OpenAPI** interface at the following URL [http://localhost:8090/swagger-ui.html](http://localhost:8090/swagger-ui.html) #### System Behaviours -1. If you execute get customers through `/bank/api/v1/customers`, it will return pre-defined seven customers. -2. If you tried to pass through `/bank/api/v1/customers/{id}/accounts` the following: + +1. If you execute get customers through `/bank/api/v1/customers`, it will return pre-defined seven + customers. +2. If you tried to pass through `/bank/api/v1/customers/{id}/accounts` the following: 1. A none exist customer {Id} system will return **404** (*Not Found*) with error message. - 2. A minus customer {Id} or Initial Credit, System will return **422** (*Unprocessable Entity*) with an error message. - 3. An invalid format customer {Id} or Initial Credit, System will return **400** (*Bad Request*) with an error message. + 2. A minus customer {Id} or Initial Credit, System will return **422** (*Unprocessable Entity*) + with an error message. + 3. An invalid format customer {Id} or Initial Credit, System will return **400** (*Bad Request*) + with an error message. 3. If the initial credit is 0.0, the system expects to create an account and no transaction. -4. If the initial credit is > 0.0, i.e., 100, The system is expected to create a new account with a balance of 100. A new transaction with the amount of 100 and customer balance will be 100. -5. Suppose another create account call happened to the same customer. In that case, the system will do what is done at point **#4**. The customer balance will be *updated* to reflect the sum of all accounts balances and transaction amounts. +4. If the initial credit is > 0.0, i.e., 100, The system is expected to create a new account with a + balance of 100. A new transaction with the amount of 100 and customer balance will be 100. +5. Suppose another create account call happened to the same customer. In that case, the system will + do what is done at point **#4**. The customer balance will be *updated* to reflect the sum of all + accounts balances and transaction amounts. ### Stopping Them All -Now it's the time to stop all of our Microservices, and it's straightforward, just run the following commands: + +Now it's the time to stop all of our Microservices, and it's straightforward, just run the following +commands: ```bash mohamed_taman:Harvest-Bank$ ./stop-em-all.sh @@ -188,16 +230,20 @@ Stopping μService at port 8092 .... {"message":"Shutting down, bye..."} μService at port 8092 stopped successfully .... ``` + ### Testing Them All -Now it's time to test all the application functionality as one part. To do so just run the following automation test script: + +Now it's time to test all the application functionality as one part. To do so just run the following +automation test script: ```bash mohamed_taman:Harvest-Bank$ ./test-em-all.sh start stop ``` -You can use `stop` switch with `start`, that will -1. **Start** the whole microservices landscape, -2. **Run** the system black-box tests, if successful then +You can use `stop` switch with `start`, that will + +1. **Start** the whole microservices landscape, +2. **Run** the system black-box tests, if successful then 3. **Stop** the whole microservices. The result will look like this: @@ -272,11 +318,14 @@ Stopping μService at port 8092 .... ### Closing The Story -Finally, hope you enjoyed the application and find it useful. If you would like to enhance please open **PR**, and finally give it a 🌟. +Finally, hope you enjoyed the application and find it useful. If you would like to enhance please +open **PR**, and finally give it a 🌟. ## The End -Happy Coding 😊 + +Happy Coding 😊 ## License + Copyright (C) 2020 Mohamed Taman, Licensed under the **Apache-2.0 License**. diff --git a/bank-base/bank-build-chassis/pom.xml b/bank-base/bank-build-chassis/pom.xml index 13a1fbb..328e64d 100644 --- a/bank-base/bank-build-chassis/pom.xml +++ b/bank-base/bank-build-chassis/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.0-M2 + 2.5.3 @@ -18,15 +18,15 @@ pom - 15 + 17 UTF-8 UTF-8 3.8.1 - 3.0.0-M4 - 3.0.0-M4 - 1.18.12 + 3.0.0-M5 + 3.0.0-M5 + 1.18.20 @@ -35,6 +35,7 @@ org.projectlombok lombok true + ${org.lombok.version} diff --git a/bank-base/bank-services-chassis/pom.xml b/bank-base/bank-services-chassis/pom.xml index 3a9982b..aa9d33c 100644 --- a/bank-base/bank-services-chassis/pom.xml +++ b/bank-base/bank-services-chassis/pom.xml @@ -85,12 +85,12 @@ org.springframework.boot spring-boot-starter-test test - - - org.junit.vintage - junit-vintage-engine - - + + + + + + org.junit.jupiter diff --git a/bank-common/pom.xml b/bank-common/pom.xml index d7afe37..a683ddc 100644 --- a/bank-common/pom.xml +++ b/bank-common/pom.xml @@ -20,7 +20,7 @@ com.fasterxml.jackson.core jackson-databind - 2.11.2 + 2.12.4 \ No newline at end of file diff --git a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/account/Account.java b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/account/Account.java index 927aa1b..40e6da3 100644 --- a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/account/Account.java +++ b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/account/Account.java @@ -7,7 +7,7 @@ import java.util.List; public record Account(@JsonProperty("customerId") int customerId, - @JsonProperty("balance") BigDecimal balance, - @JsonProperty("type") AccountType type, - @JsonProperty("transactions") List transactions) { -} +@JsonProperty("balance") BigDecimal balance, +@JsonProperty("type") AccountType type, +@JsonProperty("transactions") List transactions){ + } diff --git a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/customer/CustomerAggregate.java b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/customer/CustomerAggregate.java index 44066f1..16136a3 100644 --- a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/customer/CustomerAggregate.java +++ b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/customer/CustomerAggregate.java @@ -12,16 +12,15 @@ * @implNote Since it is a record and not normal POJO, so it needs some customizations * to be serialized to JSON and this is done with method * GlobalConfiguration.jacksonCustomizer(). - * - * @see java.lang.Record * @author mohamed.taman * @version v0.1 + * @see java.lang.Record * @since v0.1 */ public record CustomerAggregate( - @JsonProperty("id") int id, - @JsonProperty("name") String firstName, - @JsonProperty("Surname")String lastName, - @JsonProperty("balance") BigDecimal balance, - @JsonProperty("accounts") List accounts) { -} +@JsonProperty("id") int id, +@JsonProperty("name") String firstName, +@JsonProperty("Surname")String lastName, +@JsonProperty("balance") BigDecimal balance, +@JsonProperty("accounts") List accounts){ + } diff --git a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/trx/Transaction.java b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/trx/Transaction.java index 597c15c..e4f61ed 100644 --- a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/trx/Transaction.java +++ b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/composite/trx/Transaction.java @@ -5,6 +5,6 @@ import java.math.BigDecimal; public record Transaction(@JsonProperty("accountId") int accountId, - @JsonProperty("type") TransactionType type, - @JsonProperty("amount") BigDecimal amount) { -} +@JsonProperty("type") TransactionType type, +@JsonProperty("amount") BigDecimal amount){ + } diff --git a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/dto/TransactionDTO.java b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/dto/TransactionDTO.java index 8e25e86..2e3c55d 100644 --- a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/dto/TransactionDTO.java +++ b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/api/dto/TransactionDTO.java @@ -10,9 +10,9 @@ @RequiredArgsConstructor public class TransactionDTO { - @NonNull - private int accountId; + @NonNull + private int accountId; - @NonNull - private BigDecimal amount; + @NonNull + private BigDecimal amount; } diff --git a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/exception/InvalidInputException.java b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/exception/InvalidInputException.java index 2e129d9..8c635bf 100644 --- a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/exception/InvalidInputException.java +++ b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/exception/InvalidInputException.java @@ -2,17 +2,18 @@ public class InvalidInputException extends RuntimeException { - public InvalidInputException() {} + public InvalidInputException() { + } - public InvalidInputException(String message) { - super(message); - } + public InvalidInputException(String message) { + super(message); + } - public InvalidInputException(String message, Throwable cause) { - super(message, cause); - } + public InvalidInputException(String message, Throwable cause) { + super(message, cause); + } - public InvalidInputException(Throwable cause) { - super(cause); - } + public InvalidInputException(Throwable cause) { + super(cause); + } } diff --git a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/exception/NotFoundException.java b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/exception/NotFoundException.java index 2b586f2..265cd46 100644 --- a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/exception/NotFoundException.java +++ b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/common/exception/NotFoundException.java @@ -2,17 +2,18 @@ public class NotFoundException extends RuntimeException { - public NotFoundException() {} + public NotFoundException() { + } - public NotFoundException(String message) { - super(message); - } + public NotFoundException(String message) { + super(message); + } - public NotFoundException(String message, Throwable cause) { - super(message, cause); - } + public NotFoundException(String message, Throwable cause) { + super(message, cause); + } - public NotFoundException(Throwable cause) { - super(cause); - } + public NotFoundException(Throwable cause) { + super(cause); + } } diff --git a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/util/JsonUtilities.java b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/util/JsonUtilities.java index f9f1eb6..99d8655 100644 --- a/bank-common/src/main/java/org/siriusxi/blueharvest/bank/util/JsonUtilities.java +++ b/bank-common/src/main/java/org/siriusxi/blueharvest/bank/util/JsonUtilities.java @@ -7,7 +7,8 @@ public final class JsonUtilities { - private JsonUtilities(){} + private JsonUtilities() { + } private static final ObjectMapper mapper = new ObjectMapper(); diff --git a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/AccountServiceApplication.java b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/AccountServiceApplication.java index 55a7c16..b1fbcbd 100644 --- a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/AccountServiceApplication.java +++ b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/AccountServiceApplication.java @@ -6,8 +6,8 @@ @SpringBootApplication public class AccountServiceApplication { - public static void main(String[] args) { - SpringApplication.run(AccountServiceApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(AccountServiceApplication.class, args); + } } diff --git a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/api/AccountController.java b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/api/AccountController.java index ab17b7a..772ec8d 100644 --- a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/api/AccountController.java +++ b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/api/AccountController.java @@ -17,26 +17,26 @@ @Log4j2 public class AccountController { - private final AccountService accountService; - - @Autowired - public AccountController(AccountService accountService) { - this.accountService = accountService; - } - - @GetMapping( - value = "accounts", - produces = APPLICATION_JSON_VALUE) - public List getAccounts(@RequestParam("customerId") int customerId) { - return accountService.getAccounts(customerId); - } - - @PostMapping( - value = "/accounts", - consumes = APPLICATION_JSON_VALUE) - public void createAccount(@RequestBody AccountDTO account) { - accountService.createAccount( - new AccountEntity(account.getCustomerId(), account.getInitialCredit())); - log.debug("createAccount: creates a new account {}", account.toString()); - } + private final AccountService accountService; + + @Autowired + public AccountController(AccountService accountService) { + this.accountService = accountService; + } + + @GetMapping( + value = "accounts", + produces = APPLICATION_JSON_VALUE) + public List getAccounts(@RequestParam("customerId") int customerId) { + return accountService.getAccounts(customerId); + } + + @PostMapping( + value = "/accounts", + consumes = APPLICATION_JSON_VALUE) + public void createAccount(@RequestBody AccountDTO account) { + accountService.createAccount( + new AccountEntity(account.getCustomerId(), account.getInitialCredit())); + log.debug("createAccount: creates a new account {}", account.toString()); + } } diff --git a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/config/ServiceConfiguration.java b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/config/ServiceConfiguration.java index 1a6ce1c..ea26193 100644 --- a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/config/ServiceConfiguration.java +++ b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/config/ServiceConfiguration.java @@ -7,8 +7,8 @@ @Configuration public class ServiceConfiguration { - @Bean - RestTemplate restTemplate() { - return new RestTemplate(); - } + @Bean + RestTemplate restTemplate() { + return new RestTemplate(); + } } diff --git a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/integration/TransactionIntegration.java b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/integration/TransactionIntegration.java index 2fc5ce6..d96c6ce 100644 --- a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/integration/TransactionIntegration.java +++ b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/integration/TransactionIntegration.java @@ -27,31 +27,32 @@ public class TransactionIntegration { @Autowired public TransactionIntegration( - RestTemplate restTemplate, - @Value("${app.transaction-service.host}") String transactionServiceHost, - @Value("${app.transaction-service.port}") int transactionServicePort) { + RestTemplate restTemplate, + @Value("${app.transaction-service.host}") String transactionServiceHost, + @Value("${app.transaction-service.port}") int transactionServicePort) { this.restTemplate = restTemplate; transactionServiceUrl = "http://" - .concat(transactionServiceHost) - .concat(":") - .concat(String.valueOf(transactionServicePort)) - .concat(BASE_URL) - .concat("transactions"); + .concat(transactionServiceHost) + .concat(":") + .concat(String.valueOf(transactionServicePort)) + .concat(BASE_URL) + .concat("transactions"); } - public List getAccountTransactions(int accountId){ + public List getAccountTransactions(int accountId) { String url = transactionServiceUrl.concat(QUERY_PARAM).concat(String.valueOf(accountId)); log.debug("Will call getCustomerAccounts API on URL: {}", url); return restTemplate.exchange(url, GET, null, - new ParameterizedTypeReference>() {}).getBody(); + new ParameterizedTypeReference>() { + }).getBody(); } public void createTransaction(TransactionDTO transaction) { - restTemplate.postForObject(transactionServiceUrl,transaction, TransactionDTO.class); + restTemplate.postForObject(transactionServiceUrl, transaction, TransactionDTO.class); } diff --git a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/persistence/entity/AccountEntity.java b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/persistence/entity/AccountEntity.java index 5958fd4..0fad9e9 100644 --- a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/persistence/entity/AccountEntity.java +++ b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/persistence/entity/AccountEntity.java @@ -20,14 +20,14 @@ @NoArgsConstructor @RequiredArgsConstructor public class AccountEntity { - @Id - @GeneratedValue(strategy = IDENTITY) - private int id; + @Id + @GeneratedValue(strategy = IDENTITY) + private int id; - @NonNull - private int customerId; - private AccountType type = AccountType.CURRENT; + @NonNull + private int customerId; + private AccountType type = AccountType.CURRENT; - @NonNull - private BigDecimal balance; + @NonNull + private BigDecimal balance; } diff --git a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/service/AccountService.java b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/service/AccountService.java index 80667df..8eab142 100644 --- a/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/service/AccountService.java +++ b/bank-services/account-service/src/main/java/org/siriusxi/blueharvest/bank/as/service/AccountService.java @@ -25,68 +25,68 @@ @Log4j2 public class AccountService { - private final AccountRepository accountRepository; - private final TransactionIntegration transactionIntegration; + private final AccountRepository accountRepository; + private final TransactionIntegration transactionIntegration; - @Autowired - public AccountService( - AccountRepository accountRepository, TransactionIntegration transactionIntegration) { - this.accountRepository = accountRepository; - this.transactionIntegration = transactionIntegration; - } + @Autowired + public AccountService( + AccountRepository accountRepository, TransactionIntegration transactionIntegration) { + this.accountRepository = accountRepository; + this.transactionIntegration = transactionIntegration; + } + + /** + * This method getAccounts() return the accounts information with related + * transactions. + * + * @param customerId customer id + * @return List<Account> accounts and related transactions information. + * @since Harvest beta v0.1 + */ + public List getAccounts(int customerId) { - /** - * This method getAccounts() return the accounts information with related - * transactions. - * - * @param customerId customer id - * @return List<Account> accounts and related transactions information. - * @since Harvest beta v0.1 - */ - public List getAccounts(int customerId) { + log.trace("Calling - getAccounts -> Getting all accounts for customer ID {}", customerId); - log.trace("Calling - getAccounts -> Getting all accounts for customer ID {}", customerId); + var accounts = + accountRepository.findByCustomerId(customerId).stream() + .map( + entity -> + new Account( + customerId, + entity.getBalance(), + entity.getType(), + transactionIntegration.getAccountTransactions(entity.getId()))) + .collect(Collectors.toList()); - var accounts = - accountRepository.findByCustomerId(customerId).stream() - .map( - entity -> - new Account( - customerId, - entity.getBalance(), - entity.getType(), - transactionIntegration.getAccountTransactions(entity.getId()))) - .collect(Collectors.toList()); + log.trace("Returning - getAccounts -> With {} accounts", accounts.size()); - log.trace("Returning - getAccounts -> With {} accounts", accounts.size()); + return accounts; + } - return accounts; - } + /** + * This method createAccount() is responsible to create customer accounts with the + * following logic: + * + *
    + *
  • Once the endpoint is called, a new account will be opened connected to the user whose ID + * is customerID. + *
  • Also, if initialCredit is not 0, a transaction will be sent to the new account. + *
+ * + * @param entity is the account to save. + * @since Harvest beta v0.1 + */ + public void createAccount(AccountEntity entity) { + log.trace("Calling - createAccount -> to save account {}", entity); + accountRepository.save(entity); - /** - * This method createAccount() is responsible to create customer accounts with the - * following logic: - * - *
    - *
  • Once the endpoint is called, a new account will be opened connected to the user whose ID - * is customerID. - *
  • Also, if initialCredit is not 0, a transaction will be sent to the new account. - *
- * - * @param entity is the account to save. - * @since Harvest beta v0.1 - */ - public void createAccount(AccountEntity entity) { - log.trace("Calling - createAccount -> to save account {}", entity); - accountRepository.save(entity); + if (entity.getBalance().doubleValue() > 0.0) { + transactionIntegration.createTransaction( + new TransactionDTO(entity.getId(), entity.getBalance())); - if (entity.getBalance().doubleValue() > 0.0) { - transactionIntegration.createTransaction( - new TransactionDTO(entity.getId(), entity.getBalance())); + log.debug("New account transaction is created for account {}", entity.getId()); + } - log.debug("New account transaction is created for account {}", entity.getId()); + log.trace("Returning - createAccount -> account {} saved successfully", entity); } - - log.trace("Returning - createAccount -> account {} saved successfully", entity); - } } diff --git a/bank-services/account-service/src/main/resources/data.sql b/bank-services/account-service/src/main/resources/data.sql index 7d3a569..f430a42 100644 --- a/bank-services/account-service/src/main/resources/data.sql +++ b/bank-services/account-service/src/main/resources/data.sql @@ -1,8 +1,9 @@ DROP TABLE IF EXISTS account; -CREATE TABLE account ( - id INT AUTO_INCREMENT PRIMARY KEY, - customer_id INT, - type ENUM('CURRENT', 'SAVING') default 'CURRENT', - balance DECIMAL(10, 2) DEFAULT 0.0 -); \ No newline at end of file +CREATE TABLE account +( + id INT AUTO_INCREMENT PRIMARY KEY, + customer_id INT, + type ENUM ('CURRENT', 'SAVING') default 'CURRENT', + balance DECIMAL(10, 2) DEFAULT 0.0 +); diff --git a/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountControllerLayerTests.java b/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountControllerLayerTests.java index 8570b08..b63a5b3 100644 --- a/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountControllerLayerTests.java +++ b/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountControllerLayerTests.java @@ -30,62 +30,62 @@ @WebMvcTest(AccountController.class) class AccountControllerLayerTests { - @Autowired - private MockMvc mvc; + @Autowired + private MockMvc mvc; - @MockBean - private AccountService accountService; + @MockBean + private AccountService accountService; - @BeforeEach - void setup() { - // Given - given(accountService.getAccounts(1)) - .willReturn( - List.of( - new Account( - 1, - new BigDecimal("1.0"), - CURRENT, - List.of(new Transaction(1, CREDIT, new BigDecimal("1.0")))))); - } + @BeforeEach + void setup() { + // Given + given(accountService.getAccounts(1)) + .willReturn( + List.of( + new Account( + 1, + new BigDecimal("1.0"), + CURRENT, + List.of(new Transaction(1, CREDIT, new BigDecimal("1.0")))))); + } - @Test - void whenInitialCreditGreaterThanZero_thenCreateAccountAndTransaction() throws Exception { + @Test + void whenInitialCreditGreaterThanZero_thenCreateAccountAndTransaction() throws Exception { - // When - mvc.perform( + // When + mvc.perform( post("/bank/api/v1/accounts") .contentType(MediaType.APPLICATION_JSON) .content(toJson(new AccountDTO(1, new BigDecimal("1.0"))))) - .andExpect(status().isOk()); + .andExpect(status().isOk()); - var accounts = accountService.getAccounts(1); + var accounts = accountService.getAccounts(1); - // Then - assertThat(accounts.get(0).balance()).isEqualTo(new BigDecimal("1.0")); + // Then + assertThat(accounts.get(0).balance()).isEqualTo(new BigDecimal("1.0")); - assertThat(accounts.get(0).type()).isEqualTo(CURRENT); + assertThat(accounts.get(0).type()).isEqualTo(CURRENT); - assertThat(accounts.get(0).transactions()).isNotNull(); - assertThat(accounts.get(0).transactions().size()).isEqualTo(1); - } + assertThat(accounts.get(0).transactions()).isNotNull(); + assertThat(accounts.get(0).transactions().size()).isEqualTo(1); + } - @Test - void getAccountsByCustomerId_thenReturnAccountsAndTransactionsJsonArray() throws Exception { + @Test + void getAccountsByCustomerId_thenReturnAccountsAndTransactionsJsonArray() throws Exception { - // When - mvc.perform(get("/bank/api/v1/accounts?customerId=1") - .contentType(MediaType.APPLICATION_JSON)) - // Then - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$", hasSize(1))) - .andExpect(jsonPath("$[0].balance", is(1.0))) - .andExpect(jsonPath("$[0].customerId", is(1))) - .andExpect(jsonPath("$[0].type", is("CURRENT"))) - .andExpect(jsonPath("$[0].transactions", hasSize(1))) - .andExpect(jsonPath("$[0].transactions[0].accountId", is(1))) - .andExpect(jsonPath("$[0].transactions[0].type", is("CREDIT"))) - .andExpect(jsonPath("$[0].transactions[0].amount", is(1.0))); - } + // When + mvc.perform(get("/bank/api/v1/accounts?customerId=1") + .contentType(MediaType.APPLICATION_JSON)) + // Then + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$[0].balance", is(1.0))) + .andExpect(jsonPath("$[0].customerId", is(1))) + .andExpect(jsonPath("$[0].type", is("CURRENT"))) + .andExpect(jsonPath("$[0].transactions", hasSize(1))) + .andExpect(jsonPath("$[0].transactions[0].accountId", is(1))) + .andExpect(jsonPath("$[0].transactions[0].type", is("CREDIT"))) + .andExpect(jsonPath("$[0].transactions[0].amount", is(1.0))); + } } diff --git a/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountRepositoryLayerTests.java b/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountRepositoryLayerTests.java index ddb2dfc..bcf9232 100644 --- a/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountRepositoryLayerTests.java +++ b/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountRepositoryLayerTests.java @@ -22,8 +22,8 @@ class AccountRepositoryLayerTests { void whenFindByCustomerId_thenReturnAccounts() { // given accountRepository.saveAll(List.of( - new AccountEntity(99, new BigDecimal("100.20")), - new AccountEntity(99, new BigDecimal("200.20")))); + new AccountEntity(99, new BigDecimal("100.20")), + new AccountEntity(99, new BigDecimal("200.20")))); // when var accounts = accountRepository.findByCustomerId(99); diff --git a/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountServiceIntegrationTests.java b/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountServiceIntegrationTests.java index 5b16d13..cd8db72 100644 --- a/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountServiceIntegrationTests.java +++ b/bank-services/account-service/src/test/java/org/siriusxi/blueharvest/bank/as/AccountServiceIntegrationTests.java @@ -34,42 +34,42 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @SpringBootTest( - webEnvironment = RANDOM_PORT, - classes = AccountServiceApplication.class) + webEnvironment = RANDOM_PORT, + classes = AccountServiceApplication.class) @AutoConfigureMockMvc class AccountServiceIntegrationTests { - @Autowired - private MockMvc mvc; + @Autowired + private MockMvc mvc; - @Autowired - private AccountService accountService; + @Autowired + private AccountService accountService; - @MockBean - private TransactionIntegration transactionIntegration; + @MockBean + private TransactionIntegration transactionIntegration; - @Autowired - private AccountRepository accountRepository; + @Autowired + private AccountRepository accountRepository; - @AfterEach - public void resetDb() { - accountRepository.deleteAll(); - } + @AfterEach + public void resetDb() { + accountRepository.deleteAll(); + } - @Test - void addAccountWithInitialCreditGreaterThanZero_thenReturnAccountsAndTransactionsJsonArray() - throws Exception { - // Given - var account = new AccountEntity(1, new BigDecimal("90.11")); - accountService.createAccount(account); + @Test + void addAccountWithInitialCreditGreaterThanZero_thenReturnAccountsAndTransactionsJsonArray() + throws Exception { + // Given + var account = new AccountEntity(1, new BigDecimal("90.11")); + accountService.createAccount(account); - given(transactionIntegration.getAccountTransactions(1)) + given(transactionIntegration.getAccountTransactions(1)) .willReturn(List.of(new Transaction(1, CREDIT, new BigDecimal("90.11")))); - // When - mvc.perform(get("/bank/api/v1/accounts?customerId=1") - .contentType(MediaType.APPLICATION_JSON)) + // When + mvc.perform(get("/bank/api/v1/accounts?customerId=1") + .contentType(MediaType.APPLICATION_JSON)) // Then .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) @@ -81,21 +81,21 @@ void addAccountWithInitialCreditGreaterThanZero_thenReturnAccountsAndTransaction .andExpect(jsonPath("$[0].transactions[0].accountId", is(1))) .andExpect(jsonPath("$[0].transactions[0].type", is("CREDIT"))) .andExpect(jsonPath("$[0].transactions[0].amount", is(90.11))); - } + } - @Test - void addAccountWithInitialCreditEqualToZero_thenReturnAccountJsonArray() - throws Exception { - // Given - var account = new AccountEntity(1, new BigDecimal("0.0")); - accountService.createAccount(account); + @Test + void addAccountWithInitialCreditEqualToZero_thenReturnAccountJsonArray() + throws Exception { + // Given + var account = new AccountEntity(1, new BigDecimal("0.0")); + accountService.createAccount(account); - given(transactionIntegration.getAccountTransactions(1)) + given(transactionIntegration.getAccountTransactions(1)) .willReturn(emptyList()); - // When - mvc.perform(get("/bank/api/v1/accounts?customerId=1") - .contentType(MediaType.APPLICATION_JSON)) + // When + mvc.perform(get("/bank/api/v1/accounts?customerId=1") + .contentType(MediaType.APPLICATION_JSON)) // Then .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) @@ -104,31 +104,31 @@ void addAccountWithInitialCreditEqualToZero_thenReturnAccountJsonArray() .andExpect(jsonPath("$[0].customerId", is(1))) .andExpect(jsonPath("$[0].type", is("CURRENT"))) .andExpect(jsonPath("$[0].transactions", hasSize(0))); - } + } - @Test - void addAccountWithInitialCreditGreaterThanZero_thenCreateAccountAndTransaction() throws Exception { + @Test + void addAccountWithInitialCreditGreaterThanZero_thenCreateAccountAndTransaction() throws Exception { - // When - mvc.perform( + // When + mvc.perform( post("/bank/api/v1/accounts") - .contentType(MediaType.APPLICATION_JSON) - .content(toJson(new AccountDTO(1, new BigDecimal("1.00"))))) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(new AccountDTO(1, new BigDecimal("1.00"))))) .andExpect(status().isOk()); - given(transactionIntegration - .getAccountTransactions(accountRepository.findAll().iterator().next().getId())) + given(transactionIntegration + .getAccountTransactions(accountRepository.findAll().iterator().next().getId())) .willReturn(List.of(new Transaction(1, CREDIT, new BigDecimal("1.00")))); - var accounts = accountService.getAccounts(1); + var accounts = accountService.getAccounts(1); - // Then - assertThat(accounts.get(0).balance()).isEqualTo(new BigDecimal("1.00")); + // Then + assertThat(accounts.get(0).balance()).isEqualTo(new BigDecimal("1.00")); - assertThat(accounts.get(0).type()).isEqualTo(CURRENT); + assertThat(accounts.get(0).type()).isEqualTo(CURRENT); - assertThat(accounts.get(0).transactions()).isNotNull(); + assertThat(accounts.get(0).transactions()).isNotNull(); - assertThat(accounts.get(0).transactions().size()).isEqualTo(1); - } + assertThat(accounts.get(0).transactions().size()).isEqualTo(1); + } } diff --git a/bank-services/customer-service/pom.xml b/bank-services/customer-service/pom.xml index 182364d..f989dd9 100644 --- a/bank-services/customer-service/pom.xml +++ b/bank-services/customer-service/pom.xml @@ -18,7 +18,7 @@ org.springdoc springdoc-openapi-ui - 1.4.5 + 1.5.9 diff --git a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/CustomerServiceApplication.java b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/CustomerServiceApplication.java index f0917c5..38b7f0f 100644 --- a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/CustomerServiceApplication.java +++ b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/CustomerServiceApplication.java @@ -6,8 +6,8 @@ @SpringBootApplication public class CustomerServiceApplication { - public static void main(String[] args) { - SpringApplication.run(CustomerServiceApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(CustomerServiceApplication.class, args); + } } diff --git a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/api/CustomerController.java b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/api/CustomerController.java index e4428c3..48b8893 100644 --- a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/api/CustomerController.java +++ b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/api/CustomerController.java @@ -26,22 +26,20 @@ public CustomerController(CustomerService customerService) { this.customerService = customerService; } - @GetMapping( - value = "customers", - produces = APPLICATION_JSON_VALUE) + @GetMapping(value = "customers", produces = APPLICATION_JSON_VALUE) public List getCustomers() { return customerService.getCustomers(); } - @PostMapping( - value = "customers/{id}/accounts", - consumes = APPLICATION_JSON_VALUE) + @PostMapping(value = "customers/{id}/accounts", consumes = APPLICATION_JSON_VALUE) public void createAccount(@PathVariable int id, @RequestBody AccountDTO account) { requireNonNull(account, "Invalid account data."); if (id <= 0 || account.getInitialCredit().doubleValue() < 0.0) - throw new InvalidInputException(format("Invalid data (Customer Id= %d, " + - "initialCredit= %.2f)", id, account.getInitialCredit().doubleValue())); + throw new InvalidInputException( + format( + "Invalid data (Customer Id= %d, initialCredit= %.2f)", + id, account.getInitialCredit().doubleValue())); account.setCustomerId(id); customerService.createCustomerAccount(account); diff --git a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/config/ServiceConfiguration.java b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/config/ServiceConfiguration.java index d555f25..9fa0b0e 100644 --- a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/config/ServiceConfiguration.java +++ b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/config/ServiceConfiguration.java @@ -7,8 +7,8 @@ @Configuration public class ServiceConfiguration { - @Bean - RestTemplate restTemplate() { - return new RestTemplate(); - } + @Bean + RestTemplate restTemplate() { + return new RestTemplate(); + } } diff --git a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/infra/filter/GlobalControllerExceptionHandler.java b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/infra/filter/GlobalControllerExceptionHandler.java index 6baafdf..4e1fdb4 100644 --- a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/infra/filter/GlobalControllerExceptionHandler.java +++ b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/infra/filter/GlobalControllerExceptionHandler.java @@ -19,50 +19,50 @@ *

It act as filter so it is pluggable component just added to microservice context * automatically, when you add ComponentScan on your application. * - * @see org.springframework.context.annotation.ComponentScan * @author Mohamed Taman * @version 0.5 + * @see org.springframework.context.annotation.ComponentScan * @since Harvest beta v0.1 */ @RestControllerAdvice @Log4j2 class GlobalControllerExceptionHandler { - /** - * Method to handle Not found exceptions http error info. - * - * @param ex the ex to get its information - * @return the http error information. - * @since v0.1 - */ - @ResponseStatus(NOT_FOUND) - @ExceptionHandler(NotFoundException.class) - public @ResponseBody - HttpErrorInfo handleNotFoundExceptions(Exception ex) { + /** + * Method to handle Not found exceptions http error info. + * + * @param ex the ex to get its information + * @return the http error information. + * @since v0.1 + */ + @ResponseStatus(NOT_FOUND) + @ExceptionHandler(NotFoundException.class) + public @ResponseBody + HttpErrorInfo handleNotFoundExceptions(Exception ex) { - return createHttpErrorInfo(NOT_FOUND, ex); - } + return createHttpErrorInfo(NOT_FOUND, ex); + } - /** - * Method to handle invalid input exception http error info. - * - * @param ex the ex to get its information - * @return the http error information. - * @since v0.1 - */ - @ResponseStatus(UNPROCESSABLE_ENTITY) - @ExceptionHandler(InvalidInputException.class) - public @ResponseBody - HttpErrorInfo handleInvalidInputException(Exception ex) { + /** + * Method to handle invalid input exception http error info. + * + * @param ex the ex to get its information + * @return the http error information. + * @since v0.1 + */ + @ResponseStatus(UNPROCESSABLE_ENTITY) + @ExceptionHandler(InvalidInputException.class) + public @ResponseBody + HttpErrorInfo handleInvalidInputException(Exception ex) { - return createHttpErrorInfo(UNPROCESSABLE_ENTITY, ex); - } + return createHttpErrorInfo(UNPROCESSABLE_ENTITY, ex); + } - private HttpErrorInfo createHttpErrorInfo( - HttpStatus httpStatus , Exception ex) { - final var message = ex.getMessage(); + private HttpErrorInfo createHttpErrorInfo( + HttpStatus httpStatus, Exception ex) { + final var message = ex.getMessage(); - log.debug("Returning HTTP status: {}, message: {}", httpStatus, message); - return new HttpErrorInfo(httpStatus, message); - } + log.debug("Returning HTTP status: {}, message: {}", httpStatus, message); + return new HttpErrorInfo(httpStatus, message); + } } diff --git a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/infra/filter/HttpErrorInfo.java b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/infra/filter/HttpErrorInfo.java index 6383571..3ab7f29 100644 --- a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/infra/filter/HttpErrorInfo.java +++ b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/infra/filter/HttpErrorInfo.java @@ -14,19 +14,18 @@ * @implNote Since it is a record and not normal POJO, so it needs some customizations * to be serialized to JSON and this is done with method * GlobalConfiguration.jacksonCustomizer(). - * - * @see Record * @author Mohamed Taman * @version 0.5 + * @see java.lang.Record * @since Harvest beta v0.1 */ public record HttpErrorInfo( - @JsonProperty("status") HttpStatus httpStatus, - @JsonProperty("message") String message, - @JsonProperty("timestamp") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd@HH:mm:ss.SSSZ") - @JsonSerialize(using = ZonedDateTimeSerializer.class) - ZonedDateTime timestamp) { +@JsonProperty("status") HttpStatus httpStatus, +@JsonProperty("message") String message, +@JsonProperty("timestamp") +@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd@HH:mm:ss.SSSZ") +@JsonSerialize(using = ZonedDateTimeSerializer.class) + ZonedDateTime timestamp){ /** * Instantiates a new Http error info. @@ -34,7 +33,7 @@ public record HttpErrorInfo( * @param httpStatus the http status code and type. * @param message the error message. */ -public HttpErrorInfo(HttpStatus httpStatus, String message) { - this(httpStatus, message, ZonedDateTime.now()); + public HttpErrorInfo(HttpStatus httpStatus,String message){ + this(httpStatus,message,ZonedDateTime.now()); } } diff --git a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/integration/AccountIntegration.java b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/integration/AccountIntegration.java index a042a78..2d2d54e 100644 --- a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/integration/AccountIntegration.java +++ b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/integration/AccountIntegration.java @@ -21,36 +21,37 @@ public class AccountIntegration { private final String accountServiceUrl; - private static final String BASE_URL = "/bank/api/v1/"; + private static final String BASE_URL = "/bank/api/v1/"; - private static final String QUERY_PARAM = "?customerId="; + private static final String QUERY_PARAM = "?customerId="; @Autowired public AccountIntegration( - RestTemplate restTemplate, - @Value("${app.account-service.host}") String accountServiceHost, - @Value("${app.account-service.port}") int accountServicePort) { + RestTemplate restTemplate, + @Value("${app.account-service.host}") String accountServiceHost, + @Value("${app.account-service.port}") int accountServicePort) { this.restTemplate = restTemplate; accountServiceUrl = "http://" - .concat(accountServiceHost) - .concat(":") - .concat(String.valueOf(accountServicePort)) - .concat(BASE_URL) - .concat("accounts"); + .concat(accountServiceHost) + .concat(":") + .concat(String.valueOf(accountServicePort)) + .concat(BASE_URL) + .concat("accounts"); } - public List getCustomerAccounts(int customerId){ + public List getCustomerAccounts(int customerId) { String url = accountServiceUrl.concat(QUERY_PARAM).concat(String.valueOf(customerId)); log.debug("Will call getCustomerAccounts API on URL: {}", url); return restTemplate.exchange(url, GET, null, - new ParameterizedTypeReference>() {}).getBody(); + new ParameterizedTypeReference>() { + }).getBody(); } public void createAccount(AccountDTO account) { - restTemplate.postForObject(accountServiceUrl,account, AccountDTO.class); + restTemplate.postForObject(accountServiceUrl, account, AccountDTO.class); } } diff --git a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/persistence/entity/CustomerEntity.java b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/persistence/entity/CustomerEntity.java index 646e7c4..5b22923 100644 --- a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/persistence/entity/CustomerEntity.java +++ b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/persistence/entity/CustomerEntity.java @@ -19,13 +19,13 @@ @Data public class CustomerEntity { - @Id - @GeneratedValue(strategy = IDENTITY) - private int id; + @Id + @GeneratedValue(strategy = IDENTITY) + private int id; - private String firstName; - private String lastName; + private String firstName; + private String lastName; - private BigDecimal balance; + private BigDecimal balance; } diff --git a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/service/CustomerService.java b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/service/CustomerService.java index 646b154..37c60ff 100644 --- a/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/service/CustomerService.java +++ b/bank-services/customer-service/src/main/java/org/siriusxi/blueharvest/bank/cs/service/CustomerService.java @@ -27,60 +27,60 @@ @Log4j2 public class CustomerService { - private final CustomerRepository customerRepository; - private final AccountIntegration accountIntegration; + private final CustomerRepository customerRepository; + private final AccountIntegration accountIntegration; - @Autowired - public CustomerService( - CustomerRepository customerRepository, AccountIntegration accountIntegration) { - this.customerRepository = customerRepository; - this.accountIntegration = accountIntegration; - } + @Autowired + public CustomerService( + CustomerRepository customerRepository, AccountIntegration accountIntegration) { + this.customerRepository = customerRepository; + this.accountIntegration = accountIntegration; + } - /** - * This method getCustomers() return the user information showing Name, Surname, - * balance, and transactions of the accounts. - * - * @return List<CustomerAggregate> customers information. - * @since Harvest beta v0.1 - */ - public List getCustomers() { + /** + * This method getCustomers() return the user information showing Name, Surname, + * balance, and transactions of the accounts. + * + * @return List<CustomerAggregate> customers information. + * @since Harvest beta v0.1 + */ + public List getCustomers() { - return StreamSupport.stream(customerRepository.findAll().spliterator(), false) - .map( - entity -> - new CustomerAggregate( - entity.getId(), - entity.getFirstName(), - entity.getLastName(), - entity.getBalance(), - accountIntegration.getCustomerAccounts(entity.getId()))) - .collect(Collectors.toList()); - } + return StreamSupport.stream(customerRepository.findAll().spliterator(), false) + .map( + entity -> + new CustomerAggregate( + entity.getId(), + entity.getFirstName(), + entity.getLastName(), + entity.getBalance(), + accountIntegration.getCustomerAccounts(entity.getId()))) + .collect(Collectors.toList()); + } - /** - * This method accepts customer Id and initial credit and then - * create the customer account and related transaction if any. - * - * @param account info to be created - * @since Harvest beta v0.1 - */ - public void createCustomerAccount(AccountDTO account) { - // get User first - var customer = - customerRepository - .findById(account.getCustomerId()) - .orElseThrow( - () -> - new NotFoundException( - format("No Customer found for id {%d}", account.getCustomerId()))); - // create customer account - accountIntegration.createAccount(account); + /** + * This method accepts customer Id and initial credit and then + * create the customer account and related transaction if any. + * + * @param account info to be created + * @since Harvest beta v0.1 + */ + public void createCustomerAccount(AccountDTO account) { + // get User first + var customer = + customerRepository + .findById(account.getCustomerId()) + .orElseThrow( + () -> + new NotFoundException( + format("No Customer found for id {%d}", account.getCustomerId()))); + // create customer account + accountIntegration.createAccount(account); - // Update customer balance - customer.setBalance(customer.getBalance().add(account.getInitialCredit())); + // Update customer balance + customer.setBalance(customer.getBalance().add(account.getInitialCredit())); - // Update customer - customerRepository.save(customer); - } + // Update customer + customerRepository.save(customer); + } } diff --git a/bank-services/customer-service/src/main/resources/application.yml b/bank-services/customer-service/src/main/resources/application.yml index cc6f125..e990ab0 100644 --- a/bank-services/customer-service/src/main/resources/application.yml +++ b/bank-services/customer-service/src/main/resources/application.yml @@ -8,6 +8,9 @@ spring: h2: console: enabled: true + jpa: + hibernate: + ddl-auto: update # Server configs server: diff --git a/bank-services/customer-service/src/main/resources/data.sql b/bank-services/customer-service/src/main/resources/data.sql index 0e812da..40d4f39 100644 --- a/bank-services/customer-service/src/main/resources/data.sql +++ b/bank-services/customer-service/src/main/resources/data.sql @@ -1,17 +1,18 @@ DROP TABLE IF EXISTS customer; -CREATE TABLE customer ( - id INT AUTO_INCREMENT PRIMARY KEY, - first_name VARCHAR(250) NOT NULL, - last_name VARCHAR(250) NOT NULL, - balance DECIMAL(10, 2) DEFAULT 0.0 +CREATE TABLE customer +( + id INT AUTO_INCREMENT PRIMARY KEY, + first_name VARCHAR(250) NOT NULL, + last_name VARCHAR(250) NOT NULL, + balance DECIMAL(10, 2) DEFAULT 0.0 ); -INSERT INTO customer (first_name, last_name) VALUES - ('Mohamed', 'Taman'), - ('Issa', 'Ahmed'), - ('Matin', 'Abbasi'), - ('Milica', 'Jovicic'), - ('Angèl', 'Wijnhard'), - ('Fatih', 'Akbas'), - ('Ibrahim', 'Moustafa'); \ No newline at end of file +INSERT INTO customer (first_name, last_name) +VALUES ('Mohamed', 'Taman'), + ('Issa', 'Ahmed'), + ('Matin', 'Abbasi'), + ('Milica', 'Jovicic'), + ('Angèl', 'Wijnhard'), + ('Fatih', 'Akbas'), + ('Ibrahim', 'Moustafa'); diff --git a/bank-services/customer-service/src/test/java/org/siriusxi/blueharvest/bank/cs/CustomerServiceIntegrationTests.java b/bank-services/customer-service/src/test/java/org/siriusxi/blueharvest/bank/cs/CustomerServiceIntegrationTests.java index af2e56f..6cc75c4 100644 --- a/bank-services/customer-service/src/test/java/org/siriusxi/blueharvest/bank/cs/CustomerServiceIntegrationTests.java +++ b/bank-services/customer-service/src/test/java/org/siriusxi/blueharvest/bank/cs/CustomerServiceIntegrationTests.java @@ -35,87 +35,87 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @SpringBootTest( - webEnvironment = RANDOM_PORT, - classes = CustomerServiceApplication.class) + webEnvironment = RANDOM_PORT, + classes = CustomerServiceApplication.class) @AutoConfigureMockMvc @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class CustomerServiceIntegrationTests { - @Autowired - private MockMvc mvc; + @Autowired + private MockMvc mvc; - @MockBean - private AccountIntegration accountIntegration; + @MockBean + private AccountIntegration accountIntegration; - @Autowired - private CustomerRepository customerRepository; + @Autowired + private CustomerRepository customerRepository; - @Test - @Order(1) - void createAccountForCustomerDoesNotExist() throws Exception { + @Test + @Order(1) + void createAccountForCustomerDoesNotExist() throws Exception { - // When - mvc.perform( + // When + mvc.perform( post("/bank/api/v1/customers/12/accounts") .contentType(MediaType.APPLICATION_JSON) .content(toJson(new AccountDTO(new BigDecimal("1.00"))))) - .andExpect(status().isNotFound()) - .andExpect(jsonPath("$.status", is("NOT_FOUND"))) - .andExpect(jsonPath("$.message", is("No Customer found for id {12}"))); - } + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.status", is("NOT_FOUND"))) + .andExpect(jsonPath("$.message", is("No Customer found for id {12}"))); + } - @Test - @Order(2) - void createAccountInvalidCustomerIdLessThanZero() throws Exception { + @Test + @Order(2) + void createAccountInvalidCustomerIdLessThanZero() throws Exception { - // When - mvc.perform( + // When + mvc.perform( post("/bank/api/v1/customers/-1/accounts") .contentType(MediaType.APPLICATION_JSON) .content(toJson(new AccountDTO(new BigDecimal("100.00"))))) - .andExpect(status().isUnprocessableEntity()) - .andExpect(jsonPath("$.status", is("UNPROCESSABLE_ENTITY"))) - .andExpect( - jsonPath( - "$.message", - is("Invalid data (Customer Id= -1, initialCredit= 100.00)"))); - } - - @Test - @Order(3) - void createAccountInvalidCreditLessThanZero() throws Exception { - - // When - mvc.perform( + .andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.status", is("UNPROCESSABLE_ENTITY"))) + .andExpect( + jsonPath( + "$.message", + is("Invalid data (Customer Id= -1, initialCredit= 100.00)"))); + } + + @Test + @Order(3) + void createAccountInvalidCreditLessThanZero() throws Exception { + + // When + mvc.perform( post("/bank/api/v1/customers/1/accounts") .contentType(MediaType.APPLICATION_JSON) .content(toJson(new AccountDTO(new BigDecimal("-100.00"))))) - .andExpect(status().isUnprocessableEntity()) - .andExpect(jsonPath("$.status", is("UNPROCESSABLE_ENTITY"))) - .andExpect( - jsonPath( - "$.message", - is("Invalid data (Customer Id= 1, initialCredit= -100.00)"))); - } - - @Test - @Order(4) - void createAccountWithCreditZero_ThenGetCustomerWitAccountNoTransaction() throws Exception { - - // Given - mvc.perform( + .andExpect(status().isUnprocessableEntity()) + .andExpect(jsonPath("$.status", is("UNPROCESSABLE_ENTITY"))) + .andExpect( + jsonPath( + "$.message", + is("Invalid data (Customer Id= 1, initialCredit= -100.00)"))); + } + + @Test + @Order(4) + void createAccountWithCreditZero_ThenGetCustomerWitAccountNoTransaction() throws Exception { + + // Given + mvc.perform( post("/bank/api/v1/customers/1/accounts") .contentType(MediaType.APPLICATION_JSON) .content(toJson(new AccountDTO(new BigDecimal("0.00"))))) - .andExpect(status().isOk()); + .andExpect(status().isOk()); - given(accountIntegration.getCustomerAccounts(1)) - .willReturn( - List.of(new Account(1, + given(accountIntegration.getCustomerAccounts(1)) + .willReturn( + List.of(new Account(1, new BigDecimal("0.0"), CURRENT, Collections.emptyList()))); - // When - mvc.perform(get("/bank/api/v1/customers").contentType(MediaType.APPLICATION_JSON)) + // When + mvc.perform(get("/bank/api/v1/customers").contentType(MediaType.APPLICATION_JSON)) // Then .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) @@ -124,27 +124,27 @@ void createAccountWithCreditZero_ThenGetCustomerWitAccountNoTransaction() throws .andExpect(jsonPath("$[0].balance", is(0.0))) .andExpect(jsonPath("$[0].accounts[0].transactions", hasSize(0))); - } + } - @Test - @Order(5) - void createAccountWithCredit_ThenGetCustomerWitAccountAndTransaction() throws Exception { + @Test + @Order(5) + void createAccountWithCredit_ThenGetCustomerWitAccountAndTransaction() throws Exception { - // Given - mvc.perform( + // Given + mvc.perform( post("/bank/api/v1/customers/2/accounts") - .contentType(MediaType.APPLICATION_JSON) - .content(toJson(new AccountDTO(new BigDecimal("100.20"))))) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(new AccountDTO(new BigDecimal("100.20"))))) .andExpect(status().isOk()); - given(accountIntegration.getCustomerAccounts(2)) + given(accountIntegration.getCustomerAccounts(2)) .willReturn( - List.of(new Account(2, - new BigDecimal("100.20"), CURRENT, - List.of(new Transaction(1,CREDIT,new BigDecimal("100.20")))))); + List.of(new Account(2, + new BigDecimal("100.20"), CURRENT, + List.of(new Transaction(1, CREDIT, new BigDecimal("100.20")))))); - // When - mvc.perform(get("/bank/api/v1/customers").contentType(MediaType.APPLICATION_JSON)) + // When + mvc.perform(get("/bank/api/v1/customers").contentType(MediaType.APPLICATION_JSON)) // Then .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) @@ -155,36 +155,36 @@ void createAccountWithCredit_ThenGetCustomerWitAccountAndTransaction() throws Ex .andExpect(jsonPath("$[1].accounts[0].balance", is(100.20))) .andExpect(jsonPath("$[1].accounts[0].transactions", hasSize(1))) .andExpect(jsonPath("$[1].accounts[0].transactions[0].amount", is(100.20))); - } + } - @Test - @Order(6) - void create2AccountsForTheSameCustomer_thenCustomerBalanceGetUpdated() throws Exception { + @Test + @Order(6) + void create2AccountsForTheSameCustomer_thenCustomerBalanceGetUpdated() throws Exception { - // Given - mvc.perform( + // Given + mvc.perform( post("/bank/api/v1/customers/1/accounts") - .contentType(MediaType.APPLICATION_JSON) - .content(toJson(new AccountDTO(new BigDecimal("100.20"))))) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(new AccountDTO(new BigDecimal("100.20"))))) .andExpect(status().isOk()); - mvc.perform( + mvc.perform( post("/bank/api/v1/customers/1/accounts") - .contentType(MediaType.APPLICATION_JSON) - .content(toJson(new AccountDTO(new BigDecimal("100.20"))))) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(new AccountDTO(new BigDecimal("100.20"))))) .andExpect(status().isOk()); - given(accountIntegration.getCustomerAccounts(1)) + given(accountIntegration.getCustomerAccounts(1)) .willReturn( - List.of(new Account(1, - new BigDecimal("100.20"), CURRENT, - List.of(new Transaction(1,CREDIT,new BigDecimal("100.20"))) - ), new Account(1, - new BigDecimal("100.20"), CURRENT, - List.of(new Transaction(1,CREDIT,new BigDecimal("100.20")))))); - - // When - mvc.perform(get("/bank/api/v1/customers").contentType(MediaType.APPLICATION_JSON)) + List.of(new Account(1, + new BigDecimal("100.20"), CURRENT, + List.of(new Transaction(1, CREDIT, new BigDecimal("100.20"))) + ), new Account(1, + new BigDecimal("100.20"), CURRENT, + List.of(new Transaction(1, CREDIT, new BigDecimal("100.20")))))); + + // When + mvc.perform(get("/bank/api/v1/customers").contentType(MediaType.APPLICATION_JSON)) // Then .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) @@ -196,23 +196,23 @@ void create2AccountsForTheSameCustomer_thenCustomerBalanceGetUpdated() throws Ex .andExpect(jsonPath("$[0].accounts[0].transactions", hasSize(1))) .andExpect(jsonPath("$[0].accounts[0].transactions[0].amount", is(100.20))); - var found = customerRepository.findById(1); - CustomerEntity customer = null; - if(found.isPresent()) - customer = found.get(); - - assert customer != null; - assertThat(customer.getBalance()).isEqualTo(new BigDecimal("200.40")); - } - - @Test - @Order(7) - void getAllSevenCustomers() throws Exception { - - mvc.perform(get("/bank/api/v1/customers").contentType(MediaType.APPLICATION_JSON)) - // Then - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$", hasSize(7))); - } + var found = customerRepository.findById(1); + CustomerEntity customer = null; + if (found.isPresent()) + customer = found.get(); + + assert customer != null; + assertThat(customer.getBalance()).isEqualTo(new BigDecimal("200.40")); + } + + @Test + @Order(7) + void getAllSevenCustomers() throws Exception { + + mvc.perform(get("/bank/api/v1/customers").contentType(MediaType.APPLICATION_JSON)) + // Then + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$", hasSize(7))); + } } diff --git a/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceApplication.java b/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceApplication.java index 5079c3f..6a525a4 100644 --- a/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceApplication.java +++ b/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceApplication.java @@ -6,8 +6,8 @@ @SpringBootApplication public class TransactionServiceApplication { - public static void main(String[] args) { - SpringApplication.run(TransactionServiceApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(TransactionServiceApplication.class, args); + } } diff --git a/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/api/TransactionController.java b/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/api/TransactionController.java index ade6c0a..c0234da 100644 --- a/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/api/TransactionController.java +++ b/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/api/TransactionController.java @@ -17,26 +17,26 @@ @Log4j2 public class TransactionController { - private final TransactionService transactionService; - - @Autowired - public TransactionController(TransactionService transactionService) { - this.transactionService = transactionService; - } - - @GetMapping( - value = "transactions", - produces = APPLICATION_JSON_VALUE) - public List getTransactions(@RequestParam("accountId") int accountId) { - return transactionService.getTransactions(accountId); - } - - @PostMapping( - value = "/transactions", - consumes = APPLICATION_JSON_VALUE) - public void createTransaction(@RequestBody TransactionDTO transaction) { - transactionService.createTransaction(new TransactionEntity(transaction.getAccountId(), + private final TransactionService transactionService; + + @Autowired + public TransactionController(TransactionService transactionService) { + this.transactionService = transactionService; + } + + @GetMapping( + value = "transactions", + produces = APPLICATION_JSON_VALUE) + public List getTransactions(@RequestParam("accountId") int accountId) { + return transactionService.getTransactions(accountId); + } + + @PostMapping( + value = "/transactions", + consumes = APPLICATION_JSON_VALUE) + public void createTransaction(@RequestBody TransactionDTO transaction) { + transactionService.createTransaction(new TransactionEntity(transaction.getAccountId(), transaction.getAmount())); - log.debug("createTransaction: creates a new Transaction {}", transaction); - } + log.debug("createTransaction: creates a new Transaction {}", transaction); + } } diff --git a/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/persistence/entity/TransactionEntity.java b/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/persistence/entity/TransactionEntity.java index f4e0dc0..58b467e 100644 --- a/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/persistence/entity/TransactionEntity.java +++ b/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/persistence/entity/TransactionEntity.java @@ -23,15 +23,15 @@ @RequiredArgsConstructor public class TransactionEntity { - @Id - @GeneratedValue(strategy = IDENTITY) - private int id; + @Id + @GeneratedValue(strategy = IDENTITY) + private int id; - @NonNull - private int accountId; + @NonNull + private int accountId; - private TransactionType type = TransactionType.CREDIT; + private TransactionType type = TransactionType.CREDIT; - @NonNull - private BigDecimal amount; + @NonNull + private BigDecimal amount; } diff --git a/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/service/TransactionService.java b/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/service/TransactionService.java index 76d6d79..59d91e4 100644 --- a/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/service/TransactionService.java +++ b/bank-services/transaction-service/src/main/java/org/siriusxi/blueharvest/bank/ts/service/TransactionService.java @@ -22,13 +22,13 @@ public TransactionService(TransactionRepository transactionRepository) { public List getTransactions(int accountId) { return transactionRepository.findByAccountId(accountId) - .stream() - .map(entity -> new Transaction(accountId, entity.getType() - ,entity.getAmount())) - .collect(Collectors.toList()); + .stream() + .map(entity -> new Transaction(accountId, entity.getType() + , entity.getAmount())) + .collect(Collectors.toList()); } - public void createTransaction(TransactionEntity entity){ + public void createTransaction(TransactionEntity entity) { transactionRepository.save(entity); } } diff --git a/bank-services/transaction-service/src/main/resources/data.sql b/bank-services/transaction-service/src/main/resources/data.sql index 5ffd16c..8b0694d 100644 --- a/bank-services/transaction-service/src/main/resources/data.sql +++ b/bank-services/transaction-service/src/main/resources/data.sql @@ -1,8 +1,9 @@ DROP TABLE IF EXISTS account_transaction; -CREATE TABLE account_transaction ( - id INT AUTO_INCREMENT PRIMARY KEY, - account_id INT, - type ENUM('CREDIT', 'DEBIT') default 'CREDIT', - amount DECIMAL(10, 2) DEFAULT 0.0 -); \ No newline at end of file +CREATE TABLE account_transaction +( + id INT AUTO_INCREMENT PRIMARY KEY, + account_id INT, + type ENUM ('CREDIT', 'DEBIT') default 'CREDIT', + amount DECIMAL(10, 2) DEFAULT 0.0 +); diff --git a/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionControllerLayerTests.java b/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionControllerLayerTests.java index 63419fb..b2ce4fa 100644 --- a/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionControllerLayerTests.java +++ b/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionControllerLayerTests.java @@ -30,59 +30,59 @@ @WebMvcTest(TransactionController.class) class TransactionControllerLayerTests { - @Autowired - private MockMvc mvc; + @Autowired + private MockMvc mvc; - @MockBean - private TransactionService transactionService; + @MockBean + private TransactionService transactionService; - @Test - void whenValidInput_thenCreateTransaction() throws Exception { - // Given - given(transactionService.getTransactions(1)) + @Test + void whenValidInput_thenCreateTransaction() throws Exception { + // Given + given(transactionService.getTransactions(1)) .willReturn( - List.of( - new Transaction(1, CREDIT, new BigDecimal("10000.77")))); + List.of( + new Transaction(1, CREDIT, new BigDecimal("10000.77")))); - // When - mvc.perform( + // When + mvc.perform( post("/bank/api/v1/transactions") .contentType(MediaType.APPLICATION_JSON) .content(toJson(new TransactionDTO(1, new BigDecimal("10000.77"))))) - .andExpect(status().isOk()); + .andExpect(status().isOk()); - List found = transactionService.getTransactions(1); + List found = transactionService.getTransactions(1); - // Then - assertThat(found.get(0).amount()).isEqualTo(new BigDecimal("10000.77")); + // Then + assertThat(found.get(0).amount()).isEqualTo(new BigDecimal("10000.77")); - assertThat(found.get(0).type()).isEqualTo(CREDIT); - } + assertThat(found.get(0).type()).isEqualTo(CREDIT); + } - @Test - void givenTransactions_whenGetTransactions_thenReturnJsonArray() throws Exception { - // Given - given(transactionService.getTransactions(1)) - .willReturn( - List.of( - new Transaction(1, CREDIT, new BigDecimal("100.10")), - new Transaction(1, TransactionType.DEBIT, new BigDecimal("90.11")))); + @Test + void givenTransactions_whenGetTransactions_thenReturnJsonArray() throws Exception { + // Given + given(transactionService.getTransactions(1)) + .willReturn( + List.of( + new Transaction(1, CREDIT, new BigDecimal("100.10")), + new Transaction(1, TransactionType.DEBIT, new BigDecimal("90.11")))); - // When - mvc.perform( + // When + mvc.perform( get("/bank/api/v1/transactions?accountId=1") - .contentType(MediaType.APPLICATION_JSON)) - .andDo(print()) - // Then - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$", hasSize(2))) - .andExpect(jsonPath("$[0].amount", is(100.10))) - .andExpect(jsonPath("$[0].accountId", is(1))) - .andExpect(jsonPath("$[0].type", is("CREDIT"))) - // And second transaction - .andExpect(jsonPath("$[1].amount", is(90.11))) - .andExpect(jsonPath("$[1].accountId", is(1))) - .andExpect(jsonPath("$[1].type", is("DEBIT"))); - } + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + // Then + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[0].amount", is(100.10))) + .andExpect(jsonPath("$[0].accountId", is(1))) + .andExpect(jsonPath("$[0].type", is("CREDIT"))) + // And second transaction + .andExpect(jsonPath("$[1].amount", is(90.11))) + .andExpect(jsonPath("$[1].accountId", is(1))) + .andExpect(jsonPath("$[1].type", is("DEBIT"))); + } } diff --git a/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionRepositoryLayerTests.java b/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionRepositoryLayerTests.java index 007ead9..ef7418d 100644 --- a/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionRepositoryLayerTests.java +++ b/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionRepositoryLayerTests.java @@ -25,7 +25,7 @@ void whenFindByAccountId_thenReturnTransaction() { // When TransactionEntity found = - transactionRepository.findByAccountId(trx.getAccountId()).iterator().next(); + transactionRepository.findByAccountId(trx.getAccountId()).iterator().next(); // Then assertThat(found.getId()).isEqualTo(1); diff --git a/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceIntegrationTests.java b/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceIntegrationTests.java index bd21e39..b79c3f9 100644 --- a/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceIntegrationTests.java +++ b/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceIntegrationTests.java @@ -30,62 +30,65 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @SpringBootTest( - webEnvironment = RANDOM_PORT, - classes = TransactionServiceApplication.class) + webEnvironment = RANDOM_PORT, + classes = TransactionServiceApplication.class) @AutoConfigureMockMvc class TransactionServiceIntegrationTests { - @Autowired private MockMvc mvc; + @Autowired + private MockMvc mvc; - @Autowired private TransactionService transactionService; + @Autowired + private TransactionService transactionService; - @Autowired private TransactionRepository transactionRepository; + @Autowired + private TransactionRepository transactionRepository; - @AfterEach - public void resetDb() { - transactionRepository.deleteAll(); - } + @AfterEach + public void resetDb() { + transactionRepository.deleteAll(); + } - @Test - void whenValidInput_thenCreateTransaction() throws Exception { - // Given transaction - mvc.perform( + @Test + void whenValidInput_thenCreateTransaction() throws Exception { + // Given transaction + mvc.perform( post("/bank/api/v1/transactions") .contentType(MediaType.APPLICATION_JSON) .content(toJson(new TransactionDTO(1, new BigDecimal("10000.77"))))) - .andExpect(status().isOk()); + .andExpect(status().isOk()); - // When - List found = transactionService.getTransactions(1); + // When + List found = transactionService.getTransactions(1); - // Then - assertThat(found.get(0).amount()).isEqualTo(new BigDecimal("10000.77")); - assertThat(found.get(0).type()).isEqualTo(CREDIT); - } + // Then + assertThat(found.get(0).amount()).isEqualTo(new BigDecimal("10000.77")); + assertThat(found.get(0).type()).isEqualTo(CREDIT); + } - @Test - void givenTransactions_whenGetTransactions_thenReturnJsonArray() throws Exception { - // Given - TransactionEntity trx = new TransactionEntity(1, new BigDecimal("90.11")); - trx.setType(TransactionType.DEBIT); + @Test + void givenTransactions_whenGetTransactions_thenReturnJsonArray() throws Exception { + // Given + TransactionEntity trx = new TransactionEntity(1, new BigDecimal("90.11")); + trx.setType(TransactionType.DEBIT); - List.of(new TransactionEntity(1, new BigDecimal("100.10")), trx) - .forEach(entity -> transactionService.createTransaction(entity)); + List.of(new TransactionEntity(1, new BigDecimal("100.10")), trx) + .forEach(entity -> transactionService.createTransaction(entity)); - // When - mvc.perform( + // When + mvc.perform( get("/bank/api/v1/transactions?accountId=1").contentType(MediaType.APPLICATION_JSON)) - .andDo(print()) - // Then - .andExpect(status().isOk()) - .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$", hasSize(2))) - .andExpect(jsonPath("$[0].amount", is(100.10))) - .andExpect(jsonPath("$[0].accountId", is(1))) - .andExpect(jsonPath("$[0].type", is("CREDIT"))) - // And second transaction - .andExpect(jsonPath("$[1].amount", is(90.11))) - .andExpect(jsonPath("$[1].accountId", is(1))) - .andExpect(jsonPath("$[1].type", is("DEBIT"))); - } + .andDo(print()) + // Then + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[0].amount", is(100.10))) + .andExpect(jsonPath("$[0].accountId", is(1))) + .andExpect(jsonPath("$[0].type", is("CREDIT"))) + // And second transaction + .andExpect(jsonPath("$[1].amount", is(90.11))) + .andExpect(jsonPath("$[1].accountId", is(1))) + .andExpect(jsonPath("$[1].type", is("DEBIT"))); + } } diff --git a/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceLayerTests.java b/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceLayerTests.java index c7a4626..1893c8e 100644 --- a/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceLayerTests.java +++ b/bank-services/transaction-service/src/test/java/org/siriusxi/blueharvest/bank/ts/TransactionServiceLayerTests.java @@ -19,45 +19,45 @@ import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) -class TransactionServiceLayerTests { +abstract class TransactionServiceLayerTests { - @InjectMocks - private TransactionService transactionService; + @InjectMocks + private TransactionService transactionService; - @Mock - private TransactionRepository transactionRepository; + @Mock + private TransactionRepository transactionRepository; - @BeforeEach - void setUp() throws Exception { + @BeforeEach + void setUp() throws Exception { - MockitoAnnotations.openMocks(this).close(); - } + MockitoAnnotations.openMocks(this).close(); + } - @Test - void createTransactions(){ - TransactionEntity trx1 = new TransactionEntity(1, new BigDecimal("100.20")); + @Test + void createTransactions() { + TransactionEntity trx1 = new TransactionEntity(1, new BigDecimal("100.20")); - transactionService.createTransaction(trx1); - transactionService.createTransaction(trx1); + transactionService.createTransaction(trx1); + transactionService.createTransaction(trx1); - verify(transactionRepository, times(2)).save(trx1); + verify(transactionRepository, times(2)).save(trx1); - } + } - @Test - void whenAddNewTransactions_thenTransactionsShouldBeFound() { - //Given - when(transactionRepository.findByAccountId(1)) - .thenReturn( - List.of(new TransactionEntity(1, new BigDecimal("100.20")), - new TransactionEntity(1, new BigDecimal("200.20")))); + @Test + void whenAddNewTransactions_thenTransactionsShouldBeFound() { + //Given + when(transactionRepository.findByAccountId(1)) + .thenReturn( + List.of(new TransactionEntity(1, new BigDecimal("100.20")), + new TransactionEntity(1, new BigDecimal("200.20")))); - //when - List found = transactionService.getTransactions(1); + //when + List found = transactionService.getTransactions(1); - //Then - assertThat(found.size()).isEqualTo(2); - assertThat(found.get(0).amount()).isEqualTo(new BigDecimal("100.20")); - verify(transactionRepository, times(1)).findByAccountId(1); - } + //Then + assertThat(found.size()).isEqualTo(2); + assertThat(found.get(0).amount()).isEqualTo(new BigDecimal("100.20")); + verify(transactionRepository, times(1)).findByAccountId(1); + } } diff --git a/run-em-all.sh b/run-em-all.sh index 769e881..b4b6a12 100755 --- a/run-em-all.sh +++ b/run-em-all.sh @@ -6,7 +6,7 @@ echo -e "\nStarting [Harvest Bank] μServices ....\n\ ---------------------------------------\n" function runService(){ - java --enable-preview -jar $1/target/*.jar > /dev/null + java --enable-preview -jar "$1"/target/*.jar > /dev/null } for dir in $(find bank-services/*-service -maxdepth 0 -type d) diff --git a/test-em-all.sh b/test-em-all.sh index 2ed9b53..87ecae5 100755 --- a/test-em-all.sh +++ b/test-em-all.sh @@ -24,9 +24,9 @@ function assertCurl() { local httpCode="${result:(-3)}" RESPONSE='' && (( ${#result} > 3 )) && RESPONSE="${result%???}" - if [[ "$httpCode" = "$expectedHttpCode" ]] + if [[ "$httpCode" == "$expectedHttpCode" ]] then - if [[ "$httpCode" = "200" ]] + if [[ "$httpCode" == "200" ]] then echo "Test OK (HTTP Code: $httpCode)" else @@ -46,7 +46,7 @@ function assertEqual() { local expected=$1 local actual=$2 - if [[ "$actual" = "$expected" ]] + if [[ "$actual" == "$expected" ]] then echo "Test OK (actual value: $actual)" return 0