59 Commits

Author SHA1 Message Date
Chris Richardson
8ae6db7229 Merge branch 'master' into wip-eventuate-client-java
Conflicts:
	java-spring/monolithic-service/build.gradle
	java-spring/monolithic-service/src/main/java/net/chrisrichardson/eventstore/javaexamples/banking/web/BankingWebConfiguration.java
2016-09-06 21:51:17 -07:00
Chris Richardson
c03a5fed8d Merge pull request #36 from dartpopikyardo/wip-customer
UI updates, backend transaction history fixes
2016-09-06 20:49:04 -07:00
dartpopikyardo
ee45163f2c merged with shopcookeat/private-event-sourcing-examples wip-customer 2016-09-06 21:53:05 +03:00
Andrew Revinsky (DART)
e6bf638b4b Transfers history - correct statuses and calculation 2016-09-06 20:29:47 +03:00
dartpopikyardo
d6f60101db - updated AccountInfoUpdateService: fixed simultaneous upsert overrides 2016-09-06 19:06:40 +03:00
Andrew Revinsky (DART)
e54577d656 REST endpoints should return an JSON object, not an Array. #14 2016-09-06 02:46:19 +03:00
dartpopikyardo
bae00f6bd7 private-event-sourcing-examples-8 GatewayController ignores http status code returned by destination server
- added http status and headers to GatewayController's response
2016-09-05 22:53:56 +03:00
dartpopikyardo
68dca23a6b updated AccountInfoUpdateService: changed addTransaction saving-to-mongo logic 2016-09-05 22:28:19 +03:00
dartpopikyardo
19c9f88a7f wip issue #8 2016-09-05 15:17:28 +03:00
dartpopikyardo
9b6956b8df wip issue #8 2016-09-05 14:46:48 +03:00
dartpopikyardo
e2de325df2 wip issue #8 2016-09-05 14:31:14 +03:00
dartpopikyardo
c9fa916cdd fixed issue #22
fixed issue #35
2016-09-05 12:05:48 +03:00
dartpopikyardo
f79ebb4d18 Merge remote-tracking branch 'remotes/dartandrevinsky/wip-customer' into wip-customer 2016-09-02 23:53:11 +03:00
dartpopikyardo
8f2fc83a34 wip issue #22 - fixed api gateway controller's request mapping 2016-09-02 23:52:29 +03:00
Andrew Revinsky (DART)
05f2f309e7 Transaction history - step 1 2016-09-02 23:14:52 +03:00
dartpopikyardo
6cf774da2a wip issue #22 2016-09-02 15:39:03 +03:00
dartpopikyardo
3c2e9d374c wip issue #22 2016-09-02 15:32:55 +03:00
dartpopikyardo
55cb34ef4f fix issue #22 2016-09-02 15:11:44 +03:00
dartpopikyardo
dde554e442 reverted back AccountInfo and AccountInfoUpdateService changes 2016-09-02 11:53:50 +03:00
Chris Richardson
fd75779093 Merge pull request #33 from shopcookeat/revert-32-wip-customer
Revert "wip-customer small issues fixes"
2016-09-01 16:12:28 -07:00
Chris Richardson
c8291bec71 Revert "wip-customer small issues fixes" 2016-09-01 16:11:53 -07:00
Chris Richardson
1005c47d83 Merge pull request #32 from dartpopikyardo/wip-customer
wip-customer small issues fixes
2016-09-01 15:53:08 -07:00
dartpopikyardo
47e9053285 wip on issue #22 2016-09-02 01:45:30 +03:00
dartpopikyardo
f76912a6cf fix issue #13,
fix issue #14,
fix issue #15,
fix issue #17
2016-09-02 01:24:28 +03:00
dartpopikyardo
625ea6007e fix issue #7,
fix issue #8,
fix issue #9,
fix issue #11
2016-09-01 22:34:26 +03:00
Chris Richardson
6fae59fdeb Merge pull request #12 from dartandrevinsky/wip-customer
Changes to the app
2016-09-01 08:26:24 -07:00
dartpopikyardo
f846a32d95 fix issue #5,
fix issue #6
2016-09-01 16:36:48 +03:00
dartpopikyardo
2a712017f1 - updated {accountId}/history controller 2016-09-01 16:06:51 +03:00
dartpopikyardo
85613936f4 Merge remote-tracking branch 'remotes/dartandrevinsky/wip-customer' into wip-customer 2016-09-01 09:34:28 +03:00
dartpopikyardo
6f480ad11a fix issue #10 2016-09-01 09:10:03 +03:00
dartpopikyardo
2b0c405378 fixed tests 2016-09-01 08:08:30 +03:00
dartpopikyardo
a5d1e7312c Merge remote-tracking branch 'remotes/upstream/wip-eventuate-local' into wip-customer 2016-09-01 02:39:47 +03:00
Andrew Revinsky (DART)
1b53bd9147 Refresh after account creation 2016-08-31 19:40:09 +03:00
Andrew Revinsky (DART)
d1328e4ce8 Merge commit '45bda8e14d32cf016ac5dd105828f2cda073e7aa' into wip-customer
* commit '45bda8e14d32cf016ac5dd105828f2cda073e7aa':
  Fixed misc @Configuration issues and commented out incorrect version check that causing failure in AccountInfoUpdateService
  - fixed test config
  - disable vertx file cache
  -changed path to UI static content
  Merge remote-tracking branch 'remotes/upstream/wip-eventuate-local' into wip-customer
  Upgraded to Eventuate Local 0.2.0.RELEASE
  Bumped version
  Support for Eventuate Local
2016-08-31 11:57:34 +03:00
dartpopikyardo
45bda8e14d Merge last changes from 'remotes/upstream/wip-customer' into wip-customer 2016-08-31 10:06:05 +03:00
Andrew Revinsky (DART)
d9e13ff669 Improve the UI #2 2016-08-31 03:52:35 +03:00
Chris Richardson
e14787bce8 Fixed misc @Configuration issues and commented out incorrect version check that causing failure in AccountInfoUpdateService 2016-08-30 16:26:21 -07:00
dartpopikyardo
1d14ece9cf - fixed test config 2016-08-30 23:13:32 +03:00
dartpopikyardo
1e13d482a8 - disable vertx file cache 2016-08-30 22:37:55 +03:00
dartpopikyardo
2467099c3e -changed path to UI static content 2016-08-30 19:14:03 +03:00
dartpopikyardo
f4ec33d275 Merge remote-tracking branch 'remotes/upstream/wip-eventuate-local' into wip-customer 2016-08-30 19:07:57 +03:00
dartpopikyardo
a91ade08b1 Merge remote-tracking branch 'remotes/upstream/wip-eventuate-local' into wip-customer 2016-08-30 19:05:44 +03:00
Andrew Revinsky (DART)
fb97767a06 Improve the UI #2 2016-08-26 20:32:10 +03:00
Andrew Revinsky (DART)
1e7234166a Changes to the app 2016-08-26 14:13:50 +03:00
Chris Richardson
ef444bde9b Fixed API gateway misconfiguration 2016-08-25 14:48:30 -07:00
Chris Richardson
4b73b9beed Misc bug fixes 2016-08-25 14:21:44 -07:00
Chris Richardson
d0bdd51406 Fixed typo 2016-08-25 12:15:54 -07:00
Chris Richardson
b0b32d0a3f Merge pull request #22 from dartpopikyardo/wip-customer
refactored the tests
2016-08-25 12:14:35 -07:00
Main
e9517fe30f unified code format 2016-08-24 21:22:12 +03:00
Main
5f8475d675 - fixed the tests
- updated eventuate-client version
- fixed api-gateway and common module's dependencies
2016-08-10 23:39:00 +03:00
Main
12e46582cd Merge remote-tracking branch 'remotes/upstream/wip-customer' into wip-customer
# Conflicts:
#	java-spring/monolithic-service/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/web/BankingWebIntegrationTest.java
2016-08-09 22:59:59 +03:00
Main
b0855ebd81 - refactored the tests
- moved some dto classes into common module
2016-08-09 22:58:00 +03:00
Main
39309f23a1 - refactored the tests
- moved some dto classes into common module
2016-08-09 22:43:41 +03:00
Chris Richardson
e488df3d06 Fixed typo 2016-06-10 16:45:02 -07:00
Chris Richardson
4f11433390 Merge branch 'master' of github.com:cer/event-sourcing-examples 2016-06-10 16:43:32 -07:00
Chris Richardson
b573027fc2 Added Swagger to monolithic deployment, Added mongodb-cli.sh 2016-06-10 16:43:14 -07:00
Chris Richardson
3117f12402 Fixed typo 2016-06-06 20:48:58 -07:00
Chris Richardson
5b029d8307 Merge branch 'master' of github.com:cer/event-sourcing-examples 2016-05-31 15:08:46 -07:00
Chris Richardson
6d8376cfd5 Use specific Java 8 Docker image 2016-05-31 15:08:34 -07:00
181 changed files with 67670 additions and 2424 deletions

View File

@@ -1,7 +1,7 @@
#Event-Sourcing+CQRS example application #Event-Sourcing+CQRS example application
This example application is the money transfer application described in my talk [Building and deploying microservices with event sourcing, CQRS and Docker](http://plainoldobjects.com/presentations/building-and-deploying-microservices-with-event-sourcing-cqrs-and-docker/). This example application is the money transfer application described in my talk [Building and deploying microservices with event sourcing, CQRS and Docker](http://plainoldobjects.com/presentations/building-and-deploying-microservices-with-event-sourcing-cqrs-and-docker/).
This talk describe a way of architecting highly scalable and available applications that is based on microservices, polyglot persistence, This talk describes a way of architecting highly scalable and available applications that is based on microservices, polyglot persistence,
event sourcing (ES) and command query responsibility segregation (CQRS). event sourcing (ES) and command query responsibility segregation (CQRS).
Applications consist of loosely coupled components that communicate using events. Applications consist of loosely coupled components that communicate using events.
These components can be deployed either as separate services or packaged as a monolithic application for simplified development and testing. These components can be deployed either as separate services or packaged as a monolithic application for simplified development and testing.
@@ -94,9 +94,12 @@ First, you need to tell the query side code how to connect to MongoDB:
``` ```
[Docker Compose](https://docs.docker.com/compose/) is a great way to run MongoDB. [Docker Compose](https://docs.docker.com/compose/) is a great way to run MongoDB.
You can run the `docker-compose up -d mongodb` to run MongoDB. You can run the `docker-compose up -d mongodb` to run MongoDB and then set `SPRING_DATA_MONGODB_URI` as follows:
```
export SPRING_DATA_MONGODB_URI=mongodb://$(docker-machine ip default)/yourdb
```
Second, some of the tests in accounts-command-side-service, transactions-command-side-service, accounts-query-side-service and e2e-test need you need to set some environment variables that tell them how to connect to the Event Store server. Second, some of the tests in accounts-command-side-service, transactions-command-side-service, accounts-query-side-service and e2e-test require you to set some environment variables that tell them how to connect to the Event Store server.
But don't worry. But don't worry.
The build is configured to ignore failures for those projects. The build is configured to ignore failures for those projects.
@@ -117,6 +120,15 @@ Simply use this command:
java -jar monolithic-service/build/libs/monolithic-service.jar java -jar monolithic-service/build/libs/monolithic-service.jar
``` ```
This will start the service running on port 8080 (you can change using the --server.port=9999 option).
Once the service has started you can open the Swagger UI: http://localhost:8080/swagger-ui.html.
You can then:
1. Create two accounts (save the account ids)
2. Create a money transfer
3. View the updated account balances
## Running the microservices ## Running the microservices
The other option is to run the services separately. The other option is to run the services separately.

View File

@@ -7,19 +7,19 @@ This application consists of three microservices:
* Account Service - the command side business logic for Accounts * Account Service - the command side business logic for Accounts
* Money Transfer Service - the command side business logic for Money Transfers * Money Transfer Service - the command side business logic for Money Transfers
* Query service - query side implementation of a MongoDB-based, denormalized view of Accounts and MoneyTransfers * Query service - query side implementation of a MongoDB-based, denormalized view of Accounts and MoneyTransfers
The Account Service consists of the following modules: The Account Service consists of the following modules:
* accounts-command-side-backend - the Account aggregate * accounts-command-side-backend - the Account aggregate
* accounts-command-side-web - a REST API for creating and retrieving Accounts * accounts-command-side-web - a REST API for creating and retrieving Accounts
* accounts-command-side-service - a standalone microservice * accounts-command-side-service - a standalone microservice
The Money Transfer Service consists of the following modules: The Money Transfer Service consists of the following modules:
* transactions-command-side-backend - the MoneyTransfer aggregate * transactions-command-side-backend - the MoneyTransfer aggregate
* transactions-command-side-web - a REST API for creating and retrieving Money Transfers * transactions-command-side-web - a REST API for creating and retrieving Money Transfers
* transactions-command-side-service - a standalone microservice * transactions-command-side-service - a standalone microservice
The Query Service consists the following modules: The Query Service consists the following modules:
* accounts-query-side-backend - MongoDB-based, denormalized view of Accounts and MoneyTransfers * accounts-query-side-backend - MongoDB-based, denormalized view of Accounts and MoneyTransfers
@@ -28,10 +28,8 @@ The Query Service consists the following modules:
# Deploying the application # Deploying the application
These services can be deployed either as either separate standalone services using the Event Store server, or they can be deployed as a monolithic application for simpified integration testing. These services can be deployed either as either separate standalone services using the Event Store server, or they can be deployed as a monolithic application for simplified integration testing.
The three services can also be packaged as a single monolithic web application in order to be used with the embedded Event Store: The three services can also be packaged as a single monolithic web application in order to be used with the embedded Event Store:
* monolithic-service - all-in-one, monolithic packaging of the application * monolithic-service - all-in-one, monolithic packaging of the application

View File

@@ -11,8 +11,8 @@ import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transa
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@EventSubscriber(id="accountEventHandlers") @EventSubscriber(id = "accountEventHandlers")
public class AccountWorkflow { public class AccountWorkflow {
@EventHandlerMethod @EventHandlerMethod
public CompletableFuture<?> debitAccount(EventHandlerContext<MoneyTransferCreatedEvent> ctx) { public CompletableFuture<?> debitAccount(EventHandlerContext<MoneyTransferCreatedEvent> ctx) {
@@ -22,7 +22,13 @@ public class AccountWorkflow {
String fromAccountId = event.getDetails().getFromAccountId(); String fromAccountId = event.getDetails().getFromAccountId();
return ctx.update(Account.class, fromAccountId, new DebitAccountCommand(amount, transactionId)); return ctx.update(Account.class, fromAccountId, new DebitAccountCommand(amount, transactionId)).handle((x, e) -> {
if (e != null) {
e.printStackTrace();
}
return x;
}
);
} }
@EventHandlerMethod @EventHandlerMethod
@@ -32,7 +38,13 @@ public class AccountWorkflow {
String fromAccountId = event.getDetails().getToAccountId(); String fromAccountId = event.getDetails().getToAccountId();
String transactionId = ctx.getEntityId(); String transactionId = ctx.getEntityId();
return ctx.update(Account.class, fromAccountId, new CreditAccountCommand(amount, transactionId)); return ctx.update(Account.class, fromAccountId, new CreditAccountCommand(amount, transactionId)).handle((x, e) -> {
if (e != null) {
e.printStackTrace();
}
return x;
}
);
} }
} }

View File

@@ -1,7 +1,7 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web; package net.chrisrichardson.eventstore.javaexamples.banking.web;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountRequest; import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountResponse; import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.CreateAccountResponse;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -25,7 +25,7 @@ public class AccountsCommandSideServiceIntegrationTest {
private int port; private int port;
private String baseUrl(String path) { private String baseUrl(String path) {
return "http://localhost:" + port + "/" + path; return "http://localhost:" + port + "/api" + path;
} }
@Autowired @Autowired

View File

@@ -1,6 +1,8 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.AccountService; import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.AccountService;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.CreateAccountResponse;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
@@ -11,7 +13,7 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@RestController @RestController
@RequestMapping("/accounts") @RequestMapping("/api/accounts")
public class AccountController { public class AccountController {
private AccountService accountService; private AccountService accountService;

View File

@@ -35,7 +35,7 @@ public class AccountControllerIntegrationTest {
@Test @Test
public void shouldCreateAccount() throws Exception { public void shouldCreateAccount() throws Exception {
mockMvc.perform(post("/accounts") mockMvc.perform(post("/api/accounts")
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content("{\"customerId\" : \"00000000-00000000\", \"initialBalance\" : 500}") .content("{\"customerId\" : \"00000000-00000000\", \"initialBalance\" : 500}")
.accept(MediaType.APPLICATION_JSON)) .accept(MediaType.APPLICATION_JSON))
@@ -44,7 +44,7 @@ public class AccountControllerIntegrationTest {
@Test @Test
public void shouldRejectBadRequest() throws Exception { public void shouldRejectBadRequest() throws Exception {
mockMvc.perform(post("/accounts") mockMvc.perform(post("/api/accounts")
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content("{\"initialBalanceXXX\" : 500}") .content("{\"initialBalanceXXX\" : 500}")
.accept(MediaType.APPLICATION_JSON)) .accept(MediaType.APPLICATION_JSON))

View File

@@ -1,31 +0,0 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
public class AccountChangeInfo {
private String changeId;
private String transactionId;
private String transactionType;
private long amount;
private long balanceDelta;
public AccountChangeInfo(String changeId, String transactionId, String transactionType, long amount, long balanceDelta) {
this.changeId = changeId;
this.transactionId = transactionId;
this.transactionType = transactionType;
this.amount = amount;
this.balanceDelta = balanceDelta;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -1,9 +1,10 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import java.util.ArrayList; import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountChangeInfo;
import java.util.Collections; import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountTransactionInfo;
import java.util.List; import net.chrisrichardson.eventstore.javaexamples.banking.common.transactions.TransferState;
import java.util.Map;
import java.util.*;
/** /**
* Created by cer on 11/21/14. * Created by cer on 11/21/14.
@@ -17,12 +18,18 @@ public class AccountInfo {
private long balance; private long balance;
private List<AccountChangeInfo> changes; private List<AccountChangeInfo> changes;
private Map<String, AccountTransactionInfo> transactions; private Map<String, AccountTransactionInfo> transactions;
private Map<String, TransferState> transferStates;
private String version; private String version;
private Date date;
private AccountInfo() { private AccountInfo() {
} }
public AccountInfo(String id, String customerId, String title, String description, long balance, List<AccountChangeInfo> changes, Map<String, AccountTransactionInfo> transactions, String version) { public AccountInfo(String id, String customerId, String title, String description, long balance, List<AccountChangeInfo> changes, Map<String, AccountTransactionInfo> transactions, String version) {
this(id, customerId, title, description, balance, changes, transactions, version, new Date());
}
public AccountInfo(String id, String customerId, String title, String description, long balance, List<AccountChangeInfo> changes, Map<String, AccountTransactionInfo> transactions, String version, Date date) {
this.id = id; this.id = id;
this.customerId = customerId; this.customerId = customerId;
@@ -32,6 +39,7 @@ public class AccountInfo {
this.changes = changes; this.changes = changes;
this.transactions = transactions; this.transactions = transactions;
this.version = version; this.version = version;
this.date = date;
} }
public String getId() { public String getId() {
@@ -65,4 +73,16 @@ public class AccountInfo {
public String getVersion() { public String getVersion() {
return version; return version;
} }
public Date getDate() {
return date;
}
public Map<String, TransferState> getTransferStates() {
return transferStates;
}
public void setTransferStates(Map<String, TransferState> transferStates) {
this.transferStates = transferStates;
}
} }

View File

@@ -1,6 +1,9 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import com.mongodb.WriteResult; import com.mongodb.WriteResult;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountChangeInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountTransactionInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.transactions.TransferState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException; import org.springframework.dao.DuplicateKeyException;
@@ -9,30 +12,35 @@ import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update; import org.springframework.data.mongodb.core.query.Update;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Collections; import java.util.Date;
import static net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.MoneyUtil.toIntegerRepr; import static net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.MoneyUtil.toIntegerRepr;
import static org.springframework.data.mongodb.core.query.Criteria.where; import static org.springframework.data.mongodb.core.query.Criteria.where;
public class AccountInfoUpdateService { public class AccountInfoUpdateService {
private Logger logger = LoggerFactory.getLogger(getClass()); private Logger logger = LoggerFactory.getLogger(getClass());
private AccountInfoRepository accountInfoRepository;
private MongoTemplate mongoTemplate; private MongoTemplate mongoTemplate;
public AccountInfoUpdateService(MongoTemplate mongoTemplate) { public AccountInfoUpdateService(AccountInfoRepository accountInfoRepository, MongoTemplate mongoTemplate) {
this.accountInfoRepository = accountInfoRepository;
this.mongoTemplate = mongoTemplate; this.mongoTemplate = mongoTemplate;
} }
public void create(String accountId, String customerId, String title, BigDecimal initialBalance, String description, String version) {
public void create(String accountId, String customerId, String title, BigDecimal initialBalance, String description, String version) {
try { try {
AccountChangeInfo ci = new AccountChangeInfo();
ci.setAmount(toIntegerRepr(initialBalance));
WriteResult x = mongoTemplate.upsert(new Query(where("id").is(accountId).and("version").exists(false)), WriteResult x = mongoTemplate.upsert(new Query(where("id").is(accountId).and("version").exists(false)),
new Update() new Update()
.set("customerId", customerId) .set("customerId", customerId)
.set("title", title) .set("title", title)
.set("description", description) .set("description", description)
.set("balance", toIntegerRepr(initialBalance)) .set("balance", toIntegerRepr(initialBalance))
.push("changes", ci)
.set("date", new Date())
.set("version", version), .set("version", version),
AccountInfo.class); AccountInfo.class);
logger.info("Saved in mongo"); logger.info("Saved in mongo");
@@ -46,10 +54,10 @@ public class AccountInfoUpdateService {
} }
public void addTransaction(String eventId, String accountId, AccountTransactionInfo ti) { public void addTransaction(String accountId, AccountTransactionInfo ti) {
mongoTemplate.upsert(new Query(where("id").is(accountId)), mongoTemplate.upsert(new Query(where("id").is(accountId)),
new Update(). new Update().
set("transactions." + eventId, ti), set("transactions." + ti.getTransactionId(), ti),
AccountInfo.class); AccountInfo.class);
} }
@@ -63,5 +71,10 @@ public class AccountInfoUpdateService {
AccountInfo.class); AccountInfo.class);
} }
public void updateTransactionStatus(String accountId, String transactionId, TransferState status) {
mongoTemplate.upsert(new Query(where("id").is(accountId)),
new Update().
set("transferStates." + transactionId, status),
AccountInfo.class);
}
} }

View File

@@ -1,9 +1,6 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import io.eventuate.CompletableFutureUtil;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
public class AccountQueryService { public class AccountQueryService {
@@ -13,15 +10,17 @@ public class AccountQueryService {
this.accountInfoRepository = accountInfoRepository; this.accountInfoRepository = accountInfoRepository;
} }
public CompletableFuture<AccountInfo> findByAccountId(String accountId) { public AccountInfo findByAccountId(String accountId) {
AccountInfo account = accountInfoRepository.findOne(accountId); AccountInfo account = accountInfoRepository.findOne(accountId);
if (account == null) if (account == null)
return CompletableFutureUtil.failedFuture(new AccountNotFoundException(accountId)); throw new AccountNotFoundException(accountId);
else else
return CompletableFuture.completedFuture(account); if(account.getTransferStates()!=null)
account.getTransactions().stream().forEach(ati -> ati.setStatus(account.getTransferStates().get(ati.getTransactionId())));
return account;
} }
public CompletableFuture<List<AccountInfo>> findByCustomerId(String customerId) { public List<AccountInfo> findByCustomerId(String customerId) {
return CompletableFuture.completedFuture(accountInfoRepository.findByCustomerId(customerId)); return accountInfoRepository.findByCustomerId(customerId);
} }
} }

View File

@@ -3,11 +3,14 @@ package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.ac
import io.eventuate.DispatchedEvent; import io.eventuate.DispatchedEvent;
import io.eventuate.EventHandlerMethod; import io.eventuate.EventHandlerMethod;
import io.eventuate.EventSubscriber; import io.eventuate.EventSubscriber;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountChangedEvent; import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.*;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountCreditedEvent; import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.CreditRecordedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountDebitedEvent; import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.DebitRecordedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountOpenedEvent; import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.FailedDebitRecordedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.MoneyTransferCreatedEvent; import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.MoneyTransferCreatedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountChangeInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountTransactionInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.transactions.TransferState;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -56,20 +59,36 @@ public class AccountQueryWorkflow {
de.getEvent().getDetails().getDate(), de.getEvent().getDetails().getDate(),
de.getEvent().getDetails().getDescription()); de.getEvent().getDetails().getDescription());
accountInfoUpdateService.addTransaction(eventId, fromAccountId, ti); accountInfoUpdateService.addTransaction(fromAccountId, ti);
accountInfoUpdateService.addTransaction(eventId, toAccountId, ti); accountInfoUpdateService.addTransaction(toAccountId, ti);
} }
@EventHandlerMethod @EventHandlerMethod
public void recordDebit(DispatchedEvent<AccountDebitedEvent> de) { public void recordDebit(DispatchedEvent<AccountDebitedEvent> de) {
String accountId = de.getEntityId();
String transactionId = de.getEvent().getTransactionId();
accountInfoUpdateService.updateTransactionStatus(accountId, transactionId, TransferState.DEBITED);
saveChange(de, -1); saveChange(de, -1);
} }
@EventHandlerMethod @EventHandlerMethod
public void recordCredit(DispatchedEvent<AccountCreditedEvent> de) { public void recordCredit(DispatchedEvent<AccountCreditedEvent> de) {
String accountId = de.getEntityId();
String transactionId = de.getEvent().getTransactionId();
accountInfoUpdateService.updateTransactionStatus(accountId, transactionId, TransferState.COMPLETED);
saveChange(de, +1); saveChange(de, +1);
} }
@EventHandlerMethod
public void recordFailed(DispatchedEvent<AccountDebitFailedDueToInsufficientFundsEvent> de) {
String accountId = de.getEntityId();
String transactionId = de.getEvent().getTransactionId();
accountInfoUpdateService.updateTransactionStatus(accountId, transactionId, TransferState.FAILED_DUE_TO_INSUFFICIENT_FUNDS);
}
public <T extends AccountChangedEvent> void saveChange(DispatchedEvent<T> de, int delta) { public <T extends AccountChangedEvent> void saveChange(DispatchedEvent<T> de, int delta) {
String changeId = de.getEventId().asString(); String changeId = de.getEventId().asString();
String transactionId = de.getEvent().getTransactionId(); String transactionId = de.getEvent().getTransactionId();

View File

@@ -18,8 +18,8 @@ public class QuerySideAccountConfiguration {
} }
@Bean @Bean
public AccountInfoUpdateService accountInfoUpdateService(MongoTemplate mongoTemplate) { public AccountInfoUpdateService accountInfoUpdateService(AccountInfoRepository accountInfoRepository, MongoTemplate mongoTemplate) {
return new AccountInfoUpdateService(mongoTemplate); return new AccountInfoUpdateService(accountInfoRepository, mongoTemplate);
} }
@Bean @Bean
@@ -27,8 +27,6 @@ public class QuerySideAccountConfiguration {
return new AccountQueryService(accountInfoRepository); return new AccountQueryService(accountInfoRepository);
} }
@Bean @Bean
public QuerySideDependencyChecker querysideDependencyChecker(MongoTemplate mongoTemplate) { public QuerySideDependencyChecker querysideDependencyChecker(MongoTemplate mongoTemplate) {
return new QuerySideDependencyChecker(mongoTemplate); return new QuerySideDependencyChecker(mongoTemplate);

View File

@@ -4,6 +4,9 @@ import io.eventuate.javaclient.spring.jdbc.EventuateJdbcEventStoreConfiguration;
import io.eventuate.javaclient.spring.jdbc.IdGenerator; import io.eventuate.javaclient.spring.jdbc.IdGenerator;
import io.eventuate.javaclient.spring.jdbc.IdGeneratorImpl; import io.eventuate.javaclient.spring.jdbc.IdGeneratorImpl;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountCreditedEvent; import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountCreditedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountChangeInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountTransactionInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.transactions.TransferState;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -13,11 +16,9 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@@ -53,14 +54,14 @@ public class AccountInfoUpdateServiceTest {
accountInfoUpdateService.create(accountId, customerId, title, initialBalance, description, version); accountInfoUpdateService.create(accountId, customerId, title, initialBalance, description, version);
AccountInfo accountInfo = accountQueryService.findByAccountId(accountId).get(); AccountInfo accountInfo = accountQueryService.findByAccountId(accountId);
assertEquals(accountId, accountInfo.getId()); assertEquals(accountId, accountInfo.getId());
assertEquals(customerId, accountInfo.getCustomerId()); assertEquals(customerId, accountInfo.getCustomerId());
assertEquals(title, accountInfo.getTitle()); assertEquals(title, accountInfo.getTitle());
assertEquals(description, accountInfo.getDescription()); assertEquals(description, accountInfo.getDescription());
assertEquals(initialBalance.longValue() * 100, accountInfo.getBalance()); assertEquals(initialBalance.longValue() * 100, accountInfo.getBalance());
assertTrue(accountInfo.getChanges().isEmpty()); assertEquals(1, accountInfo.getChanges().size());
assertTrue(accountInfo.getTransactions().isEmpty()); assertTrue(accountInfo.getTransactions().isEmpty());
assertEquals(version, accountInfo.getVersion()); assertEquals(version, accountInfo.getVersion());
@@ -75,19 +76,19 @@ public class AccountInfoUpdateServiceTest {
accountInfoUpdateService.updateBalance(accountId, changeId, 500, accountInfoUpdateService.updateBalance(accountId, changeId, 500,
change); change);
accountInfo = accountQueryService.findByAccountId(accountId).get(); accountInfo = accountQueryService.findByAccountId(accountId);
assertEquals(initialBalance.add(new BigDecimal(5)).longValue() * 100, accountInfo.getBalance()); assertEquals(initialBalance.add(new BigDecimal(5)).longValue() * 100, accountInfo.getBalance());
assertFalse(accountInfo.getChanges().isEmpty()); assertFalse(accountInfo.getChanges().isEmpty());
assertEquals(change, accountInfo.getChanges().get(0)); assertEquals(change, accountInfo.getChanges().get(1));
String eventId = x.genId().asString(); String eventId = x.genId().asString();
AccountTransactionInfo ti = new AccountTransactionInfo(transactionId, accountId, accountId, 5, new Date(), "A transfer"); AccountTransactionInfo ti = new AccountTransactionInfo(transactionId, accountId, accountId, 5, new Date(), "A transfer");
accountInfoUpdateService.addTransaction(eventId, accountId, ti); accountInfoUpdateService.addTransaction(accountId, ti);
accountInfo = accountQueryService.findByAccountId(accountId).get(); accountInfo = accountQueryService.findByAccountId(accountId);
assertFalse(accountInfo.getTransactions().isEmpty()); assertFalse(accountInfo.getTransactions().isEmpty());
assertEquals(ti, accountInfo.getTransactions().get(0)); assertEquals(ti, accountInfo.getTransactions().get(0));
@@ -106,7 +107,43 @@ public class AccountInfoUpdateServiceTest {
accountInfoUpdateService.create(accountId, customerId, title, initialBalance, description, version); accountInfoUpdateService.create(accountId, customerId, title, initialBalance, description, version);
accountInfoUpdateService.create(accountId, customerId, title, initialBalance, description, version); accountInfoUpdateService.create(accountId, customerId, title, initialBalance, description, version);
}
@Test
public void shouldUpdateTransactionStatus() {
IdGenerator x = new IdGeneratorImpl();
String accountId = x.genId().asString();
String customerId = x.genId().asString();
String version = x.genId().asString();
String title = "Checking account";
BigDecimal initialBalance = new BigDecimal("1345");
String description = "Some account";
accountInfoUpdateService.create(accountId, customerId, title, initialBalance, description, version);
String transactionId = x.genId().asString();
AccountTransactionInfo transactionInfo = new AccountTransactionInfo();
transactionInfo.setTransactionId(transactionId);
transactionInfo.setStatus(TransferState.INITIAL);
accountInfoUpdateService.addTransaction(accountId, transactionInfo);
AccountInfo accountInfo = accountQueryService.findByAccountId(accountId);
assertEquals(accountId, accountInfo.getId());
assertFalse(accountInfo.getTransactions().isEmpty());
assertEquals(1, accountInfo.getTransactions().size());
assertEquals(TransferState.INITIAL, accountInfo.getTransactions().get(0).getStatus());
accountInfoUpdateService.updateTransactionStatus(accountId, transactionId, TransferState.COMPLETED);
accountInfo = accountQueryService.findByAccountId(accountId);
assertEquals(accountId, accountInfo.getId());
assertFalse(accountInfo.getTransactions().isEmpty());
assertEquals(1, accountInfo.getTransactions().size());
assertEquals(TransferState.COMPLETED, accountInfo.getTransactions().get(0).getStatus());
} }
} }

View File

@@ -1,6 +1,6 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web; package net.chrisrichardson.eventstore.javaexamples.banking.web;
import net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.accounts.GetAccountResponse; import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.GetAccountResponse;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -30,7 +30,7 @@ public class AccountsQuerySideServiceIntegrationTest {
private int port; private int port;
private String baseUrl(String path) { private String baseUrl(String path) {
return "http://localhost:" + port + "/" + path; return "http://localhost:" + port + "/api" + path;
} }
@Autowired @Autowired

View File

@@ -4,17 +4,19 @@ import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.acc
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountNotFoundException; import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountNotFoundException;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountQueryService; import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountQueryService;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountTransactionInfo; import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@RestController @RestController
@RequestMapping("/api")
public class AccountQueryController { public class AccountQueryController {
private AccountQueryService accountInfoQueryService; private AccountQueryService accountInfoQueryService;
@@ -25,21 +27,35 @@ public class AccountQueryController {
} }
@RequestMapping(value = "/accounts/{accountId}", method = RequestMethod.GET) @RequestMapping(value = "/accounts/{accountId}", method = RequestMethod.GET)
public CompletableFuture<GetAccountResponse> get(@PathVariable String accountId) { public ResponseEntity<GetAccountResponse> get(@PathVariable String accountId) {
return accountInfoQueryService.findByAccountId(accountId) AccountInfo accountInfo = accountInfoQueryService.findByAccountId(accountId);
.thenApply(accountInfo -> new GetAccountResponse(accountInfo.getId(), new BigDecimal(accountInfo.getBalance()), accountInfo.getTitle(), accountInfo.getDescription())); return ResponseEntity.ok().body(new GetAccountResponse(accountInfo.getId(), new BigDecimal(accountInfo.getBalance()), accountInfo.getTitle(), accountInfo.getDescription()));
} }
@RequestMapping(value = "/accounts", method = RequestMethod.GET) @RequestMapping(value = "/customers/{customerId}/accounts", method = RequestMethod.GET)
public CompletableFuture<List<GetAccountResponse>> getAccountsForCustomer(@RequestParam("customerId") String customerId) { public ResponseEntity<GetAccountsResponse> getAccountsForCustomer(@PathVariable("customerId") String customerId) {
return accountInfoQueryService.findByCustomerId(customerId) return ResponseEntity.ok().body(
.thenApply(accountInfoList -> accountInfoList.stream().map(accountInfo -> new GetAccountResponse(accountInfo.getId(), new BigDecimal(accountInfo.getBalance()), accountInfo.getTitle(), accountInfo.getDescription())).collect(Collectors.toList())); new GetAccountsResponse(
accountInfoQueryService.findByCustomerId(customerId)
.stream()
.map(accountInfo -> new GetAccountResponse(
accountInfo.getId(),
new BigDecimal(accountInfo.getBalance()),
accountInfo.getTitle(),
accountInfo.getDescription()))
.collect(Collectors.toList())
)
);
} }
@RequestMapping(value = "/accounts/{accountId}/history", method = RequestMethod.GET) @RequestMapping(value = "/accounts/{accountId}/history", method = RequestMethod.GET)
public CompletableFuture<List<AccountTransactionInfo>> getTransactionsHistory(@PathVariable String accountId) { public ResponseEntity<AccountHistoryResponse> getTransactionsHistory(@PathVariable String accountId) {
return accountInfoQueryService.findByAccountId(accountId) AccountInfo accountInfo = accountInfoQueryService.findByAccountId(accountId);
.thenApply(AccountInfo::getTransactions); List<AccountHistoryEntry> historyEntries = new ArrayList<>();
historyEntries.add(new AccountOpenInfo(accountInfo.getDate(), AccountHistoryEntry.EntryType.account, accountInfo.getChanges().get(0).getAmount()));
accountInfo.getTransactions().forEach(historyEntries::add);
return ResponseEntity.ok().body(new AccountHistoryResponse(historyEntries));
} }
@ResponseStatus(value= HttpStatus.NOT_FOUND, reason="account not found") @ResponseStatus(value= HttpStatus.NOT_FOUND, reason="account not found")

View File

@@ -1,53 +0,0 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.accounts;
import java.math.BigDecimal;
public class GetAccountResponse {
private String accountId;
private BigDecimal balance;
private String title;
private String description;
public GetAccountResponse() {
}
public GetAccountResponse(String accountId, BigDecimal balance, String title, String description) {
this.accountId = accountId;
this.balance = balance;
this.title = title;
this.description = description;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public String getAccountId() {
return accountId;
}
public BigDecimal getBalance() {
return balance;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@@ -15,7 +15,7 @@ dependencies {
} }
task copyWebStatic(type: Copy) { task copyWebStatic(type: Copy) {
from "../../prebuilt-web-client" from "../../js-frontend/build"
into "build/resources/main/static" into "build/resources/main/static"
} }

View File

@@ -11,50 +11,50 @@ import java.util.List;
@ConfigurationProperties(prefix = "api.gateway") @ConfigurationProperties(prefix = "api.gateway")
public class ApiGatewayProperties { public class ApiGatewayProperties {
private List<Endpoint> endpoints; private List<Endpoint> endpoints;
public static class Endpoint { public static class Endpoint {
private String path; private String path;
private RequestMethod method; private RequestMethod method;
private String location; private String location;
public Endpoint() { public Endpoint() {
}
public Endpoint(String location) {
this.location = location;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public RequestMethod getMethod() {
return method;
}
public void setMethod(RequestMethod method) {
this.method = method;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
} }
public List<Endpoint> getEndpoints() { public Endpoint(String location) {
return endpoints; this.location = location;
} }
public void setEndpoints(List<Endpoint> endpoints) { public String getPath() {
this.endpoints = endpoints; return path;
} }
public void setPath(String path) {
this.path = path;
}
public RequestMethod getMethod() {
return method;
}
public void setMethod(RequestMethod method) {
this.method = method;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
public List<Endpoint> getEndpoints() {
return endpoints;
}
public void setEndpoints(List<Endpoint> endpoints) {
this.endpoints = endpoints;
}
} }

View File

@@ -1,7 +1,6 @@
package net.chrisrichardson.eventstore.javaexamples.banking.apigateway; package net.chrisrichardson.eventstore.javaexamples.banking.apigateway;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.eventuate.javaclient.spring.httpstomp.EventuateHttpStompClientConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.AuthConfiguration; import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.AuthConfiguration;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
@@ -27,24 +26,24 @@ import java.util.Collections;
@Configuration @Configuration
@ComponentScan @ComponentScan
@EnableAutoConfiguration @EnableAutoConfiguration
@Import({EventuateHttpStompClientConfiguration.class, AuthConfiguration.class}) @Import({AuthConfiguration.class})
@EnableConfigurationProperties({ApiGatewayProperties.class}) @EnableConfigurationProperties({ApiGatewayProperties.class})
public class ApiGatewayServiceConfiguration extends WebMvcConfigurerAdapter { public class ApiGatewayServiceConfiguration extends WebMvcConfigurerAdapter {
@Bean @Bean
public RestTemplate restTemplate(HttpMessageConverters converters) { public RestTemplate restTemplate(HttpMessageConverters converters) {
// we have to define Apache HTTP client to use the PATCH verb // we have to define Apache HTTP client to use the PATCH verb
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/json")); converter.setSupportedMediaTypes(MediaType.parseMediaTypes("application/json"));
converter.setObjectMapper(new ObjectMapper()); converter.setObjectMapper(new ObjectMapper());
HttpClient httpClient = HttpClients.createDefault(); HttpClient httpClient = HttpClients.createDefault();
RestTemplate restTemplate = new RestTemplate(Collections.<HttpMessageConverter<?>>singletonList(converter)); RestTemplate restTemplate = new RestTemplate(Collections.<HttpMessageConverter<?>>singletonList(converter));
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient)); restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
restTemplate.setErrorHandler(new RestTemplateErrorHandler()); restTemplate.setErrorHandler(new RestTemplateErrorHandler());
return restTemplate; return restTemplate;
} }
} }

View File

@@ -10,15 +10,15 @@ import java.io.IOException;
public class RestTemplateErrorHandler implements ResponseErrorHandler { public class RestTemplateErrorHandler implements ResponseErrorHandler {
private static final Logger log = LoggerFactory.getLogger(RestTemplateErrorHandler.class); private static final Logger log = LoggerFactory.getLogger(RestTemplateErrorHandler.class);
@Override @Override
public void handleError(ClientHttpResponse response) throws IOException { public void handleError(ClientHttpResponse response) throws IOException {
log.error("Response error: {} {}", response.getStatusCode(), response.getStatusText()); log.error("Response error: {} {}", response.getStatusCode(), response.getStatusText());
} }
@Override @Override
public boolean hasError(ClientHttpResponse response) throws IOException { public boolean hasError(ClientHttpResponse response) throws IOException {
return RestUtil.isError(response.getStatusCode()); return RestUtil.isError(response.getStatusCode());
} }
} }

View File

@@ -7,9 +7,9 @@ import org.springframework.http.HttpStatus;
*/ */
public class RestUtil { public class RestUtil {
public static boolean isError(HttpStatus status) { public static boolean isError(HttpStatus status) {
HttpStatus.Series series = status.series(); HttpStatus.Series series = status.series();
return (HttpStatus.Series.CLIENT_ERROR.equals(series) return (HttpStatus.Series.CLIENT_ERROR.equals(series)
|| HttpStatus.Series.SERVER_ERROR.equals(series)); || HttpStatus.Series.SERVER_ERROR.equals(series));
} }
} }

View File

@@ -4,6 +4,7 @@ import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.ApiGateway
import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils.ContentRequestTransformer; import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils.ContentRequestTransformer;
import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils.HeadersRequestTransformer; import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils.HeadersRequestTransformer;
import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils.URLRequestTransformer; import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils.URLRequestTransformer;
import org.apache.http.Header;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.HttpUriRequest;
@@ -12,7 +13,12 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
@@ -24,8 +30,10 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.springframework.web.bind.annotation.RequestMethod.*; import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
/** /**
* Created by popikyardo on 15.01.16. * Created by popikyardo on 15.01.16.
@@ -33,43 +41,51 @@ import static org.springframework.web.bind.annotation.RequestMethod.*;
@RestController @RestController
public class GatewayController { public class GatewayController {
Logger log = LoggerFactory.getLogger(this.getClass()); Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired @Autowired
private ApiGatewayProperties apiGatewayProperties; private ApiGatewayProperties apiGatewayProperties;
private HttpClient httpClient; private HttpClient httpClient;
@PostConstruct @PostConstruct
public void init() { public void init() {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
httpClient = HttpClients.custom() httpClient = HttpClients.custom()
.setConnectionManager(cm) .setConnectionManager(cm)
.build(); .build();
} }
@RequestMapping(value = "/**", method = {GET, POST}) @RequestMapping(value = "/api/**", method = {GET, POST})
public String proxyRequest(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, IOException, URISyntaxException { @ResponseBody
HttpUriRequest proxiedRequest = createHttpUriRequest(request); public ResponseEntity<String> proxyRequest(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, IOException, URISyntaxException {
log.info("request: {}", proxiedRequest); HttpUriRequest proxiedRequest = createHttpUriRequest(request);
HttpResponse proxiedResponse = httpClient.execute(proxiedRequest); logger.info("request: {}", proxiedRequest);
return read(proxiedResponse.getEntity().getContent()); HttpResponse proxiedResponse = httpClient.execute(proxiedRequest);
} logger.info("Response {}", proxiedResponse.getStatusLine().getStatusCode());
return new ResponseEntity<>(read(proxiedResponse.getEntity().getContent()), processHeaders(proxiedResponse.getAllHeaders()), HttpStatus.valueOf(proxiedResponse.getStatusLine().getStatusCode()));
private HttpUriRequest createHttpUriRequest(HttpServletRequest request) throws URISyntaxException, NoSuchRequestHandlingMethodException, IOException { }
URLRequestTransformer urlRequestTransformer = new URLRequestTransformer(apiGatewayProperties);
ContentRequestTransformer contentRequestTransformer = new ContentRequestTransformer(); private HttpHeaders processHeaders(Header[] headers) {
HeadersRequestTransformer headersRequestTransformer = new HeadersRequestTransformer(); HttpHeaders result = new HttpHeaders();
headersRequestTransformer.setPredecessor(contentRequestTransformer); Stream.of(headers).filter(h -> h.getName().equalsIgnoreCase("Content-Type")).forEach( h -> result.set(h.getName(), h.getValue()));
contentRequestTransformer.setPredecessor(urlRequestTransformer); return result;
}
return headersRequestTransformer.transform(request).build();
} private HttpUriRequest createHttpUriRequest(HttpServletRequest request) throws URISyntaxException, NoSuchRequestHandlingMethodException, IOException {
URLRequestTransformer urlRequestTransformer = new URLRequestTransformer(apiGatewayProperties);
private String read(InputStream input) throws IOException { ContentRequestTransformer contentRequestTransformer = new ContentRequestTransformer();
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) { HeadersRequestTransformer headersRequestTransformer = new HeadersRequestTransformer();
return buffer.lines().collect(Collectors.joining("\n")); headersRequestTransformer.setPredecessor(contentRequestTransformer);
} contentRequestTransformer.setPredecessor(urlRequestTransformer);
return headersRequestTransformer.transform(request).build();
}
private String read(InputStream input) throws IOException {
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) {
return buffer.lines().collect(Collectors.joining("\n"));
} }
}
} }

View File

@@ -7,7 +7,7 @@ import org.springframework.boot.SpringApplication;
* Created by Main on 19.01.2016. * Created by Main on 19.01.2016.
*/ */
public class ApiGatewayServiceMain { public class ApiGatewayServiceMain {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(ApiGatewayServiceConfiguration.class, args); SpringApplication.run(ApiGatewayServiceConfiguration.class, args);
} }
} }

View File

@@ -15,16 +15,16 @@ import java.util.stream.Collectors;
*/ */
public class ContentRequestTransformer extends ProxyRequestTransformer { public class ContentRequestTransformer extends ProxyRequestTransformer {
@Override @Override
public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException { public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException {
RequestBuilder requestBuilder = predecessor.transform(request); RequestBuilder requestBuilder = predecessor.transform(request);
String requestContent = request.getReader().lines().collect(Collectors.joining("")); String requestContent = request.getReader().lines().collect(Collectors.joining(""));
if(!requestContent.isEmpty()) { if (!requestContent.isEmpty()) {
StringEntity entity = new StringEntity(requestContent, ContentType.APPLICATION_JSON); StringEntity entity = new StringEntity(requestContent, ContentType.APPLICATION_JSON);
requestBuilder.setEntity(entity); requestBuilder.setEntity(entity);
}
return requestBuilder;
} }
return requestBuilder;
}
} }

View File

@@ -13,20 +13,19 @@ import java.util.Enumeration;
*/ */
public class HeadersRequestTransformer extends ProxyRequestTransformer { public class HeadersRequestTransformer extends ProxyRequestTransformer {
@Override
public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException {
RequestBuilder requestBuilder = predecessor.transform(request);
@Override Enumeration<String> headerNames = request.getHeaderNames();
public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException { while (headerNames.hasMoreElements()) {
RequestBuilder requestBuilder = predecessor.transform(request); String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
Enumeration<String> headerNames = request.getHeaderNames(); if (headerName.equals("x-access-token")) {
while (headerNames.hasMoreElements()) { requestBuilder.addHeader(headerName, headerValue);
String headerName = headerNames.nextElement(); }
String headerValue = request.getHeader(headerName);
if(headerName.equals("x-access-token")) {
requestBuilder.addHeader(headerName, headerValue);
}
}
return requestBuilder;
} }
return requestBuilder;
}
} }

View File

@@ -12,11 +12,11 @@ import java.net.URISyntaxException;
*/ */
public abstract class ProxyRequestTransformer { public abstract class ProxyRequestTransformer {
protected ProxyRequestTransformer predecessor; protected ProxyRequestTransformer predecessor;
public abstract RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException; public abstract RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException;
public void setPredecessor(ProxyRequestTransformer transformer) { public void setPredecessor(ProxyRequestTransformer transformer) {
this.predecessor = transformer; this.predecessor = transformer;
}; }
} }

View File

@@ -14,35 +14,35 @@ import java.net.URISyntaxException;
*/ */
public class URLRequestTransformer extends ProxyRequestTransformer { public class URLRequestTransformer extends ProxyRequestTransformer {
private ApiGatewayProperties apiGatewayProperties; private ApiGatewayProperties apiGatewayProperties;
public URLRequestTransformer(ApiGatewayProperties apiGatewayProperties) { public URLRequestTransformer(ApiGatewayProperties apiGatewayProperties) {
this.apiGatewayProperties = apiGatewayProperties; this.apiGatewayProperties = apiGatewayProperties;
}
@Override
public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException {
String requestURI = request.getRequestURI();
URI uri;
if (request.getQueryString() != null && !request.getQueryString().isEmpty()) {
uri = new URI(getServiceUrl(requestURI, request) + "?" + request.getQueryString());
} else {
uri = new URI(getServiceUrl(requestURI, request));
} }
@Override RequestBuilder rb = RequestBuilder.create(request.getMethod());
public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException { rb.setUri(uri);
String requestURI = request.getRequestURI(); return rb;
URI uri; }
if (request.getQueryString() != null && !request.getQueryString().isEmpty()) {
uri = new URI(getServiceUrl(requestURI, request) + "?" + request.getQueryString());
} else {
uri = new URI(getServiceUrl(requestURI, request));
}
RequestBuilder rb = RequestBuilder.create(request.getMethod()); private String getServiceUrl(String requestURI, HttpServletRequest httpServletRequest) throws NoSuchRequestHandlingMethodException {
rb.setUri(uri);
return rb;
}
private String getServiceUrl(String requestURI, HttpServletRequest httpServletRequest) throws NoSuchRequestHandlingMethodException { ApiGatewayProperties.Endpoint endpoint =
apiGatewayProperties.getEndpoints().stream()
ApiGatewayProperties.Endpoint endpoint = .filter(e ->
apiGatewayProperties.getEndpoints().stream() requestURI.matches(e.getPath()) && e.getMethod() == RequestMethod.valueOf(httpServletRequest.getMethod())
.filter(e -> )
requestURI.matches(e.getPath()) && e.getMethod() == RequestMethod.valueOf(httpServletRequest.getMethod()) .findFirst().orElseThrow(() -> new NoSuchRequestHandlingMethodException(httpServletRequest));
) return endpoint.getLocation() + requestURI;
.findFirst().orElseThrow(() -> new NoSuchRequestHandlingMethodException(httpServletRequest)); }
return endpoint.getLocation() + requestURI;
}
} }

View File

@@ -5,18 +5,21 @@ customers.queryside.service.host=localhost
transfers.commandside.service.host=localhost transfers.commandside.service.host=localhost
api.gateway.endpoints[0].path=[/]*accounts.* api.gateway.endpoints[0].path=[/]*api/accounts.*
api.gateway.endpoints[0].method=GET api.gateway.endpoints[0].method=GET
api.gateway.endpoints[0].location=http://${accounts.queryside.service.host}:8080 api.gateway.endpoints[0].location=http://${accounts.queryside.service.host}:8080
api.gateway.endpoints[1].path=[/]*accounts.* api.gateway.endpoints[1].path=[/]*api/customers.*/accounts
api.gateway.endpoints[1].method=POST api.gateway.endpoints[1].method=GET
api.gateway.endpoints[1].location=http://${accounts.commandside.service.host}:8080 api.gateway.endpoints[1].location=http://${accounts.queryside.service.host}:8080
api.gateway.endpoints[2].path=[/]*customers.* api.gateway.endpoints[2].path=[/]*api/accounts.*
api.gateway.endpoints[2].method=GET api.gateway.endpoints[2].method=POST
api.gateway.endpoints[2].location=http://${customers.queryside.service.host}:8080 api.gateway.endpoints[2].location=http://${accounts.commandside.service.host}:8080
api.gateway.endpoints[3].path=[/]*customers.* api.gateway.endpoints[3].path=[/]*api/customers.*
api.gateway.endpoints[3].method=POST api.gateway.endpoints[3].method=GET
api.gateway.endpoints[3].location=http://${customers.commandside.service.host}:8080 api.gateway.endpoints[3].location=http://${customers.queryside.service.host}:8080
api.gateway.endpoints[4].path=[/]*transfers.* api.gateway.endpoints[4].path=[/]*api/customers.*
api.gateway.endpoints[4].method=POST api.gateway.endpoints[4].method=POST
api.gateway.endpoints[4].location=http://${transfers.commandside.service.host}:8080 api.gateway.endpoints[4].location=http://${customers.commandside.service.host}:8080
api.gateway.endpoints[5].path=[/]*api/transfers.*
api.gateway.endpoints[5].method=POST
api.gateway.endpoints[5].location=http://${transfers.commandside.service.host}:8080

View File

@@ -14,6 +14,11 @@
</root> </root>
<logger name="org.springframework" level='info'> <logger name="org.springframework" level='info'>
</logger> </logger>
<logger name="net.chrisrichardson.eventstore.client" level='info'>
<logger name="net.chrisrichardson.eventstore.javaexamples.banking" level='info'>
</logger> </logger>
<logger name="io.eventuate" level='debug'>
</logger>
</configuration> </configuration>

View File

@@ -7,7 +7,7 @@ dependencies {
testCompile project(":accounts-query-side-backend") testCompile project(":accounts-query-side-backend")
testCompile project(":customers-command-side-backend") testCompile project(":customers-command-side-backend")
testCompile project(":customers-query-side-backend") testCompile project(":customers-query-side-backend")
testCompile project(":testutil-customers") testCompile project(":testutil")
testCompile "junit:junit:4.11" testCompile "junit:junit:4.11"
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"

View File

@@ -6,6 +6,7 @@ import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.t
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@Configuration @Configuration
@Import({AccountConfiguration.class, MoneyTransferConfiguration.class, EventuateJdbcEventStoreConfiguration.class}) @Import({AccountConfiguration.class, MoneyTransferConfiguration.class, EventuateJdbcEventStoreConfiguration.class})

View File

@@ -6,7 +6,7 @@ import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.a
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.AccountService; import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.AccountService;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransfer; import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransfer;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransferService; import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransferService;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.TransferState; import net.chrisrichardson.eventstore.javaexamples.banking.common.transactions.TransferState;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.TransferDetails; import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.TransferDetails;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@@ -23,7 +23,7 @@ import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=BankingTestConfiguration.class) @SpringApplicationConfiguration(classes = BankingTestConfiguration.class)
@IntegrationTest @IntegrationTest
public class MoneyTransferIntegrationTest { public class MoneyTransferIntegrationTest {
@@ -35,11 +35,11 @@ public class MoneyTransferIntegrationTest {
@Autowired @Autowired
private EventuateAggregateStore eventStore; private EventuateAggregateStore eventStore;
@Test @Test
public void shouldTransferMoney() { public void shouldTransferMoney() {
final EntityWithIdAndVersion<Account> fromAccount= await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(150), "")); final EntityWithIdAndVersion<Account> fromAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(150), ""));
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(300), "")); final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(300), ""));
@@ -49,22 +49,22 @@ public class MoneyTransferIntegrationTest {
new BigDecimal(80)))); new BigDecimal(80))));
eventually ( eventually(
() -> eventStore.find(Account.class, fromAccount.getEntityId()), () -> eventStore.find(Account.class, fromAccount.getEntityId()),
account -> Assert.assertEquals(new BigDecimal(70), account.getEntity().getBalance())); account -> Assert.assertEquals(new BigDecimal(70), account.getEntity().getBalance()));
eventually ( eventually(
() -> eventStore.find(Account.class, toAccount.getEntityId()), () -> eventStore.find(Account.class, toAccount.getEntityId()),
account -> Assert.assertEquals(new BigDecimal(380), account.getEntity().getBalance())); account -> Assert.assertEquals(new BigDecimal(380), account.getEntity().getBalance()));
eventually ( eventually(
() -> eventStore.find(MoneyTransfer.class, transaction.getEntityId()), () -> eventStore.find(MoneyTransfer.class, transaction.getEntityId()),
updatedTransaction -> Assert.assertEquals(TransferState.COMPLETED, updatedTransaction.getEntity().getState())); updatedTransaction -> Assert.assertEquals(TransferState.COMPLETED, updatedTransaction.getEntity().getState()));
} }
@Test @Test
public void shouldFailDueToInsufficientFunds() { public void shouldFailDueToInsufficientFunds() {
final EntityWithIdAndVersion<Account> fromAccount= await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(150), "")); final EntityWithIdAndVersion<Account> fromAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(150), ""));
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(300), "")); final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(300), ""));
@@ -74,15 +74,15 @@ public class MoneyTransferIntegrationTest {
new BigDecimal(200)))); new BigDecimal(200))));
eventually ( eventually(
() -> eventStore.find(MoneyTransfer.class, transaction.getEntityId()), () -> eventStore.find(MoneyTransfer.class, transaction.getEntityId()),
updatedTransaction -> Assert.assertEquals(TransferState.FAILED_DUE_TO_INSUFFICIENT_FUNDS, updatedTransaction.getEntity().getState())); updatedTransaction -> Assert.assertEquals(TransferState.FAILED_DUE_TO_INSUFFICIENT_FUNDS, updatedTransaction.getEntity().getState()));
eventually ( eventually(
() -> eventStore.find(Account.class, fromAccount.getEntityId()), () -> eventStore.find(Account.class, fromAccount.getEntityId()),
account -> Assert.assertEquals(new BigDecimal(150), account.getEntity().getBalance())); account -> Assert.assertEquals(new BigDecimal(150), account.getEntity().getBalance()));
eventually ( eventually(
() -> eventStore.find(Account.class, toAccount.getEntityId()), () -> eventStore.find(Account.class, toAccount.getEntityId()),
account -> Assert.assertEquals(new BigDecimal(300), account.getEntity().getBalance())); account -> Assert.assertEquals(new BigDecimal(300), account.getEntity().getBalance()));

View File

@@ -1,16 +1,13 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import io.eventuate.EntityWithIdAndVersion; import io.eventuate.EntityWithIdAndVersion;
import io.eventuate.EntityWithMetadata;
import io.eventuate.EventuateAggregateStore; import io.eventuate.EventuateAggregateStore;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.Account; import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.Account;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.AccountService; import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.AccountService;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransfer; import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransfer;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransferService; import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransferService;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.TransferState; import net.chrisrichardson.eventstore.javaexamples.banking.common.transactions.TransferState;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.TransferDetails; import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.TransferDetails;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -18,9 +15,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import rx.Observable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.concurrent.CompletableFuture;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.await; import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.await;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually; import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
@@ -59,10 +56,10 @@ public class AccountQuerySideIntegrationTest {
updatedTransaction -> Assert.assertEquals(TransferState.COMPLETED, updatedTransaction.getEntity().getState())); updatedTransaction -> Assert.assertEquals(TransferState.COMPLETED, updatedTransaction.getEntity().getState()));
eventually( eventually(
() -> accountQueryService.findByAccountId(fromAccount.getEntityId()), () -> CompletableFuture.completedFuture(accountQueryService.findByAccountId(fromAccount.getEntityId())),
accountInfo -> Assert.assertEquals(70*100, accountInfo.getBalance())); accountInfo -> Assert.assertEquals(70 * 100, accountInfo.getBalance()));
eventually( eventually(
() -> accountQueryService.findByAccountId(toAccount.getEntityId()), () -> CompletableFuture.completedFuture(accountQueryService.findByAccountId(toAccount.getEntityId())),
accountInfo -> Assert.assertEquals(380*100, accountInfo.getBalance())); accountInfo -> Assert.assertEquals(380 * 100, accountInfo.getBalance()));
} }
} }

View File

@@ -19,10 +19,10 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateCustomerInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateToAccountInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.await; import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.await;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually; import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils.generateCustomerInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils.generateToAccountInfo;
/** /**
* Created by Main on 10.02.2016. * Created by Main on 10.02.2016.
@@ -32,43 +32,43 @@ import static net.chrisrichardson.eventstorestore.javaexamples.testutil.customer
@IntegrationTest @IntegrationTest
public class CustomerQuerySideIntegrationTest { public class CustomerQuerySideIntegrationTest {
@Autowired @Autowired
private CustomerService customerService; private CustomerService customerService;
@Autowired @Autowired
private CustomerQueryService customerQueryService; private CustomerQueryService customerQueryService;
@Autowired @Autowired
private EventuateAggregateStore eventStore; private EventuateAggregateStore eventStore;
@Test @Test
public void shouldCreateCustomerAndAddToAccount() throws Exception { public void shouldCreateCustomerAndAddToAccount() throws Exception {
CustomerInfo customerInfo = generateCustomerInfo(); CustomerInfo customerInfo = generateCustomerInfo();
EntityWithIdAndVersion<Customer> customer = await(customerService.createCustomer(customerInfo)); EntityWithIdAndVersion<Customer> customer = await(customerService.createCustomer(customerInfo));
ToAccountInfo toAccountInfo = generateToAccountInfo(); ToAccountInfo toAccountInfo = generateToAccountInfo();
EntityWithIdAndVersion<Customer> customerWithNewAccount = await(customerService.addToAccount(customer.getEntityId(), toAccountInfo)); EntityWithIdAndVersion<Customer> customerWithNewAccount = await(customerService.addToAccount(customer.getEntityId(), toAccountInfo));
eventually( eventually(
new Producer<QuerySideCustomer>() { new Producer<QuerySideCustomer>() {
@Override @Override
public CompletableFuture<QuerySideCustomer> produce() { public CompletableFuture<QuerySideCustomer> produce() {
return customerQueryService.findByCustomerId(customer.getEntityId()); return customerQueryService.findByCustomerId(customer.getEntityId());
} }
}, },
new Verifier<QuerySideCustomer>() { new Verifier<QuerySideCustomer>() {
@Override @Override
public void verify(QuerySideCustomer querySideCustomer) { public void verify(QuerySideCustomer querySideCustomer) {
Assert.assertEquals(customerInfo.getName(), querySideCustomer.getName()); Assert.assertEquals(customerInfo.getName(), querySideCustomer.getName());
Assert.assertEquals(customerInfo.getSsn(), querySideCustomer.getSsn()); Assert.assertEquals(customerInfo.getSsn(), querySideCustomer.getSsn());
Assert.assertEquals(customerInfo.getEmail(), querySideCustomer.getEmail()); Assert.assertEquals(customerInfo.getEmail(), querySideCustomer.getEmail());
Assert.assertEquals(customerInfo.getPhoneNumber(), querySideCustomer.getPhoneNumber()); Assert.assertEquals(customerInfo.getPhoneNumber(), querySideCustomer.getPhoneNumber());
Assert.assertEquals(customerInfo.getAddress(), querySideCustomer.getAddress()); Assert.assertEquals(customerInfo.getAddress(), querySideCustomer.getAddress());
Assert.assertNotNull(querySideCustomer.getToAccounts()); Assert.assertNotNull(querySideCustomer.getToAccounts());
Assert.assertFalse(querySideCustomer.getToAccounts().isEmpty()); Assert.assertFalse(querySideCustomer.getToAccounts().isEmpty());
Assert.assertEquals(querySideCustomer.getToAccounts().get("11111111-11111111"), toAccountInfo); Assert.assertEquals(querySideCustomer.getToAccounts().get("11111111-11111111"), toAccountInfo);
} }
}); });
} }
} }

View File

@@ -2,7 +2,7 @@ apply plugin: 'java'
dependencies { dependencies {
compile project(":common-auth") compile project(":common-auth")
compile project(":common-customers") compile project(":common")
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
compile "org.springframework.boot:spring-boot-starter-security:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-security:$springBootVersion"

View File

@@ -28,37 +28,38 @@ import static org.springframework.web.bind.annotation.RequestMethod.POST;
*/ */
@RestController @RestController
@Validated @Validated
@RequestMapping("/api")
public class AuthController { public class AuthController {
@Autowired @Autowired
private TokenService tokenService; private TokenService tokenService;
@Autowired @Autowired
private CustomerAuthService customerAuthService; private CustomerAuthService customerAuthService;
private static ObjectMapper objectMapper = new ObjectMapper(); private static ObjectMapper objectMapper = new ObjectMapper();
@RequestMapping(value = "/login", method = POST) @RequestMapping(value = "/login", method = POST)
public ResponseEntity<QuerySideCustomer> doAuth(@RequestBody @Valid AuthRequest request) throws IOException { public ResponseEntity<QuerySideCustomer> doAuth(@RequestBody @Valid AuthRequest request) throws IOException {
QuerySideCustomer customer = customerAuthService.findByEmail(request.getEmail()); QuerySideCustomer customer = customerAuthService.findByEmail(request.getEmail());
Token token = tokenService.allocateToken(objectMapper.writeValueAsString(new User(request.getEmail()))); Token token = tokenService.allocateToken(objectMapper.writeValueAsString(new User(request.getEmail())));
return ResponseEntity.status(HttpStatus.OK).header("access-token", token.getKey()) return ResponseEntity.status(HttpStatus.OK).header("access-token", token.getKey())
.body(customer); .body(customer);
} }
@ResponseStatus(value = HttpStatus.NOT_FOUND) @ResponseStatus(value = HttpStatus.NOT_FOUND)
@ExceptionHandler(IncorrectResultSizeDataAccessException.class) @ExceptionHandler(IncorrectResultSizeDataAccessException.class)
public ErrorResponse customersNotFound() { public ErrorResponse customersNotFound() {
return new ErrorResponse("Customer not found"); return new ErrorResponse("Customer not found");
} }
@RequestMapping(value = "/user", method = GET) @RequestMapping(value = "/user", method = GET)
public ResponseEntity<QuerySideCustomer> getCurrentUser() { public ResponseEntity<QuerySideCustomer> getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication(); Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return ResponseEntity.status(HttpStatus.OK).body(customerAuthService.findByEmail(auth.getName())); return ResponseEntity.status(HttpStatus.OK).body(customerAuthService.findByEmail(auth.getName()));
} }
} }

View File

@@ -8,22 +8,22 @@ import org.hibernate.validator.constraints.NotBlank;
*/ */
public class AuthRequest { public class AuthRequest {
@NotBlank @NotBlank
@Email @Email
private String email; private String email;
public AuthRequest() { public AuthRequest() {
} }
public AuthRequest(String email) { public AuthRequest(String email) {
this.email = email; this.email = email;
} }
public String getEmail() { public String getEmail() {
return email; return email;
} }
public void setEmail(String email) { public void setEmail(String email) {
this.email = email; this.email = email;
} }
} }

View File

@@ -5,20 +5,20 @@ package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model;
*/ */
public class ErrorResponse { public class ErrorResponse {
private String message; private String message;
public ErrorResponse() { public ErrorResponse() {
} }
public ErrorResponse(String message) { public ErrorResponse(String message) {
this.message = message; this.message = message;
} }
public String getMessage() { public String getMessage() {
return message; return message;
} }
public void setMessage(String message) { public void setMessage(String message) {
this.message = message; this.message = message;
} }
} }

View File

@@ -1,7 +1,7 @@
apply plugin: 'java' apply plugin: 'java'
dependencies { dependencies {
compile project(":common-customers") compile project(":common")
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion"

View File

@@ -32,66 +32,66 @@ import java.security.SecureRandom;
@EnableConfigurationProperties({AuthProperties.class}) @EnableConfigurationProperties({AuthProperties.class})
public class AuthConfiguration extends WebSecurityConfigurerAdapter { public class AuthConfiguration extends WebSecurityConfigurerAdapter {
@Autowired @Autowired
private AuthProperties securityProperties; private AuthProperties securityProperties;
@Autowired @Autowired
private TokenAuthenticationService tokenAuthenticationService; private TokenAuthenticationService tokenAuthenticationService;
@Autowired @Autowired
CustomerAuthService customerAuthService; CustomerAuthService customerAuthService;
@Override @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//auth.inMemoryAuthentication(); //auth.inMemoryAuthentication();
auth.userDetailsService(userDetailsServiceBean()); auth.userDetailsService(userDetailsServiceBean());
} }
@Override @Override
public UserDetailsService userDetailsServiceBean() { public UserDetailsService userDetailsServiceBean() {
return email -> { return email -> {
/* QuerySideCustomer customer = customerAuthService.findByEmail(email); /* QuerySideCustomer customer = customerAuthService.findByEmail(email);
if (customer != null) { if (customer != null) {
return new User(email); return new User(email);
} else { } else {
throw new UsernameNotFoundException(String.format("could not find the customer '%s'", email)); throw new UsernameNotFoundException(String.format("could not find the customer '%s'", email));
}*/ }*/
//authorize everyone with basic authentication //authorize everyone with basic authentication
return new User(email, "", true, true, true, true, return new User(email, "", true, true, true, true,
AuthorityUtils.createAuthorityList("USER")); AuthorityUtils.createAuthorityList("USER"));
}; };
} }
@Bean @Bean
public CustomerAuthService customerAuthService(CustomerAuthRepository customerAuthRepository) { public CustomerAuthService customerAuthService(CustomerAuthRepository customerAuthRepository) {
return new CustomerAuthService(customerAuthRepository); return new CustomerAuthService(customerAuthRepository);
} }
@Bean @Bean
@Override @Override
public AuthenticationManager authenticationManagerBean() throws Exception { public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean(); return super.authenticationManagerBean();
} }
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() http.csrf().disable()
.httpBasic().and() .httpBasic().and()
.authorizeRequests() .authorizeRequests()
.antMatchers("/index.html", "/", "/**.js", "/**.css").permitAll() .antMatchers("/index.html", "/", "/**.js", "/**.css").permitAll()
.antMatchers("/swagger-ui.html", "/v2/api-docs").permitAll() .antMatchers("/swagger-ui.html", "/v2/api-docs").permitAll()
.antMatchers(HttpMethod.POST, "/customers", "/login").permitAll() .antMatchers(HttpMethod.POST, "/api/customers", "/api/login").permitAll()
.anyRequest().authenticated().and() .anyRequest().authenticated().and()
.addFilterAfter(new StatelessAuthenticationFilter(tokenAuthenticationService), BasicAuthenticationFilter.class); .addFilterAfter(new StatelessAuthenticationFilter(tokenAuthenticationService), BasicAuthenticationFilter.class);
} }
@Bean @Bean
public TokenService tokenService() { public TokenService tokenService() {
KeyBasedPersistenceTokenService res = new KeyBasedPersistenceTokenService(); KeyBasedPersistenceTokenService res = new KeyBasedPersistenceTokenService();
res.setSecureRandom(new SecureRandom()); res.setSecureRandom(new SecureRandom());
res.setServerSecret(securityProperties.getServerSecret()); res.setServerSecret(securityProperties.getServerSecret());
res.setServerInteger(securityProperties.getServerInteger()); res.setServerInteger(securityProperties.getServerInteger());
return res; return res;
} }
} }

View File

@@ -7,22 +7,22 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
*/ */
@ConfigurationProperties(locations = "classpath:auth.properties", ignoreUnknownFields = false, prefix = "auth") @ConfigurationProperties(locations = "classpath:auth.properties", ignoreUnknownFields = false, prefix = "auth")
public class AuthProperties { public class AuthProperties {
private String serverSecret; private String serverSecret;
private Integer serverInteger; private Integer serverInteger;
public String getServerSecret() { public String getServerSecret() {
return serverSecret; return serverSecret;
} }
public void setServerSecret(String serverSecret) { public void setServerSecret(String serverSecret) {
this.serverSecret = serverSecret; this.serverSecret = serverSecret;
} }
public Integer getServerInteger() { public Integer getServerInteger() {
return serverInteger; return serverInteger;
} }
public void setServerInteger(Integer serverInteger) { public void setServerInteger(Integer serverInteger) {
this.serverInteger = serverInteger; this.serverInteger = serverInteger;
} }
} }

View File

@@ -7,5 +7,5 @@ import java.util.List;
interface CustomerAuthRepository extends MongoRepository<QuerySideCustomer, String> { interface CustomerAuthRepository extends MongoRepository<QuerySideCustomer, String> {
List<QuerySideCustomer> findByEmail(String email); List<QuerySideCustomer> findByEmail(String email);
} }

View File

@@ -2,7 +2,6 @@ package net.chrisrichardson.eventstore.javaexamples.banking.commonauth;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.QuerySideCustomer; import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.QuerySideCustomer;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import java.util.List; import java.util.List;
@@ -10,20 +9,20 @@ import java.util.List;
* Created by Main on 15.02.2016. * Created by Main on 15.02.2016.
*/ */
public class CustomerAuthService { public class CustomerAuthService {
private CustomerAuthRepository customerAuthRepository; private CustomerAuthRepository customerAuthRepository;
public CustomerAuthService(CustomerAuthRepository customerAuthRepository) { public CustomerAuthService(CustomerAuthRepository customerAuthRepository) {
this.customerAuthRepository = customerAuthRepository; this.customerAuthRepository = customerAuthRepository;
} }
public QuerySideCustomer findByEmail(String email){ public QuerySideCustomer findByEmail(String email) {
List<QuerySideCustomer> customers = customerAuthRepository.findByEmail(email); List<QuerySideCustomer> customers = customerAuthRepository.findByEmail(email);
if (customers.isEmpty()) if (customers.isEmpty())
throw new EmptyResultDataAccessException(1); throw new EmptyResultDataAccessException(1);
//TODO: add unique email constraint //TODO: add unique email constraint
/* else if(customers.size()>1) /* else if(customers.size()>1)
throw new IncorrectResultSizeDataAccessException(1, customers.size());*/ throw new IncorrectResultSizeDataAccessException(1, customers.size());*/
else else
return customers.get(0); return customers.get(0);
} }
} }

View File

@@ -18,26 +18,26 @@ import java.io.IOException;
@Service @Service
public class TokenAuthenticationService { public class TokenAuthenticationService {
@Autowired @Autowired
private TokenService tokenService; private TokenService tokenService;
private static final String AUTH_HEADER_NAME = "access-token"; private static final String AUTH_HEADER_NAME = "access-token";
private static final long DAY = 1000 * 60 * 60 * 24; private static final long DAY = 1000 * 60 * 60 * 24;
private ObjectMapper mapper = new ObjectMapper(); private ObjectMapper mapper = new ObjectMapper();
public Authentication getAuthentication(HttpServletRequest request) throws IOException { public Authentication getAuthentication(HttpServletRequest request) throws IOException {
final String tokenString = request.getHeader(AUTH_HEADER_NAME); final String tokenString = request.getHeader(AUTH_HEADER_NAME);
if (tokenString != null) { if (tokenString != null) {
Token token = tokenService.verifyToken(tokenString); Token token = tokenService.verifyToken(tokenString);
final User user = mapper.readValue(token.getExtendedInformation(), User.class); final User user = mapper.readValue(token.getExtendedInformation(), User.class);
if (user != null && (System.currentTimeMillis() - token.getKeyCreationTime()) < DAY) { if (user != null && (System.currentTimeMillis() - token.getKeyCreationTime()) < DAY) {
return new UserAuthentication(user); return new UserAuthentication(user);
} }
}
return null;
} }
return null;
}
} }

View File

@@ -16,18 +16,18 @@ import java.io.IOException;
*/ */
public class StatelessAuthenticationFilter extends GenericFilterBean { public class StatelessAuthenticationFilter extends GenericFilterBean {
private final TokenAuthenticationService tokenAuthenticationService; private final TokenAuthenticationService tokenAuthenticationService;
public StatelessAuthenticationFilter(TokenAuthenticationService taService) { public StatelessAuthenticationFilter(TokenAuthenticationService taService) {
this.tokenAuthenticationService = taService; this.tokenAuthenticationService = taService;
} }
@Override @Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
if (SecurityContextHolder.getContext().getAuthentication()==null) { if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication( SecurityContextHolder.getContext().setAuthentication(
tokenAuthenticationService.getAuthentication((HttpServletRequest) req)); tokenAuthenticationService.getAuthentication((HttpServletRequest) req));
}
chain.doFilter(req, res);
} }
chain.doFilter(req, res);
}
} }

View File

@@ -16,63 +16,63 @@ import java.util.Set;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class User implements UserDetails { public class User implements UserDetails {
private String email; private String email;
public User() { public User() {
} }
public User(String email) { public User(String email) {
this.email = email; this.email = email;
} }
public void setUsername(String username) { public void setUsername(String username) {
this.email = username; this.email = username;
} }
@Override @Override
@JsonIgnore @JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() { public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("USER"); SimpleGrantedAuthority authority = new SimpleGrantedAuthority("USER");
Set<GrantedAuthority> res = new HashSet<GrantedAuthority>(); Set<GrantedAuthority> res = new HashSet<GrantedAuthority>();
res.add(authority); res.add(authority);
return res; return res;
} }
@Override @Override
public String getPassword() { public String getPassword() {
return ""; return "";
} }
@Override @Override
public String getUsername() { public String getUsername() {
return this.email; return this.email;
} }
@Override @Override
public boolean isAccountNonExpired() { public boolean isAccountNonExpired() {
return false; return false;
} }
@Override @Override
public boolean isAccountNonLocked() { public boolean isAccountNonLocked() {
return false; return false;
} }
@Override @Override
public boolean isCredentialsNonExpired() { public boolean isCredentialsNonExpired() {
return false; return false;
} }
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return false; return false;
} }
public String getEmail() { public String getEmail() {
return email; return email;
} }
public void setEmail(String email) { public void setEmail(String email) {
this.email = email; this.email = email;
} }
} }

View File

@@ -10,45 +10,45 @@ import java.util.Collection;
*/ */
public class UserAuthentication implements Authentication { public class UserAuthentication implements Authentication {
private final User user; private final User user;
private boolean authenticated = true; private boolean authenticated = true;
public UserAuthentication(User user) { public UserAuthentication(User user) {
this.user = user; this.user = user;
} }
@Override @Override
public String getName() { public String getName() {
return user.getUsername(); return user.getUsername();
} }
@Override @Override
public Collection<? extends GrantedAuthority> getAuthorities() { public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities(); return user.getAuthorities();
} }
@Override @Override
public Object getCredentials() { public Object getCredentials() {
return user.getPassword(); return user.getPassword();
} }
@Override @Override
public User getDetails() { public User getDetails() {
return user; return user;
} }
@Override @Override
public Object getPrincipal() { public Object getPrincipal() {
return user.getUsername(); return user.getUsername();
} }
@Override @Override
public boolean isAuthenticated() { public boolean isAuthenticated() {
return authenticated; return authenticated;
} }
@Override @Override
public void setAuthenticated(boolean authenticated) { public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated; this.authenticated = authenticated;
} }
} }

View File

@@ -1,45 +0,0 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.utils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.Charset;
/**
* Created by Main on 18.02.2016.
*/
public class BasicAuthUtils {
public static HttpHeaders basicAuthHeaders(String username) {
return new HttpHeaders() {
{
String auth = username + ":";
byte[] encodedAuth = Base64.encodeBase64(
auth.getBytes(Charset.forName("US-ASCII")));
String authHeader = "Basic " + new String(encodedAuth);
set("Authorization", authHeader);
}
};
}
public static <T> T doBasicAuthenticatedRequest(RestTemplate restTemplate, String url, HttpMethod httpMethod, Class<T> responseType) {
return doBasicAuthenticatedRequest(restTemplate, url, httpMethod, responseType, null);
}
public static <T> T doBasicAuthenticatedRequest(RestTemplate restTemplate, String url, HttpMethod httpMethod, Class<T> responseType, Object requestObject) {
HttpEntity httpEntity;
if(requestObject!=null) {
httpEntity = new HttpEntity(requestObject, BasicAuthUtils.basicAuthHeaders("test_user@mail.com"));
} else {
httpEntity = new HttpEntity(BasicAuthUtils.basicAuthHeaders("test_user@mail.com"));
}
return restTemplate.exchange(url,
httpMethod,
httpEntity,
responseType).getBody();
}
}

View File

@@ -1,7 +1,7 @@
apply plugin: 'java' apply plugin: 'java'
dependencies { dependencies {
compile project(":common-customers") compile project(":common")
compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion" compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion"

View File

@@ -1,7 +1,5 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts;
import io.eventuate.Aggregate;
import io.eventuate.Event; import io.eventuate.Event;
import java.math.BigDecimal; import java.math.BigDecimal;

View File

@@ -1,8 +1,5 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts;
import io.eventuate.Aggregate;
import java.math.BigDecimal; import java.math.BigDecimal;
public class AccountCreditedEvent extends AccountChangedEvent { public class AccountCreditedEvent extends AccountChangedEvent {

View File

@@ -1,7 +1,5 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts;
import io.eventuate.Aggregate;
import io.eventuate.Event; import io.eventuate.Event;
public class AccountDebitFailedDueToInsufficientFundsEvent implements Event { public class AccountDebitFailedDueToInsufficientFundsEvent implements Event {

View File

@@ -1,8 +1,5 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts;
import io.eventuate.Aggregate;
import java.math.BigDecimal; import java.math.BigDecimal;
public class AccountDebitedEvent extends AccountChangedEvent { public class AccountDebitedEvent extends AccountChangedEvent {

View File

@@ -7,20 +7,20 @@ import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAc
*/ */
public class CustomerAddedToAccount extends CustomerEvent { public class CustomerAddedToAccount extends CustomerEvent {
private ToAccountInfo toAccountInfo; private ToAccountInfo toAccountInfo;
public CustomerAddedToAccount() { public CustomerAddedToAccount() {
} }
public CustomerAddedToAccount(ToAccountInfo toAccountInfo) { public CustomerAddedToAccount(ToAccountInfo toAccountInfo) {
this.toAccountInfo = toAccountInfo; this.toAccountInfo = toAccountInfo;
} }
public ToAccountInfo getToAccountInfo() { public ToAccountInfo getToAccountInfo() {
return toAccountInfo; return toAccountInfo;
} }
public void setToAccountInfo(ToAccountInfo toAccountInfo) { public void setToAccountInfo(ToAccountInfo toAccountInfo) {
this.toAccountInfo = toAccountInfo; this.toAccountInfo = toAccountInfo;
} }
} }

View File

@@ -7,20 +7,20 @@ import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Cust
*/ */
public class CustomerCreatedEvent extends CustomerEvent { public class CustomerCreatedEvent extends CustomerEvent {
private CustomerInfo customerInfo; private CustomerInfo customerInfo;
public CustomerCreatedEvent() { public CustomerCreatedEvent() {
} }
public CustomerCreatedEvent(CustomerInfo customerInfo) { public CustomerCreatedEvent(CustomerInfo customerInfo) {
this.customerInfo = customerInfo; this.customerInfo = customerInfo;
} }
public CustomerInfo getCustomerInfo() { public CustomerInfo getCustomerInfo() {
return customerInfo; return customerInfo;
} }
public void setCustomerInfo(CustomerInfo customerInfo) { public void setCustomerInfo(CustomerInfo customerInfo) {
this.customerInfo = customerInfo; this.customerInfo = customerInfo;
} }
} }

View File

@@ -7,6 +7,6 @@ import io.eventuate.EventEntity;
/** /**
* Created by Main on 11.02.2016. * Created by Main on 11.02.2016.
*/ */
@EventEntity(entity="net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.Customer") @EventEntity(entity = "net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.Customer")
public abstract class CustomerEvent implements Event { public abstract class CustomerEvent implements Event {
} }

View File

@@ -6,6 +6,7 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.math.BigDecimal; import java.math.BigDecimal;
public class AccountOpenEventSerializationTest { public class AccountOpenEventSerializationTest {
@Test @Test

View File

@@ -1,24 +0,0 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
/**
* Created by popikyardo on 24.03.16.
*/
public class AddToAccountResponse {
private String version;
public AddToAccountResponse() {
}
public AddToAccountResponse(String version) {
this.version = version;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}

View File

@@ -1,82 +0,0 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import javax.validation.constraints.NotNull;
/**
* Created by popikyardo on 02.02.16.
*/
public class Address {
@NotNull
private String street1;
private String street2;
@NotNull
private String city;
@NotNull
private String state;
@NotNull
private String zipCode;
public Address() {
}
public Address(String street1, String street2, String city, String state, String zipCode) {
this.street1 = street1;
this.street2 = street2;
this.city = city;
this.state = state;
this.zipCode = zipCode;
}
public String getStreet1() {
return street1;
}
public void setStreet1(String street1) {
this.street1 = street1;
}
public String getStreet2() {
return street2;
}
public void setStreet2(String street2) {
this.street2 = street2;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -1,61 +0,0 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import javax.validation.constraints.NotNull;
/**
* Created by popikyardo on 03.02.16.
*/
public class CustomerInfo {
private Name name;
@NotNull
protected String email;
@NotNull
protected String ssn;
@NotNull
protected String phoneNumber;
protected Address address;
public CustomerInfo() {
}
public CustomerInfo(Name name, String email, String ssn, String phoneNumber, Address address) {
this.name = name;
this.email = email;
this.ssn = ssn;
this.phoneNumber = phoneNumber;
this.address = address;
}
public Name getName() {
return name;
}
public String getEmail() {
return email;
}
public String getSsn() {
return ssn;
}
public String getPhoneNumber() {
return phoneNumber;
}
public Address getAddress() {
return address;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -1,47 +0,0 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
/**
* Created by popikyardo on 03.02.16.
*/
public class CustomerResponse {
private String id;
private CustomerInfo customerInfo;
public CustomerResponse() {
}
public CustomerResponse(String id, CustomerInfo customerInfo) {
this.id = id;
this.customerInfo = customerInfo;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public CustomerInfo getCustomerInfo() {
return customerInfo;
}
public void setCustomerInfo(CustomerInfo customerInfo) {
this.customerInfo = customerInfo;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -1,50 +0,0 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import javax.validation.constraints.NotNull;
/**
* Created by Main on 10.02.2016.
*/
public class Name {
@NotNull
private String firstName;
@NotNull
private String lastName;
public Name() {
}
public Name(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -1,85 +0,0 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import java.util.Map;
/**
* Created by Main on 05.02.2016.
*/
public class QuerySideCustomer{
private String id;
private Name name;
private String email;
private String ssn;
private String phoneNumber;
private Address address;
private Map<String, ToAccountInfo> toAccounts;
public QuerySideCustomer() {
}
public QuerySideCustomer(String id, Name name, String email, String ssn, String phoneNumber, Address address, Map<String, ToAccountInfo> toAccounts) {
this.id = id;
this.name = name;
this.email = email;
this.ssn = ssn;
this.phoneNumber = phoneNumber;
this.address = address;
this.toAccounts = toAccounts;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Map<String, ToAccountInfo> getToAccounts() {
return toAccounts;
}
public void setToAccounts(Map<String, ToAccountInfo> toAccounts) {
this.toAccounts = toAccounts;
}
}

View File

@@ -1,67 +0,0 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
/**
* Created by Main on 08.02.2016.
*/
public class ToAccountInfo {
private String id;
private String title;
private String owner;
private String description;
public ToAccountInfo() {
}
public ToAccountInfo(String id, String title, String owner, String description) {
this.id = id;
this.title = title;
this.owner = owner;
this.description = description;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -1,8 +1,8 @@
apply plugin: 'java' apply plugin: 'java'
dependencies { dependencies {
compile "commons-lang:commons-lang:2.6"
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion"
testCompile group: 'junit', name: 'junit', version: '4.11' testCompile group: 'junit', name: 'junit', version: '4.11'
} }

View File

@@ -0,0 +1,80 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import java.util.Date;
public class AccountChangeInfo {
private String changeId;
private String transactionId;
private String transactionType;
private long amount;
private long balanceDelta;
public AccountChangeInfo() {
}
public AccountChangeInfo(String changeId, String transactionId, String transactionType, long amount, long balanceDelta) {
this(new Date(), changeId, transactionId, transactionType, amount, balanceDelta);
}
public AccountChangeInfo(Date date, String changeId, String transactionId, String transactionType, long amount, long balanceDelta) {
this.changeId = changeId;
this.transactionId = transactionId;
this.transactionType = transactionType;
this.amount = amount;
this.balanceDelta = balanceDelta;
}
public String getChangeId() {
return changeId;
}
public void setChangeId(String changeId) {
this.changeId = changeId;
}
public String getTransactionId() {
return transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
public String getTransactionType() {
return transactionType;
}
public void setTransactionType(String transactionType) {
this.transactionType = transactionType;
}
public long getAmount() {
return amount;
}
public void setAmount(long amount) {
this.amount = amount;
}
public long getBalanceDelta() {
return balanceDelta;
}
public void setBalanceDelta(long balanceDelta) {
this.balanceDelta = balanceDelta;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -0,0 +1,50 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import java.util.Date;
/**
* Created by popikyardo on 9/1/16.
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,
include = JsonTypeInfo.As.PROPERTY,
property = "entryType")
@JsonSubTypes({
@JsonSubTypes.Type(value = AccountTransactionInfo.class, name = "transaction"),
@JsonSubTypes.Type(value = AccountOpenInfo.class, name = "account")
})
public class AccountHistoryEntry {
protected Date date;
protected EntryType entryType;
public AccountHistoryEntry() {
}
public AccountHistoryEntry(Date date, EntryType entryType) {
this.date = date;
this.entryType = entryType;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public EntryType getEntryType() {
return entryType;
}
public void setEntryType(EntryType entryType) {
this.entryType = entryType;
}
public enum EntryType {
transaction, account
}
}

View File

@@ -0,0 +1,26 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts;
import java.util.List;
/**
* Created by popikyardo on 9/1/16.
*/
public class AccountHistoryResponse {
private List<AccountHistoryEntry> transactionsHistory;
public AccountHistoryResponse() {
}
public AccountHistoryResponse(List<AccountHistoryEntry> transactionsHistory) {
this.transactionsHistory = transactionsHistory;
}
public List<AccountHistoryEntry> getTransactionsHistory() {
return transactionsHistory;
}
public void setTransactionsHistory(List<AccountHistoryEntry> transactionsHistory) {
this.transactionsHistory = transactionsHistory;
}
}

View File

@@ -0,0 +1,27 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts;
import java.util.Date;
/**
* Created by popikyardo on 9/1/16.
*/
public class AccountOpenInfo extends AccountHistoryEntry {
private long initialBalance;
public AccountOpenInfo() {
}
public AccountOpenInfo(Date date, EntryType entryType, long initialBalance) {
super(date, entryType);
this.initialBalance=initialBalance;
}
public long getInitialBalance() {
return initialBalance;
}
public void setInitialBalance(long initialBalance) {
this.initialBalance = initialBalance;
}
}

View File

@@ -1,25 +1,26 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts;
import net.chrisrichardson.eventstore.javaexamples.banking.common.transactions.TransferState;
import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringBuilder;
import java.util.Date; import java.util.Date;
public class AccountTransactionInfo { public class AccountTransactionInfo extends AccountHistoryEntry{
private String transactionId; private String transactionId;
private String fromAccountId; private String fromAccountId;
private String toAccountId; private String toAccountId;
private long amount; private long amount;
private Date date;
private String description; private String description;
private TransferState status = TransferState.INITIAL;
public AccountTransactionInfo() { public AccountTransactionInfo() {
} }
public AccountTransactionInfo(String transactionId, String fromAccountId, String toAccountId, long amount) { public AccountTransactionInfo(String transactionId, String fromAccountId, String toAccountId, long amount) {
this(transactionId, fromAccountId, toAccountId, amount, new Date(), ""); this(transactionId, fromAccountId, toAccountId, amount, new Date(), "");
} }
@Override @Override
@@ -35,6 +36,7 @@ public class AccountTransactionInfo {
this.amount = amount; this.amount = amount;
this.date = date; this.date = date;
this.description = description; this.description = description;
this.entryType = EntryType.transaction;
} }
public String getTransactionId() { public String getTransactionId() {
@@ -69,14 +71,6 @@ public class AccountTransactionInfo {
this.amount = amount; this.amount = amount;
} }
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getDescription() { public String getDescription() {
return description; return description;
} }
@@ -85,6 +79,14 @@ public class AccountTransactionInfo {
this.description = description; this.description = description;
} }
public TransferState getStatus() {
return status;
}
public void setStatus(TransferState status) {
this.status = status;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o); return EqualsBuilder.reflectionEquals(this, o);

View File

@@ -1,8 +1,7 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.DecimalMin; import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal; import java.math.BigDecimal;
public class CreateAccountRequest { public class CreateAccountRequest {

View File

@@ -1,4 +1,4 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts; package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts;
public class CreateAccountResponse { public class CreateAccountResponse {

View File

@@ -0,0 +1,53 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts;
import java.math.BigDecimal;
public class GetAccountResponse {
private String accountId;
private BigDecimal balance;
private String title;
private String description;
public GetAccountResponse() {
}
public GetAccountResponse(String accountId, BigDecimal balance, String title, String description) {
this.accountId = accountId;
this.balance = balance;
this.title = title;
this.description = description;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public String getAccountId() {
return accountId;
}
public BigDecimal getBalance() {
return balance;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View File

@@ -0,0 +1,25 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts;
import java.util.List;
/**
* Created by popikyardo on 9/1/16.
*/
public class GetAccountsResponse {
private List<GetAccountResponse> accounts;
public GetAccountsResponse() {
}
public GetAccountsResponse(List<GetAccountResponse> accounts) {
this.accounts = accounts;
}
public List<GetAccountResponse> getAccounts() {
return accounts;
}
public void setAccounts(List<GetAccountResponse> accounts) {
this.accounts = accounts;
}
}

View File

@@ -0,0 +1,24 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
/**
* Created by popikyardo on 24.03.16.
*/
public class AddToAccountResponse {
private String version;
public AddToAccountResponse() {
}
public AddToAccountResponse(String version) {
this.version = version;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}

View File

@@ -0,0 +1,82 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import javax.validation.constraints.NotNull;
/**
* Created by popikyardo on 02.02.16.
*/
public class Address {
@NotNull
private String street1;
private String street2;
@NotNull
private String city;
@NotNull
private String state;
@NotNull
private String zipCode;
public Address() {
}
public Address(String street1, String street2, String city, String state, String zipCode) {
this.street1 = street1;
this.street2 = street2;
this.city = city;
this.state = state;
this.zipCode = zipCode;
}
public String getStreet1() {
return street1;
}
public void setStreet1(String street1) {
this.street1 = street1;
}
public String getStreet2() {
return street2;
}
public void setStreet2(String street2) {
this.street2 = street2;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -0,0 +1,61 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import javax.validation.constraints.NotNull;
/**
* Created by popikyardo on 03.02.16.
*/
public class CustomerInfo {
private Name name;
@NotNull
protected String email;
@NotNull
protected String ssn;
@NotNull
protected String phoneNumber;
protected Address address;
public CustomerInfo() {
}
public CustomerInfo(Name name, String email, String ssn, String phoneNumber, Address address) {
this.name = name;
this.email = email;
this.ssn = ssn;
this.phoneNumber = phoneNumber;
this.address = address;
}
public Name getName() {
return name;
}
public String getEmail() {
return email;
}
public String getSsn() {
return ssn;
}
public String getPhoneNumber() {
return phoneNumber;
}
public Address getAddress() {
return address;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -0,0 +1,47 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
/**
* Created by popikyardo on 03.02.16.
*/
public class CustomerResponse {
private String id;
private CustomerInfo customerInfo;
public CustomerResponse() {
}
public CustomerResponse(String id, CustomerInfo customerInfo) {
this.id = id;
this.customerInfo = customerInfo;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public CustomerInfo getCustomerInfo() {
return customerInfo;
}
public void setCustomerInfo(CustomerInfo customerInfo) {
this.customerInfo = customerInfo;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -0,0 +1,50 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import javax.validation.constraints.NotNull;
/**
* Created by Main on 10.02.2016.
*/
public class Name {
@NotNull
private String firstName;
@NotNull
private String lastName;
public Name() {
}
public Name(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -0,0 +1,85 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import java.util.Map;
/**
* Created by Main on 05.02.2016.
*/
public class QuerySideCustomer {
private String id;
private Name name;
private String email;
private String ssn;
private String phoneNumber;
private Address address;
private Map<String, ToAccountInfo> toAccounts;
public QuerySideCustomer() {
}
public QuerySideCustomer(String id, Name name, String email, String ssn, String phoneNumber, Address address, Map<String, ToAccountInfo> toAccounts) {
this.id = id;
this.name = name;
this.email = email;
this.ssn = ssn;
this.phoneNumber = phoneNumber;
this.address = address;
this.toAccounts = toAccounts;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Map<String, ToAccountInfo> getToAccounts() {
return toAccounts;
}
public void setToAccounts(Map<String, ToAccountInfo> toAccounts) {
this.toAccounts = toAccounts;
}
}

View File

@@ -0,0 +1,67 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
/**
* Created by Main on 08.02.2016.
*/
public class ToAccountInfo {
private String id;
private String title;
private String owner;
private String description;
public ToAccountInfo() {
}
public ToAccountInfo(String id, String title, String owner, String description) {
this.id = id;
this.title = title;
this.owner = owner;
this.description = description;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -1,8 +1,8 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions; package net.chrisrichardson.eventstore.javaexamples.banking.common.transactions;
import java.math.BigDecimal;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.DecimalMin; import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
public class CreateMoneyTransferRequest { public class CreateMoneyTransferRequest {

View File

@@ -1,4 +1,4 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions; package net.chrisrichardson.eventstore.javaexamples.banking.common.transactions;
public class CreateMoneyTransferResponse { public class CreateMoneyTransferResponse {

View File

@@ -1,4 +1,4 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions; package net.chrisrichardson.eventstore.javaexamples.banking.common.transactions;
public enum TransferState { public enum TransferState {
NEW, INITIAL, DEBITED, COMPLETED, FAILED_DUE_TO_INSUFFICIENT_FUNDS NEW, INITIAL, DEBITED, COMPLETED, FAILED_DUE_TO_INSUFFICIENT_FUNDS

View File

@@ -1,11 +1,11 @@
apply plugin: 'java' apply plugin: 'java'
dependencies { dependencies {
compile project(":common-customers") compile project(":common")
compile project(":common-backend") compile project(":common-backend")
compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion" compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion"
testCompile project(":testutil-customers") testCompile project(":testutil")
testCompile "junit:junit:4.11" testCompile "junit:junit:4.11"
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"

View File

@@ -7,13 +7,13 @@ import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAc
*/ */
public class AddToAccountCommand implements CustomerCommand { public class AddToAccountCommand implements CustomerCommand {
private ToAccountInfo toAccountInfo; private ToAccountInfo toAccountInfo;
public AddToAccountCommand(ToAccountInfo toAccountInfo) { public AddToAccountCommand(ToAccountInfo toAccountInfo) {
this.toAccountInfo = toAccountInfo; this.toAccountInfo = toAccountInfo;
} }
public ToAccountInfo getToAccountInfo() { public ToAccountInfo getToAccountInfo() {
return toAccountInfo; return toAccountInfo;
} }
} }

View File

@@ -6,13 +6,13 @@ import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Cust
* Created by popikyardo on 02.02.16. * Created by popikyardo on 02.02.16.
*/ */
public class CreateCustomerCommand implements CustomerCommand { public class CreateCustomerCommand implements CustomerCommand {
private CustomerInfo customerInfo; private CustomerInfo customerInfo;
public CreateCustomerCommand(CustomerInfo customerInfo) { public CreateCustomerCommand(CustomerInfo customerInfo) {
this.customerInfo = customerInfo; this.customerInfo = customerInfo;
} }
public CustomerInfo getCustomerInfo() { public CustomerInfo getCustomerInfo() {
return customerInfo; return customerInfo;
} }
} }

View File

@@ -14,24 +14,24 @@ import java.util.List;
*/ */
public class Customer extends ReflectiveMutableCommandProcessingAggregate<Customer, CustomerCommand> { public class Customer extends ReflectiveMutableCommandProcessingAggregate<Customer, CustomerCommand> {
private CustomerInfo customerInfo; private CustomerInfo customerInfo;
public List<Event> process(CreateCustomerCommand cmd) { public List<Event> process(CreateCustomerCommand cmd) {
return EventUtil.events(new CustomerCreatedEvent(cmd.getCustomerInfo())); return EventUtil.events(new CustomerCreatedEvent(cmd.getCustomerInfo()));
} }
public List<Event> process(AddToAccountCommand cmd) { public List<Event> process(AddToAccountCommand cmd) {
return EventUtil.events(new CustomerAddedToAccount(cmd.getToAccountInfo())); return EventUtil.events(new CustomerAddedToAccount(cmd.getToAccountInfo()));
} }
public void apply(CustomerCreatedEvent event) { public void apply(CustomerCreatedEvent event) {
customerInfo = event.getCustomerInfo(); customerInfo = event.getCustomerInfo();
} }
public void apply(CustomerAddedToAccount event) { public void apply(CustomerAddedToAccount event) {
} }
public CustomerInfo getCustomerInfo() { public CustomerInfo getCustomerInfo() {
return customerInfo; return customerInfo;
} }
} }

View File

@@ -4,10 +4,12 @@ import io.eventuate.AggregateRepository;
import io.eventuate.EventuateAggregateStore; import io.eventuate.EventuateAggregateStore;
import io.eventuate.javaclient.spring.EnableEventHandlers; import io.eventuate.javaclient.spring.EnableEventHandlers;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
@EnableEventHandlers @EnableEventHandlers
@ComponentScan
public class CustomerConfiguration { public class CustomerConfiguration {
@Bean @Bean

View File

@@ -8,22 +8,24 @@ import org.junit.Test;
import java.util.List; import java.util.List;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils.generateCustomerInfo; import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateCustomerInfo;
public class CustomerTest { public class CustomerTest {
@Test @Test
public void testSomething() { public void testSomething() {
Customer customer = new Customer(); Customer customer = new Customer();
CustomerInfo customerInfo = generateCustomerInfo(); CustomerInfo customerInfo = generateCustomerInfo();
List<Event> events = customer.process(new CreateCustomerCommand(customerInfo)); List<Event> events = customer.process(new CreateCustomerCommand(customerInfo));
Assert.assertEquals(1, events.size());
Assert.assertEquals(CustomerCreatedEvent.class, events.get(0).getClass());
customer.applyEvent(events.get(0));
Assert.assertEquals(customerInfo, customer.getCustomerInfo());
}
Assert.assertEquals(1, events.size());
Assert.assertEquals(CustomerCreatedEvent.class, events.get(0).getClass());
customer.applyEvent(events.get(0));
Assert.assertEquals(customerInfo, customer.getCustomerInfo());
}
} }

View File

@@ -11,6 +11,7 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-web" compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-actuator" compile "org.springframework.boot:spring-boot-starter-actuator"
testCompile project(":testutil-customers") testCompile project(":testutil")
testCompile "org.springframework.boot:spring-boot-starter-test" testCompile "org.springframework.boot:spring-boot-starter-test"
testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"
} }

View File

@@ -6,7 +6,6 @@ import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.custo
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters; import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
@@ -15,7 +14,6 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
@Configuration @Configuration
@Import({CustomersCommandSideWebConfiguration.class, EventuateHttpStompClientConfiguration.class, CommonSwaggerConfiguration.class}) @Import({CustomersCommandSideWebConfiguration.class, EventuateHttpStompClientConfiguration.class, CommonSwaggerConfiguration.class})
@EnableAutoConfiguration @EnableAutoConfiguration
@ComponentScan
public class CustomersCommandSideServiceConfiguration { public class CustomersCommandSideServiceConfiguration {
@@ -24,6 +22,4 @@ public class CustomersCommandSideServiceConfiguration {
HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter(); HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter();
return new HttpMessageConverters(additional); return new HttpMessageConverters(additional);
} }
} }

View File

@@ -13,7 +13,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils.generateCustomerInfo; import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateCustomerInfo;
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = CustomersCommandSideServiceTestConfiguration.class) @SpringApplicationConfiguration(classes = CustomersCommandSideServiceTestConfiguration.class)
@@ -25,7 +25,7 @@ public class CustomersCommandSideServiceIntegrationTest {
private int port; private int port;
private String baseUrl(String path) { private String baseUrl(String path) {
return "http://localhost:" + port + "/" + path; return "http://localhost:" + port + "/api" + path;
} }
@Autowired @Autowired

View File

@@ -1,6 +1,10 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web; package net.chrisrichardson.eventstore.javaexamples.banking.web;
import io.eventuate.javaclient.spring.jdbc.EventuateJdbcEventStoreConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.AuthConfiguration; import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.AuthConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.customers.CustomersCommandSideWebConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters; import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -13,9 +17,16 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
@Configuration @Configuration
@Import({CustomersCommandSideServiceConfiguration.class, AuthConfiguration.class}) @Import({CustomersCommandSideWebConfiguration.class, EventuateJdbcEventStoreConfiguration.class, CommonSwaggerConfiguration.class, AuthConfiguration.class})
@EnableAutoConfiguration
public class CustomersCommandSideServiceTestConfiguration { public class CustomersCommandSideServiceTestConfiguration {
@Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter();
return new HttpMessageConverters(additional);
}
@Bean @Bean
public RestTemplate restTemplate(HttpMessageConverters converters) { public RestTemplate restTemplate(HttpMessageConverters converters) {
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();

View File

@@ -1,7 +1,7 @@
apply plugin: 'java' apply plugin: 'java'
dependencies { dependencies {
compile project(":common-customers") compile project(":common")
compile project(":customers-command-side-backend") compile project(":customers-command-side-backend")
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"

View File

@@ -15,26 +15,26 @@ import java.util.concurrent.CompletableFuture;
* Created by popikyardo on 03.02.16. * Created by popikyardo on 03.02.16.
*/ */
@RestController @RestController
@RequestMapping("/customers") @RequestMapping("/api/customers")
public class CustomerController { public class CustomerController {
private CustomerService customerService; private CustomerService customerService;
@Autowired @Autowired
public CustomerController(CustomerService customerService) { public CustomerController(CustomerService customerService) {
this.customerService = customerService; this.customerService = customerService;
} }
@RequestMapping(method = RequestMethod.POST) @RequestMapping(method = RequestMethod.POST)
public CompletableFuture<CustomerResponse> createCustomer(@Validated @RequestBody CustomerInfo customer) { public CompletableFuture<CustomerResponse> createCustomer(@Validated @RequestBody CustomerInfo customer) {
return customerService.createCustomer(customer) return customerService.createCustomer(customer)
.thenApply(entityAndEventInfo -> new CustomerResponse(entityAndEventInfo.getEntityId(), customer)); .thenApply(entityAndEventInfo -> new CustomerResponse(entityAndEventInfo.getEntityId(), customer));
} }
@RequestMapping(value = "/{id}/toaccounts", method = RequestMethod.POST) @RequestMapping(value = "/{id}/toaccounts", method = RequestMethod.POST)
public CompletableFuture<AddToAccountResponse> addToAccount(@PathVariable String id, @Validated @RequestBody ToAccountInfo request) { public CompletableFuture<AddToAccountResponse> addToAccount(@PathVariable String id, @Validated @RequestBody ToAccountInfo request) {
return customerService.addToAccount(id, request) return customerService.addToAccount(id, request)
.thenApply(entityAndEventInfo -> new AddToAccountResponse(entityAndEventInfo.getEntityVersion().toString())); .thenApply(entityAndEventInfo -> new AddToAccountResponse(entityAndEventInfo.getEntityVersion().toString()));
} }
} }

View File

@@ -23,7 +23,7 @@ public class CustomerQueryService {
return CompletableFuture.completedFuture(customer); return CompletableFuture.completedFuture(customer);
} }
public CompletableFuture<List<QuerySideCustomer>> findByEmail(String email){ public CompletableFuture<List<QuerySideCustomer>> findByEmail(String email) {
List<QuerySideCustomer> customers = querySideCustomerRepository.findByEmailLike(email); List<QuerySideCustomer> customers = querySideCustomerRepository.findByEmailLike(email);
if (customers.isEmpty()) if (customers.isEmpty())
return CompletableFutureUtil.failedFuture(new EmptyResultDataAccessException(1)); return CompletableFutureUtil.failedFuture(new EmptyResultDataAccessException(1));

View File

@@ -15,31 +15,31 @@ import org.slf4j.LoggerFactory;
@EventSubscriber(id = "customerQuerySideEventHandlers") @EventSubscriber(id = "customerQuerySideEventHandlers")
public class CustomerQueryWorkflow { public class CustomerQueryWorkflow {
private Logger logger = LoggerFactory.getLogger(getClass()); private Logger logger = LoggerFactory.getLogger(getClass());
private CustomerInfoUpdateService customerInfoUpdateService; private CustomerInfoUpdateService customerInfoUpdateService;
public CustomerQueryWorkflow(CustomerInfoUpdateService customerInfoUpdateService) { public CustomerQueryWorkflow(CustomerInfoUpdateService customerInfoUpdateService) {
this.customerInfoUpdateService = customerInfoUpdateService; this.customerInfoUpdateService = customerInfoUpdateService;
} }
@EventHandlerMethod @EventHandlerMethod
public void create(DispatchedEvent<CustomerCreatedEvent> de) { public void create(DispatchedEvent<CustomerCreatedEvent> de) {
CustomerCreatedEvent event = de.getEvent(); CustomerCreatedEvent event = de.getEvent();
String id = de.getEntityId(); String id = de.getEntityId();
customerInfoUpdateService.create(id, event.getCustomerInfo()); customerInfoUpdateService.create(id, event.getCustomerInfo());
} }
@EventHandlerMethod @EventHandlerMethod
public void addToAccount(DispatchedEvent<CustomerAddedToAccount> de) { public void addToAccount(DispatchedEvent<CustomerAddedToAccount> de) {
CustomerAddedToAccount event = de.getEvent(); CustomerAddedToAccount event = de.getEvent();
String id = de.getEntityId(); String id = de.getEntityId();
ToAccountInfo toAccountInfo = event.getToAccountInfo(); ToAccountInfo toAccountInfo = event.getToAccountInfo();
customerInfoUpdateService.addToAccount(id, toAccountInfo); customerInfoUpdateService.addToAccount(id, toAccountInfo);
} }
} }

View File

@@ -2,6 +2,7 @@ package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.cu
import io.eventuate.javaclient.spring.EnableEventHandlers; import io.eventuate.javaclient.spring.EnableEventHandlers;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@@ -12,26 +13,27 @@ import org.springframework.data.mongodb.repository.config.EnableMongoRepositorie
@Configuration @Configuration
@EnableMongoRepositories @EnableMongoRepositories
@EnableEventHandlers @EnableEventHandlers
@ComponentScan
public class QuerySideCustomerConfiguration { public class QuerySideCustomerConfiguration {
@Bean @Bean
public CustomerQueryWorkflow customerQueryWorkflow(CustomerInfoUpdateService accountInfoUpdateService) { public CustomerQueryWorkflow customerQueryWorkflow(CustomerInfoUpdateService accountInfoUpdateService) {
return new CustomerQueryWorkflow(accountInfoUpdateService); return new CustomerQueryWorkflow(accountInfoUpdateService);
} }
@Bean @Bean
public CustomerInfoUpdateService customerInfoUpdateService(QuerySideCustomerRepository querySideCustomerRepository) { public CustomerInfoUpdateService customerInfoUpdateService(QuerySideCustomerRepository querySideCustomerRepository) {
return new CustomerInfoUpdateService(querySideCustomerRepository); return new CustomerInfoUpdateService(querySideCustomerRepository);
} }
@Bean @Bean
public CustomerQueryService customerQueryService(QuerySideCustomerRepository accountInfoRepository) { public CustomerQueryService customerQueryService(QuerySideCustomerRepository accountInfoRepository) {
return new CustomerQueryService(accountInfoRepository); return new CustomerQueryService(accountInfoRepository);
} }
@Bean @Bean
public QuerySideDependencyChecker querysideDependencyChecker(MongoTemplate mongoTemplate) { public QuerySideDependencyChecker querysideDependencyChecker(MongoTemplate mongoTemplate) {
return new QuerySideDependencyChecker(mongoTemplate); return new QuerySideDependencyChecker(mongoTemplate);
} }
} }

View File

@@ -7,5 +7,5 @@ import java.util.List;
interface QuerySideCustomerRepository extends MongoRepository<QuerySideCustomer, String> { interface QuerySideCustomerRepository extends MongoRepository<QuerySideCustomer, String> {
List<QuerySideCustomer> findByEmailLike(String email); List<QuerySideCustomer> findByEmailLike(String email);
} }

Some files were not shown because too many files have changed in this diff Show More