118 Commits

Author SHA1 Message Date
Chris Richardson
29d42fda9a Simplified TestUtil, Fixed MongoDB update issue 2016-08-31 13:02:44 -07:00
Chris Richardson
f4e070e7bd Upgraded to Eventuate Local 0.2.0.RELEASE 2016-08-18 13:53:04 -07:00
Chris Richardson
bd3de1a938 Bumped version 2016-08-06 13:26:08 -07:00
Chris Richardson
f570ccbe90 Support for Eventuate Local 2016-08-05 16:41:16 -07:00
Chris Richardson
fe0ce037de Merge branch 'dartpopikyardo-wip-customer' into wip-customer 2016-08-05 15:15:26 -07:00
Chris Richardson
1e40b2a591 Misc changes 2016-08-05 15:14:51 -07:00
Main
f1d97ac49c -updated test configuration 2016-07-26 20:32:58 +03:00
Main
b2b68ce163 Merge remote-tracking branch 'remotes/upstream/wip-eventuate-client-java' into wip-customer 2016-07-25 23:06:05 +03:00
Chris Richardson
e486102018 Upgraded to (new) Eventuate Java client 0.2.0.RELEASE 2016-06-10 16:56:08 -07:00
Chris Richardson
f59f4c78dd Misc changes 2016-05-17 16:26:11 -07:00
Chris Richardson
4b3fe001e7 Changed to use the Eventuate Java Client
Simplified Spring MVC code since it supports CompletableFuture
2016-05-16 06:39:05 -07:00
dartpopikyardo
76f3c830af fix apigateway environment variables 2016-03-29 21:51:17 +03:00
dartpopikyardo
62d41e9d5b Merge remote-tracking branch 'remotes/dartandrevinsky/wip-customer' into wip-customer 2016-03-29 20:30:22 +03:00
Andrew Revinsky (DART)
7748217973 Prebuild client app 2016-03-25 20:19:17 +03:00
dartpopikyardo
9a9511b2c4 changed POST /customers/{id}/toaccounts endpoint result format 2016-03-24 21:37:54 +03:00
Andrew Revinsky (DART)
28eefb81ab UI improvements 2016-03-24 03:17:27 +03:00
Andrew Revinsky (DART)
70b552a961 Sign in page after register, account create dialog dismissal, ref accounts - id undefined, default messages for empty lists (transfers & accounts). 2016-03-24 02:55:43 +03:00
Andrew Revinsky (DART)
2c5b5b4132 Merge commit 'e3c0112e8f85ff2ad4ac1155b0c94c487ea15a83' into wip-customer
* commit 'e3c0112e8f85ff2ad4ac1155b0c94c487ea15a83':
  changed POST /customers/{id}/toaccounts endpoint result format
  changed POST /customers/{id}/toaccounts endpoint result format
2016-03-23 22:15:22 +03:00
dartpopikyardo
e3c0112e8f changed POST /customers/{id}/toaccounts endpoint result format 2016-03-23 22:07:35 +03:00
dartpopikyardo
1a5442a060 changed POST /customers/{id}/toaccounts endpoint result format 2016-03-23 19:21:25 +03:00
Andrew Revinsky (DART)
0b81ae8c08 Sign in - revised error reporting 2016-03-23 01:42:42 +03:00
Andrew Revinsky (DART)
b6b1fb7f0f Adding 3rd Party Account - revised (#2) 2016-03-23 01:30:08 +03:00
Andrew Revinsky (DART)
e3dddcbc7b Merge commit 'e69922016248221d243e8cefa1b6fa813aa07eab' into wip-customer
* commit 'e69922016248221d243e8cefa1b6fa813aa07eab':
  revert
2016-03-23 01:15:41 +03:00
dartpopikyardo
e699220162 revert 2016-03-23 01:13:07 +03:00
Andrew Revinsky (DART)
fe89adba09 Merge commit 'a369c4989f8a238470e6ee7bd1a063f2937da1fc' into wip-customer
* commit 'a369c4989f8a238470e6ee7bd1a063f2937da1fc':
  changed return type in POST /customers/{id}/toaccounts endpoint
2016-03-23 00:57:57 +03:00
dartpopikyardo
a369c4989f changed return type in POST /customers/{id}/toaccounts endpoint 2016-03-23 00:57:09 +03:00
Andrew Revinsky (DART)
dd0bb4551b Merge commit 'bf01ad8e00ea68c884f287b89b25393fe2ef8c7c' into wip-customer
* commit 'bf01ad8e00ea68c884f287b89b25393fe2ef8c7c':
  changed return type in POST /customers/{id}/toaccounts endpoint
2016-03-23 00:23:08 +03:00
Andrew Revinsky (DART)
888544b700 Adding 3rd Party Account - revised 2016-03-23 00:22:52 +03:00
dartpopikyardo
bf01ad8e00 changed return type in POST /customers/{id}/toaccounts endpoint 2016-03-23 00:21:56 +03:00
Andrew Revinsky (DART)
faa4027305 Added timeago + correct refresh of the account page after transaction 2016-03-22 21:05:10 +03:00
Andrew Revinsky (DART)
d55ff55e96 Merge commit '9276b1bdc839d42b39f4bbc866e54835d4469ade' into wip-customer
* commit '9276b1bdc839d42b39f4bbc866e54835d4469ade':
  changed return result for empty accounts case
  fixed tests
  fixed tests
  added new fields to TransferDetails
  added new fields to TransferDetails
  -added tests for new endpoints
2016-03-22 19:51:49 +03:00
Andrew Revinsky (DART)
bdb4d3db26 Refactored reducers 2016-03-22 19:51:00 +03:00
dartpopikyardo
9276b1bdc8 changed return result for empty accounts case 2016-03-22 19:49:09 +03:00
dartpopikyardo
23fba9e462 fixed tests 2016-03-22 15:07:48 +03:00
dartpopikyardo
05ebfd7d14 fixed tests 2016-03-22 15:07:12 +03:00
dartpopikyardo
7b54b9042d added new fields to TransferDetails 2016-03-22 14:50:35 +03:00
dartpopikyardo
7c6328aa5e added new fields to TransferDetails 2016-03-22 14:46:03 +03:00
Andrew Revinsky (DART)
9967ad9c52 Transfer form - adjusted col widths for xs devices 2016-03-22 04:17:34 +03:00
Andrew Revinsky (DART)
8f86f72d85 Transfers (entire patch: list & make transfer) + Specific controls 2016-03-22 04:13:27 +03:00
dartpopikyardo
bdcdae862d -added tests for new endpoints 2016-03-22 01:09:39 +03:00
Andrew Revinsky (DART)
bfeb2e2e16 Add 3rd Party modal customers retrieval 2016-03-19 01:35:00 +03:00
Andrew Revinsky (DART)
b125b30304 Merge commit '40e03dd8e6d805a6f322c25861b3bebda306a947' into wip-customer
* commit '40e03dd8e6d805a6f322c25861b3bebda306a947':
  - updated swagger schema
  - added  GET /accounts/{accountId}/history   endpoint to get transactions history by accountId - updated swagger schema
  - added  GET /accounts/{accountId}/history   endpoint to get transactions history by accountId - updated swagger schema
2016-03-18 21:14:27 +03:00
Andrew Revinsky (DART)
febef227fd Add 3rd Party modal & Account page 2016-03-18 21:03:32 +03:00
dartpopikyardo
40e03dd8e6 - updated swagger schema 2016-03-18 17:37:50 +03:00
dartpopikyardo
63d0e21fc4 - added GET /accounts/{accountId}/history endpoint to get transactions history by accountId
- updated swagger schema
2016-03-18 17:35:18 +03:00
dartpopikyardo
6409ed1ab9 - added GET /accounts/{accountId}/history endpoint to get transactions history by accountId
- updated swagger schema
2016-03-18 17:33:28 +03:00
Andrew Revinsky (DART)
710487ebf9 Account Detail page 2016-03-18 04:20:46 +03:00
Andrew Revinsky (DART)
df0d391521 Merge commit '6d53f149c8c154330d26756d1de73f71d8662061' into wip-customer
* commit '6d53f149c8c154330d26756d1de73f71d8662061':
  - added field "details" to ToAccountInfo
2016-03-16 15:40:27 +03:00
dartpopikyardo
6d53f149c8 - added field "details" to ToAccountInfo 2016-03-16 15:35:59 +03:00
Andrew Revinsky (DART)
3a4ee4e90c Dialog dismissal, accounts fetching (stub) 2016-03-16 02:04:18 +03:00
Andrew Revinsky (DART)
5001127978 Merge commit 'b16039ee5377311aa60f9c71b0a7c1559836b61e' into wip-customer
* commit 'b16039ee5377311aa60f9c71b0a7c1559836b61e':
  - added title and description fields to accountInfo - added GET /accounts?customerId=XXX endpoint - refactored tests
2016-03-16 00:29:47 +03:00
Andrew Revinsky (DART)
ba5077685e Account creation dialog, tidy 2016-03-16 00:29:28 +03:00
dartpopikyardo
b16039ee53 - added title and description fields to accountInfo
- added GET /accounts?customerId=XXX endpoint
- refactored tests
2016-03-15 20:10:58 +03:00
Andrew Revinsky (DART)
c11f1b1a64 Authentication & user data retrieval fixed, API methods provided 2016-03-15 04:17:55 +03:00
Andrew Revinsky (DART)
f4ecc093fe Merge commit '28216a082dc6c6f83f960c412b222a9ade67aacf' into wip-customer
* commit '28216a082dc6c6f83f960c412b222a9ade67aacf':
  find current user by token
2016-03-12 02:53:48 +03:00
Andrew Revinsky (DART)
afa3cf0042 Sign In and Register actions 2016-03-12 02:53:37 +03:00
dartpopikyardo
28216a082d find current user by token 2016-03-11 21:10:50 +03:00
Andrew Revinsky (DART)
5511d1318e Merge commit '21d21b983384188ff2f6ae985caca26e17785087' into wip-customer
* commit '21d21b983384188ff2f6ae985caca26e17785087':
  - simplified customers tests - code clearings
  - updated swagger description - added api-gateway to docker-compose build
  - added api-gateway-service - fixed ObservableReturnValueHandler
2016-03-10 23:58:13 +03:00
Chris Richardson
c5778b1379 Added missing @EnableAutoConfiguration annotations 2016-03-07 17:52:56 -08:00
dartpopikyardo
21d21b9833 - simplified customers tests
- code clearings
2016-03-02 23:54:02 +03:00
dartpopikyardo
4bc8e8408d - updated swagger description
- added api-gateway to docker-compose build
2016-03-01 21:46:37 +03:00
dartpopikyardo
91361a1b18 - added api-gateway-service
- fixed ObservableReturnValueHandler
2016-02-29 21:41:55 +03:00
Andrew Revinsky (DART)
fb1069ebb9 Auth service and MyAccounts show data 2016-02-27 02:06:47 +03:00
Andrew Revinsky (DART)
e337f05c89 Merge commit '0a38ccd09e9d575a372d76597510bfd79502dbd9' into wip-customer
* commit '0a38ccd09e9d575a372d76597510bfd79502dbd9':
  added "/customers/{id}/toaccounts" andpoint test tests refactoring
2016-02-27 00:20:21 +03:00
Andrew Revinsky (DART)
725814407d Auth service receives login data 2016-02-27 00:20:07 +03:00
dartpopikyardo
0a38ccd09e added "/customers/{id}/toaccounts" andpoint test
tests refactoring
2016-02-25 22:35:36 +03:00
Andrew Revinsky (DART)
f887c9fefe Auth services added & user reducer 2016-02-25 21:11:38 +03:00
Andrew Revinsky (DART)
587a9164b7 Account Detail page + Modals 2016-02-20 22:55:10 +03:00
Andrew Revinsky (DART)
b07933121d Merge commit 'd1339729ec3e31d7bb5159640909d9839aa6a45f' into wip-customer
* commit 'd1339729ec3e31d7bb5159640909d9839aa6a45f':
  added simultaneous token-based and http basic authentication
  added simultaneous token-based and http basic authentication
2016-02-19 04:34:57 +03:00
Andrew Revinsky (DART)
98c01cceeb Landing page markup is complete, wiring - WIP 2016-02-19 04:34:30 +03:00
Main
d1339729ec added simultaneous token-based and http basic authentication 2016-02-18 23:57:39 +03:00
Main
99d07667bb added simultaneous token-based and http basic authentication 2016-02-18 13:16:43 +03:00
Main
d36c01b331 added simultaneous token-based and http basic authentication 2016-02-17 21:42:37 +03:00
Main
c61cc82d0a changed error format 2016-02-17 18:42:57 +03:00
Main
65524286da changed error format 2016-02-17 18:36:42 +03:00
Main
99000f04ed Merge remote-tracking branch 'remotes/dartandrevinsky/wip-customer' into wip-customer 2016-02-17 15:07:23 +03:00
Main
7b2fcc8a7c updated swagger info 2016-02-17 15:05:30 +03:00
Main
8615ac1d1e fixed authentication 2016-02-17 14:56:53 +03:00
Andrew Revinsky (DART)
8d1faedef3 Stylize main containers & other controls 2016-02-17 05:11:24 +03:00
Andrew Revinsky (DART)
84debbda28 Update prebuilt client 2016-02-17 00:29:00 +03:00
Andrew Revinsky (DART)
f74196e1f2 Update prebuilt client 2016-02-17 00:28:01 +03:00
Andrew Revinsky (DART)
a73fc2477b Merge commit 'f14fd73f6364ce6623a53074a24d8c49d93921de' into wip-customer
* commit 'f14fd73f6364ce6623a53074a24d8c49d93921de':
  temporarily removed authentication
  temporarily removed authentication
  temporarily removed authentication
2016-02-17 00:16:36 +03:00
Main
f14fd73f63 temporarily removed authentication 2016-02-17 00:14:16 +03:00
Andrew Revinsky (DART)
064151fa61 Integration with the Java server - using hashbangs 2016-02-17 00:12:57 +03:00
Main
43916e10aa temporarily removed authentication 2016-02-17 00:12:38 +03:00
Main
ffeb521e3b temporarily removed authentication 2016-02-16 23:57:02 +03:00
Main
1e2e564c29 fix default authorization page 2016-02-16 23:24:09 +03:00
Main
655f86071c fix static content load 2016-02-16 21:03:38 +03:00
Andrew Revinsky (DART)
ff00467b47 Integration with the Java server 2016-02-16 20:51:10 +03:00
Andrew Revinsky (DART)
28147a707d Merge commit 'e8e147e3ebb0704aff75e4227c2224f7946a681b' into wip-customer
* commit 'e8e147e3ebb0704aff75e4227c2224f7946a681b':
  - added copy static content task
2016-02-16 20:29:47 +03:00
Main
e8e147e3eb - added copy static content task 2016-02-16 20:26:11 +03:00
Andrew Revinsky (DART)
8228738710 Merge commit '8c342e40ce02aa3c3d06ecec3c6c84c1388ef7a1' into wip-customer
* commit '8c342e40ce02aa3c3d06ecec3c6c84c1388ef7a1':
  - added /login endpoint - added authorization test
  Running Docker using Vagrant
2016-02-16 19:59:28 +03:00
Main
8c342e40ce - added /login endpoint
- added authorization test
2016-02-15 23:18:28 +03:00
Andrew Revinsky (DART)
42db215414 Fully functional Sign Up & Sign Incontrols. 2016-02-15 21:58:46 +03:00
Main
23ffa96f47 Merge remote-tracking branches 'remotes/upstream/wip-customer' and 'remotes/upstream/wip-vagrant' into wip-customer 2016-02-15 21:26:53 +03:00
Andrew Revinsky (DART)
822c0efb6d Merge commit 'a217f1b1e6ba53fc90c12507672395ca20920672'
* commit 'a217f1b1e6ba53fc90c12507672395ca20920672':
  Enhanced curl commands Fixed SpringFox/SwaggerUI issue with CustomerController
  Ignore test failures when EVENTUATE_API_KEY_ID etc is not set
  Ignore test failures when EVENTUATE_API_KEY_ID etc is not set
  - updated e2e-tests - added new services to docker configuration
2016-02-15 17:15:28 +03:00
Andrew Revinsky (DART)
5108bc1b27 Prettied UI for the auth controls 2016-02-15 17:09:49 +03:00
Main
8564ec3fb2 Merge remote-tracking branch 'remotes/upstream/master' 2016-02-15 10:27:55 +03:00
Chris Richardson
a217f1b1e6 Enhanced curl commands
Fixed SpringFox/SwaggerUI issue with CustomerController
2016-02-12 17:55:20 -08:00
Chris Richardson
92526f2780 Ignore test failures when EVENTUATE_API_KEY_ID etc is not set 2016-02-12 16:54:18 -08:00
Chris Richardson
8ab13e580b Merge branch 'master' into wip-customer 2016-02-12 16:46:07 -08:00
Main
a095076c0c - updated e2e-tests
- added new services to docker configuration
2016-02-12 22:48:23 +03:00
Andrew Revinsky (DART)
fc2bd9970b prebuilt web app updated 2016-02-12 22:01:58 +03:00
Andrew Revinsky (DART)
a5994ad496 Merge commit '5c85418cc43318eb2a9c3fa1a4c94c124a17676c'
* commit '5c85418cc43318eb2a9c3fa1a4c94c124a17676c':
  - completed moving "toAccounts" from List to Map - created CustomerEvent superclass - removed redundant CustomersNotFoundException - added integration tests for customers
  refactoring
  - updated swagger description - changed customer registration endpoint - updated tests
  - updated swagger description - added /customers/{id}/toaccounts endpoint
  updated swagger description
  removed customerId from Account aggregate
  added customers-query-side part
  added customer-command-side
  fixed fields names added swagger  definition for new endpoints
  added customer backend module added auth module added customerId to Account aggregate
2016-02-12 21:59:58 +03:00
Andrew Revinsky (DART)
bfd04ef726 Changed build process, with auth (login, registration, and couunt) components and containers 2016-02-12 21:59:24 +03:00
Main
5c85418cc4 - completed moving "toAccounts" from List to Map
- created CustomerEvent superclass
- removed redundant CustomersNotFoundException
- added integration tests for customers
2016-02-11 21:47:48 +03:00
Main
c0a9d6ed7d refactoring 2016-02-10 23:28:35 +03:00
Main
92d0940222 - updated swagger description
- changed customer registration endpoint
- updated tests
2016-02-09 22:17:27 +03:00
Andrew Revinsky
7f1f2af188 Adding the frontend project as files 2016-02-08 23:17:37 +03:00
Andrew Revinsky
51e9d6c7fa Removing the remote tree part 2016-02-08 23:16:51 +03:00
Main
cd35ac3d31 - updated swagger description
- added /customers/{id}/toaccounts endpoint
2016-02-08 22:54:03 +03:00
Andrew Revinsky
5d7e56578f Add client app folder 2016-02-08 21:41:35 +03:00
Main
59ecaa804a updated swagger description 2016-02-08 13:25:09 +03:00
Main
06f14a54bf removed customerId from Account aggregate
added tests to customers-query-side and customers-command-side
2016-02-05 20:11:53 +03:00
Main
afb7c9bc49 added customers-query-side part
added service wrappers to customers-command-side and customers-query-side
2016-02-05 01:27:17 +03:00
dartpopikyardo
e0691a61a2 added customer-command-side 2016-02-03 23:04:15 +03:00
dartpopikyardo
cbee50658e fixed fields names
added swagger  definition for new endpoints
2016-02-03 21:03:52 +03:00
dartpopikyardo
85f8826741 added customer backend module
added auth module
added customerId to Account aggregate
2016-02-02 19:16:29 +03:00
272 changed files with 10594 additions and 723 deletions

6
.gitignore vendored
View File

@@ -25,6 +25,12 @@ browserapp/reports
loginapp/reports
local_developer_config
js-frontend/node_modules
js-frontend/build
js-frontend/dist
js-frontend/dist-intermediate
/web/web.log
*.log

View File

@@ -6,6 +6,12 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DOCKER_COMPOSE="docker-compose -p event-sourcing-examples"
if [ "$1" = "-f" ] ; then
shift;
DOCKER_COMPOSE="$DOCKER_COMPOSE -f ${1?}"
shift
fi
if [ "$1" = "--use-existing" ] ; then
shift;
else
@@ -13,6 +19,13 @@ else
${DOCKER_COMPOSE?} rm -v --force
fi
NO_RM=false
if [ "$1" = "--no-rm" ] ; then
NO_RM=true
shift
fi
${DOCKER_COMPOSE?} up -d mongodb
if [ -z "$DOCKER_HOST_IP" ] ; then
@@ -47,5 +60,7 @@ set -e
./gradlew -a $* :e2e-test:cleanTest :e2e-test:test -P ignoreE2EFailures=false
${DOCKER_COMPOSE?} stop
${DOCKER_COMPOSE?} rm -v --force
if [ $NO_RM = false ] ; then
${DOCKER_COMPOSE?} stop
${DOCKER_COMPOSE?} rm -v --force
fi

View File

@@ -3,12 +3,12 @@ apply plugin: 'java'
dependencies {
compile project(":common-backend")
compile "net.chrisrichardson.eventstore.client:eventstore-java-client_2.10:$eventStoreClientVersion"
compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion"
testCompile project(":testutil")
testCompile "junit:junit:4.11"
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
testCompile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion"
testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"
}

View File

@@ -1,8 +1,8 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts;
import net.chrisrichardson.eventstore.Event;
import net.chrisrichardson.eventstore.EventUtil;
import net.chrisrichardson.eventstore.ReflectiveMutableCommandProcessingAggregate;
import io.eventuate.Event;
import io.eventuate.EventUtil;
import io.eventuate.ReflectiveMutableCommandProcessingAggregate;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountCreditedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountDebitFailedDueToInsufficientFundsEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountDebitedEvent;
@@ -16,7 +16,7 @@ public class Account extends ReflectiveMutableCommandProcessingAggregate<Account
private BigDecimal balance;
public List<Event> process(OpenAccountCommand cmd) {
return EventUtil.events(new AccountOpenedEvent(cmd.getInitialBalance()));
return EventUtil.events(new AccountOpenedEvent(cmd.getCustomerId(), cmd.getTitle(), cmd.getInitialBalance(), cmd.getDescription()));
}
public List<Event> process(DebitAccountCommand cmd) {

View File

@@ -1,6 +1,7 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts;
import net.chrisrichardson.eventstore.Command;
import io.eventuate.Command;
interface AccountCommand extends Command {
}

View File

@@ -1,13 +1,13 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts;
import net.chrisrichardson.eventstore.EventStore;
import net.chrisrichardson.eventstore.javaapi.consumer.EnableJavaEventHandlers;
import net.chrisrichardson.eventstore.repository.AggregateRepository;
import io.eventuate.AggregateRepository;
import io.eventuate.EventuateAggregateStore;
import io.eventuate.javaclient.spring.EnableEventHandlers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableJavaEventHandlers
@EnableEventHandlers
public class AccountConfiguration {
@Bean
@@ -22,7 +22,7 @@ public class AccountConfiguration {
}
@Bean
public AggregateRepository<Account, AccountCommand> accountRepository(EventStore eventStore) {
public AggregateRepository<Account, AccountCommand> accountRepository(EventuateAggregateStore eventStore) {
return new AggregateRepository<Account, AccountCommand>(Account.class, eventStore);
}

View File

@@ -1,10 +1,11 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts;
import net.chrisrichardson.eventstore.EntityWithIdAndVersion;
import net.chrisrichardson.eventstore.repository.AggregateRepository;
import io.eventuate.AggregateRepository;
import io.eventuate.EntityWithIdAndVersion;
import java.math.BigDecimal;
import java.util.concurrent.CompletableFuture;
public class AccountService {
@@ -14,8 +15,8 @@ public class AccountService {
this.accountRepository = accountRepository;
}
public rx.Observable<EntityWithIdAndVersion<Account>> openAccount(BigDecimal initialBalance) {
return accountRepository.save(new OpenAccountCommand(initialBalance));
public CompletableFuture<EntityWithIdAndVersion<Account>> openAccount(String customerId, String title, BigDecimal initialBalance, String description) {
return accountRepository.save(new OpenAccountCommand(customerId, title, initialBalance, description));
}
}

View File

@@ -1,34 +1,36 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts;
import net.chrisrichardson.eventstore.EntityIdentifier;
import net.chrisrichardson.eventstore.javaapi.consumer.EventHandlerContext;
import io.eventuate.EntityWithIdAndVersion;
import io.eventuate.EventHandlerContext;
import io.eventuate.EventHandlerMethod;
import io.eventuate.EventSubscriber;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.DebitRecordedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.MoneyTransferCreatedEvent;
import net.chrisrichardson.eventstore.subscriptions.*;
import rx.Observable;
import java.math.BigDecimal;
import java.util.concurrent.CompletableFuture;
@EventSubscriber(id="accountEventHandlers")
public class AccountWorkflow implements CompoundEventHandler {
public class AccountWorkflow {
@EventHandlerMethod
public Observable<?> debitAccount(EventHandlerContext<MoneyTransferCreatedEvent> ctx) {
public CompletableFuture<?> debitAccount(EventHandlerContext<MoneyTransferCreatedEvent> ctx) {
MoneyTransferCreatedEvent event = ctx.getEvent();
BigDecimal amount = event.getDetails().getAmount();
EntityIdentifier transactionId = ctx.getEntityIdentifier();
String transactionId = ctx.getEntityId();
EntityIdentifier fromAccountId = event.getDetails().getFromAccountId();
String fromAccountId = event.getDetails().getFromAccountId();
return ctx.update(Account.class, fromAccountId, new DebitAccountCommand(amount, transactionId));
}
@EventHandlerMethod
public Observable<?> creditAccount(EventHandlerContext<DebitRecordedEvent> ctx) {
public CompletableFuture<EntityWithIdAndVersion<Account>> creditAccount(EventHandlerContext<DebitRecordedEvent> ctx) {
DebitRecordedEvent event = ctx.getEvent();
BigDecimal amount = event.getDetails().getAmount();
EntityIdentifier fromAccountId = event.getDetails().getToAccountId();
EntityIdentifier transactionId = ctx.getEntityIdentifier();
String fromAccountId = event.getDetails().getToAccountId();
String transactionId = ctx.getEntityId();
return ctx.update(Account.class, fromAccountId, new CreditAccountCommand(amount, transactionId));
}

View File

@@ -1,15 +1,15 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts;
import net.chrisrichardson.eventstore.Aggregate;
import net.chrisrichardson.eventstore.EntityIdentifier;
import io.eventuate.Aggregate;
import java.math.BigDecimal;
public class CreditAccountCommand implements AccountCommand {
private final BigDecimal amount;
private final EntityIdentifier transactionId;
private final String transactionId;
public CreditAccountCommand(BigDecimal amount, EntityIdentifier transactionId) {
public CreditAccountCommand(BigDecimal amount, String transactionId) {
this.amount = amount;
this.transactionId = transactionId;
@@ -19,7 +19,7 @@ public class CreditAccountCommand implements AccountCommand {
return amount;
}
public EntityIdentifier getTransactionId() {
public String getTransactionId() {
return transactionId;
}
}

View File

@@ -1,15 +1,12 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts;
import net.chrisrichardson.eventstore.Aggregate;
import net.chrisrichardson.eventstore.EntityIdentifier;
import java.math.BigDecimal;
public class DebitAccountCommand implements AccountCommand {
private final BigDecimal amount;
private final EntityIdentifier transactionId;
private final String transactionId;
public DebitAccountCommand(BigDecimal amount, EntityIdentifier transactionId) {
public DebitAccountCommand(BigDecimal amount, String transactionId) {
this.amount = amount;
this.transactionId = transactionId;
@@ -19,7 +16,7 @@ public class DebitAccountCommand implements AccountCommand {
return amount;
}
public EntityIdentifier getTransactionId() {
public String getTransactionId() {
return transactionId;
}
}

View File

@@ -5,13 +5,31 @@ import java.math.BigDecimal;
public class OpenAccountCommand implements AccountCommand {
private String customerId;
private String title;
private BigDecimal initialBalance;
private String description;
public OpenAccountCommand(BigDecimal initialBalance) {
public OpenAccountCommand(String customerId, String title, BigDecimal initialBalance, String description) {
this.customerId = customerId;
this.title = title;
this.initialBalance = initialBalance;
this.description = description;
}
public BigDecimal getInitialBalance() {
return initialBalance;
}
public String getCustomerId() {
return customerId;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
}

View File

@@ -1,7 +1,6 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts;
import net.chrisrichardson.eventstore.CommandProcessingAggregates;
import net.chrisrichardson.eventstore.Event;
import io.eventuate.Event;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountOpenedEvent;
import org.junit.Assert;
import org.junit.Test;
@@ -14,14 +13,16 @@ public class AccountTest {
@Test
public void testSomething() {
Account account = new Account();
String title = "My Account";
String customerId = "00000000-00000000";
BigDecimal initialBalance = new BigDecimal(512);
List<Event> events = CommandProcessingAggregates.processToList(account, (AccountCommand)new OpenAccountCommand(initialBalance));
List<Event> events = account.process(new OpenAccountCommand(customerId, title, initialBalance, ""));
Assert.assertEquals(1, events.size());
Assert.assertEquals(AccountOpenedEvent.class, events.get(0).getClass());
account.applyEvent(events.get(0));
Assert.assertEquals(initialBalance, account.getBalance());
}
}

View File

@@ -1,5 +1,6 @@
apply plugin: VerifyMongoDBConfigurationPlugin
apply plugin: VerifyEventStoreEnvironmentPlugin
apply plugin: EventuateDependencyPlugin
apply plugin: 'spring-boot'
@@ -10,9 +11,6 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "net.chrisrichardson.eventstore.client:eventstore-http-stomp-client_2.10:$eventStoreClientVersion"
testCompile "org.springframework.boot:spring-boot-starter-test"
}

View File

@@ -1,6 +1,6 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web;
import net.chrisrichardson.eventstore.client.config.EventStoreHttpClientConfiguration;
import io.eventuate.javaclient.spring.httpstomp.EventuateHttpStompClientConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CommandSideWebAccountsConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -13,7 +13,7 @@ import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
@Configuration
@Import({CommandSideWebAccountsConfiguration.class, EventStoreHttpClientConfiguration.class, CommonSwaggerConfiguration.class})
@Import({CommandSideWebAccountsConfiguration.class, EventuateHttpStompClientConfiguration.class, CommonSwaggerConfiguration.class})
@EnableAutoConfiguration
@ComponentScan
public class AccountsCommandSideServiceConfiguration {

View File

@@ -37,11 +37,13 @@ public class AccountsCommandSideServiceIntegrationTest {
BigDecimal initialFromAccountBalance = new BigDecimal(500);
BigDecimal initialToAccountBalance = new BigDecimal(100);
BigDecimal amountToTransfer = new BigDecimal(150);
String customerId = "00000000-00000000";
String title = "My Account";
final CreateAccountResponse fromAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest(initialFromAccountBalance), CreateAccountResponse.class).getBody();
final CreateAccountResponse fromAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest(customerId, title, "", initialFromAccountBalance), CreateAccountResponse.class).getBody();
final String fromAccountId = fromAccount.getAccountId();
CreateAccountResponse toAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest(initialToAccountBalance), CreateAccountResponse.class).getBody();
CreateAccountResponse toAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest(customerId, title, "", initialToAccountBalance), CreateAccountResponse.class).getBody();
String toAccountId = toAccount.getAccountId();
Assert.assertNotNull(fromAccountId);

View File

@@ -1,12 +1,11 @@
dependencies {
compile project(":accounts-command-side-backend")
compile project(":common-web")
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
testCompile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion"
testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"
}

View File

@@ -7,7 +7,8 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import rx.Observable;
import java.util.concurrent.CompletableFuture;
@RestController
@RequestMapping("/accounts")
@@ -21,8 +22,8 @@ public class AccountController {
}
@RequestMapping(method = RequestMethod.POST)
public Observable<CreateAccountResponse> createAccount(@Validated @RequestBody CreateAccountRequest request) {
return accountService.openAccount(request.getInitialBalance())
.map(entityAndEventInfo -> new CreateAccountResponse(entityAndEventInfo.getEntityIdentifier().getId()));
public CompletableFuture<CreateAccountResponse> createAccount(@Validated @RequestBody CreateAccountRequest request) {
return accountService.openAccount(request.getCustomerId(), request.getTitle(), request.getInitialBalance(), request.getDescription())
.thenApply(entityAndEventInfo -> new CreateAccountResponse(entityAndEventInfo.getEntityId()));
}
}

View File

@@ -1,36 +1,16 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.AccountConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.util.ObservableReturnValueHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Bean;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
@Configuration
@Import({AccountConfiguration.class})
@ComponentScan
public class CommandSideWebAccountsConfiguration extends WebMvcConfigurerAdapter {
@EnableAutoConfiguration
public class CommandSideWebAccountsConfiguration {
class FakeThing {}
@Bean
public FakeThing init(RequestMappingHandlerAdapter adapter) {
// https://jira.spring.io/browse/SPR-13083
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>(adapter.getReturnValueHandlers());
handlers.add(0, new ObservableReturnValueHandler());
adapter.setReturnValueHandlers(handlers);
return new FakeThing();
}
}

View File

@@ -7,6 +7,13 @@ import java.math.BigDecimal;
public class CreateAccountRequest {
@NotNull
private String customerId;
private String title;
private String description;
@NotNull
@DecimalMin("0")
private BigDecimal initialBalance;
@@ -14,11 +21,21 @@ public class CreateAccountRequest {
public CreateAccountRequest() {
}
public CreateAccountRequest(BigDecimal initialBalance) {
public CreateAccountRequest(String customerId, String title, String description, BigDecimal initialBalance) {
this.customerId = customerId;
this.title = title;
this.description = description;
this.initialBalance = initialBalance;
}
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
public BigDecimal getInitialBalance() {
return initialBalance;
}
@@ -26,4 +43,20 @@ public class CreateAccountRequest {
public void setInitialBalance(BigDecimal initialBalance) {
this.initialBalance = initialBalance;
}
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

@@ -37,7 +37,7 @@ public class AccountControllerIntegrationTest {
public void shouldCreateAccount() throws Exception {
mockMvc.perform(post("/accounts")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"initialBalance\" : 500}")
.content("{\"customerId\" : \"00000000-00000000\", \"initialBalance\" : 500}")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}

View File

@@ -1,11 +1,11 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts;
import net.chrisrichardson.eventstore.jdbc.config.JdbcEventStoreConfiguration;
import io.eventuate.javaclient.spring.jdbc.EventuateJdbcEventStoreConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({CommandSideWebAccountsConfiguration.class, JdbcEventStoreConfiguration.class})
@Import({CommandSideWebAccountsConfiguration.class, EventuateJdbcEventStoreConfiguration.class})
public class AccountControllerIntegrationTestConfiguration {
}

View File

@@ -3,17 +3,13 @@ apply plugin: 'java'
dependencies {
compile project(":common-backend")
compile "net.chrisrichardson.eventstore.client:eventstore-java-client_2.10:$eventStoreClientVersion"
compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion"
compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion"
compile 'com.fasterxml.jackson.core:jackson-core:2.4.3'
compile 'com.fasterxml.jackson.core:jackson-databind:2.4.3'
compile 'com.fasterxml.jackson.module:jackson-module-scala_2.10:2.4.3'
testCompile project(":testutil")
testCompile "junit:junit:4.11"
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
testCompile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion"
testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"
}

View File

@@ -1,5 +1,8 @@
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;
@@ -15,4 +18,14 @@ public class AccountChangeInfo {
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,6 +1,9 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Created by cer on 11/21/14.
@@ -8,17 +11,23 @@ import java.util.List;
public class AccountInfo {
private String id;
private String customerId;
private String title;
private String description;
private long balance;
private List<AccountChangeInfo> changes;
private List<AccountTransactionInfo> transactions;
private Map<String, AccountTransactionInfo> transactions;
private String version;
private AccountInfo() {
}
public AccountInfo(String id, long balance, List<AccountChangeInfo> changes, List<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 = id;
this.customerId = customerId;
this.title = title;
this.description = description;
this.balance = balance;
this.changes = changes;
this.transactions = transactions;
@@ -29,16 +38,28 @@ public class AccountInfo {
return id;
}
public String getCustomerId() {
return customerId;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public long getBalance() {
return balance;
}
public List<AccountChangeInfo> getChanges() {
return changes;
return changes == null ? Collections.EMPTY_LIST : changes;
}
public List<AccountTransactionInfo> getTransactions() {
return transactions;
return transactions == null ? Collections.EMPTY_LIST : new ArrayList<>(transactions.values());
}
public String getVersion() {

View File

@@ -2,5 +2,9 @@ package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.ac
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
interface AccountInfoRepository extends MongoRepository<AccountInfo, String> {
List<AccountInfo> findByCustomerId(String customerId);
}

View File

@@ -3,6 +3,7 @@ package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.ac
import com.mongodb.WriteResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
@@ -16,38 +17,39 @@ import static org.springframework.data.mongodb.core.query.Criteria.where;
public class AccountInfoUpdateService {
private Logger logger = LoggerFactory.getLogger(getClass());
private AccountInfoRepository accountInfoRepository;
private MongoTemplate mongoTemplate;
public AccountInfoUpdateService(AccountInfoRepository accountInfoRepository, MongoTemplate mongoTemplate) {
this.accountInfoRepository = accountInfoRepository;
public AccountInfoUpdateService(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
public void create(String accountId, BigDecimal initialBalance, String version) {
public void create(String accountId, String customerId, String title, BigDecimal initialBalance, String description, String version) {
try {
accountInfoRepository.save(new AccountInfo(
accountId,
toIntegerRepr(initialBalance),
Collections.<AccountChangeInfo>emptyList(),
Collections.<AccountTransactionInfo>emptyList(),
version));
WriteResult x = mongoTemplate.upsert(new Query(where("id").is(accountId).and("version").exists(false)),
new Update()
.set("customerId", customerId)
.set("title", title)
.set("description", description)
.set("balance", toIntegerRepr(initialBalance))
.set("version", version),
AccountInfo.class);
logger.info("Saved in mongo");
} catch (DuplicateKeyException t) {
logger.warn("When saving ", t);
} catch (Throwable t) {
logger.error("Error during saving: ");
logger.error("Error during saving: ", t);
throw new RuntimeException(t);
}
}
public void addTransaction(String eventId, String fromAccountId, AccountTransactionInfo ti) {
mongoTemplate.updateMulti(new Query(where("id").is(fromAccountId).and("version").lt(eventId)),
public void addTransaction(String eventId, String accountId, AccountTransactionInfo ti) {
mongoTemplate.upsert(new Query(where("id").is(accountId)),
new Update().
push("transactions", ti).
set("version", eventId),
set("transactions." + eventId, ti),
AccountInfo.class);
}

View File

@@ -1,9 +1,9 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import net.chrisrichardson.eventstore.Aggregate;
import net.chrisrichardson.eventstore.EntityIdentifier;
import net.chrisrichardson.eventstore.EntityNotFoundException;
import rx.Observable;
import io.eventuate.CompletableFutureUtil;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class AccountQueryService {
@@ -13,11 +13,15 @@ public class AccountQueryService {
this.accountInfoRepository = accountInfoRepository;
}
public Observable<AccountInfo> findByAccountId(EntityIdentifier accountId) {
AccountInfo account = accountInfoRepository.findOne(accountId.getId());
public CompletableFuture<AccountInfo> findByAccountId(String accountId) {
AccountInfo account = accountInfoRepository.findOne(accountId);
if (account == null)
return Observable.error(new AccountNotFoundException(accountId.getId()));
return CompletableFutureUtil.failedFuture(new AccountNotFoundException(accountId));
else
return Observable.just(account);
return CompletableFuture.completedFuture(account);
}
public CompletableFuture<List<AccountInfo>> findByCustomerId(String customerId) {
return CompletableFuture.completedFuture(accountInfoRepository.findByCustomerId(customerId));
}
}

View File

@@ -1,17 +1,13 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import net.chrisrichardson.eventstore.EntityIdentifier;
import io.eventuate.DispatchedEvent;
import io.eventuate.EventHandlerMethod;
import io.eventuate.EventSubscriber;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountChangedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountCreditedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountDebitedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountOpenedEvent;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.MoneyTransferCreatedEvent;
import net.chrisrichardson.eventstore.subscriptions.CompoundEventHandler;
import net.chrisrichardson.eventstore.subscriptions.DispatchedEvent;
import net.chrisrichardson.eventstore.subscriptions.EventHandlerMethod;
import net.chrisrichardson.eventstore.subscriptions.EventSubscriber;
import rx.Observable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -19,9 +15,9 @@ import java.math.BigDecimal;
import static net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.MoneyUtil.toIntegerRepr;
@EventSubscriber(id="querySideEventHandlers")
public class AccountQueryWorkflow implements CompoundEventHandler {
@EventSubscriber(id="querySideEventHandlers")
public class AccountQueryWorkflow {
private Logger logger = LoggerFactory.getLogger(getClass());
private AccountInfoUpdateService accountInfoUpdateService;
@@ -31,59 +27,60 @@ public class AccountQueryWorkflow implements CompoundEventHandler {
}
@EventHandlerMethod
public Observable<Object> create(DispatchedEvent<AccountOpenedEvent> de) {
AccountOpenedEvent event = de.event();
String id = de.getEntityIdentifier().getId();
String eventId = de.eventId().asString();
public void create(DispatchedEvent<AccountOpenedEvent> de) {
AccountOpenedEvent event = de.getEvent();
String id = de.getEntityId();
String eventId = de.getEventId().asString();
logger.info("**************** account version=" + id + ", " + eventId);
BigDecimal initialBalance = event.getInitialBalance();
accountInfoUpdateService.create(id, initialBalance, eventId);
return Observable.just(null);
String customerId = event.getCustomerId();
String title = event.getTitle();
String description = event.getDescription();
accountInfoUpdateService.create(id, customerId, title, initialBalance, description, eventId);
}
@EventHandlerMethod
public Observable<Object> recordTransfer(DispatchedEvent<MoneyTransferCreatedEvent> de) {
String eventId = de.eventId().asString();
String moneyTransferId = de.getEntityIdentifier().getId();
String fromAccountId = de.event().getDetails().getFromAccountId().getId();
String toAccountId = de.event().getDetails().getToAccountId().getId();
logger.info("**************** account version=" + fromAccountId + ", " + de.eventId().asString());
logger.info("**************** account version=" + toAccountId + ", " + de.eventId().asString());
AccountTransactionInfo ti = new AccountTransactionInfo(moneyTransferId, fromAccountId, toAccountId, toIntegerRepr(de.event().getDetails().getAmount()));
public void recordTransfer(DispatchedEvent<MoneyTransferCreatedEvent> de) {
String eventId = de.getEventId().asString();
String moneyTransferId = de.getEntityId();
String fromAccountId = de.getEvent().getDetails().getFromAccountId();
String toAccountId = de.getEvent().getDetails().getToAccountId();
logger.info("**************** account version=" + fromAccountId + ", " + de.getEventId().asString());
logger.info("**************** account version=" + toAccountId + ", " + de.getEventId().asString());
AccountTransactionInfo ti = new AccountTransactionInfo(moneyTransferId,
fromAccountId,
toAccountId,
toIntegerRepr(de.getEvent().getDetails().getAmount()),
de.getEvent().getDetails().getDate(),
de.getEvent().getDetails().getDescription());
accountInfoUpdateService.addTransaction(eventId, fromAccountId, ti);
accountInfoUpdateService.addTransaction(eventId, toAccountId, ti);
return Observable.just(null);
}
@EventHandlerMethod
public Observable<Object> recordDebit(DispatchedEvent<AccountDebitedEvent> de) {
return saveChange(de, -1);
public void recordDebit(DispatchedEvent<AccountDebitedEvent> de) {
saveChange(de, -1);
}
@EventHandlerMethod
public Observable<Object> recordCredit(DispatchedEvent<AccountCreditedEvent> de) {
return saveChange(de, +1);
public void recordCredit(DispatchedEvent<AccountCreditedEvent> de) {
saveChange(de, +1);
}
public <T extends AccountChangedEvent> Observable<Object> saveChange(DispatchedEvent<T> de, int delta) {
String changeId = de.eventId().asString();
String transactionId = de.event().getTransactionId().getId();
long amount = toIntegerRepr(de.event().getAmount());
public <T extends AccountChangedEvent> void saveChange(DispatchedEvent<T> de, int delta) {
String changeId = de.getEventId().asString();
String transactionId = de.getEvent().getTransactionId();
long amount = toIntegerRepr(de.getEvent().getAmount());
long balanceDelta = amount * delta;
AccountChangeInfo ci = new AccountChangeInfo(changeId, transactionId, de.event().getClass().getSimpleName(), amount, balanceDelta);
String accountId = de.getEntityIdentifier().getId();
logger.info("**************** account version=" + accountId + ", " + de.eventId().asString());
AccountChangeInfo ci = new AccountChangeInfo(changeId, transactionId, de.getEvent().getClass().getSimpleName(), amount, balanceDelta);
String accountId = de.getEntityId();
logger.info("**************** account version=" + accountId + ", " + de.getEventId().asString());
accountInfoUpdateService.updateBalance(accountId, changeId, balanceDelta, ci);
return Observable.just(null);
}
}

View File

@@ -1,16 +1,97 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import java.util.Date;
public class AccountTransactionInfo {
private String transactionId;
private String fromAccountId;
private String toAccountId;
private long amount;
private Date date;
private String description;
public AccountTransactionInfo() {
}
public AccountTransactionInfo(String transactionId, String fromAccountId, String toAccountId, long amount) {
this(transactionId, fromAccountId, toAccountId, amount, new Date(), "");
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
public AccountTransactionInfo(String transactionId, String fromAccountId, String toAccountId, long amount, Date date, String description) {
this.transactionId = transactionId;
this.fromAccountId = fromAccountId;
this.toAccountId = toAccountId;
this.amount = amount;
this.date = date;
this.description = description;
}
public String getTransactionId() {
return transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
public String getFromAccountId() {
return fromAccountId;
}
public void setFromAccountId(String fromAccountId) {
this.fromAccountId = fromAccountId;
}
public String getToAccountId() {
return toAccountId;
}
public void setToAccountId(String toAccountId) {
this.toAccountId = toAccountId;
}
public long getAmount() {
return amount;
}
public void setAmount(long amount) {
this.amount = amount;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
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,7 +1,7 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import net.chrisrichardson.eventstore.javaapi.consumer.EnableJavaEventHandlers;
import io.eventuate.javaclient.spring.EnableEventHandlers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -9,7 +9,7 @@ import org.springframework.data.mongodb.repository.config.EnableMongoRepositorie
@Configuration
@EnableMongoRepositories
@EnableJavaEventHandlers
@EnableEventHandlers
public class QuerySideAccountConfiguration {
@Bean
@@ -18,8 +18,8 @@ public class QuerySideAccountConfiguration {
}
@Bean
public AccountInfoUpdateService accountInfoUpdateService(AccountInfoRepository accountInfoRepository, MongoTemplate mongoTemplate) {
return new AccountInfoUpdateService(accountInfoRepository, mongoTemplate);
public AccountInfoUpdateService accountInfoUpdateService(MongoTemplate mongoTemplate) {
return new AccountInfoUpdateService(mongoTemplate);
}
@Bean

View File

@@ -3,8 +3,6 @@ package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.ac
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import rx.Observable;
import rx.Subscriber;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
@@ -22,17 +20,7 @@ public class QuerySideDependencyChecker {
try {
logger.info("Checking mongodb connectivity {}", System.getenv("SPRING_DATA_MONGODB_URI"));
Observable.<Object>create(new Observable.OnSubscribe<Object>() {
@Override
public void call(Subscriber<? super Object> subscriber) {
try {
subscriber.onNext(mongoTemplate.getDb().getCollectionNames());
subscriber.onCompleted();
} catch (Throwable t) {
subscriber.onError(t);
}
}
}).timeout(5, TimeUnit.SECONDS).toBlocking().first();
mongoTemplate.getDb().getCollectionNames();
} catch (Throwable e) {
throw new RuntimeException("Error connecting to Mongo - have you set SPRING_DATA_MONGODB_URI or --spring.data.mongodb_uri?", e);

View File

@@ -0,0 +1,112 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import io.eventuate.javaclient.spring.jdbc.EventuateJdbcEventStoreConfiguration;
import io.eventuate.javaclient.spring.jdbc.IdGenerator;
import io.eventuate.javaclient.spring.jdbc.IdGeneratorImpl;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountCreditedEvent;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import java.math.BigDecimal;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = AccountInfoUpdateServiceTest.AccountInfoUpdateServiceTestConfiguration.class)
@IntegrationTest
public class AccountInfoUpdateServiceTest {
@Configuration
@EnableAutoConfiguration
@Import({QuerySideAccountConfiguration.class, EventuateJdbcEventStoreConfiguration.class})
public static class AccountInfoUpdateServiceTestConfiguration {
}
@Autowired
private AccountInfoUpdateService accountInfoUpdateService;
@Autowired
private AccountQueryService accountQueryService;
@Test
public void shouldSaveAccountInfo() throws ExecutionException, InterruptedException {
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);
AccountInfo accountInfo = accountQueryService.findByAccountId(accountId).get();
assertEquals(accountId, accountInfo.getId());
assertEquals(customerId, accountInfo.getCustomerId());
assertEquals(title, accountInfo.getTitle());
assertEquals(description, accountInfo.getDescription());
assertEquals(initialBalance.longValue() * 100, accountInfo.getBalance());
assertTrue(accountInfo.getChanges().isEmpty());
assertTrue(accountInfo.getTransactions().isEmpty());
assertEquals(version, accountInfo.getVersion());
String changeId = x.genId().asString();
String transactionId = x.genId().asString();
AccountChangeInfo change = new AccountChangeInfo(changeId, transactionId, AccountCreditedEvent.class.getSimpleName(),
500, +1);
accountInfoUpdateService.updateBalance(accountId, changeId, 500,
change);
accountInfo = accountQueryService.findByAccountId(accountId).get();
assertEquals(initialBalance.add(new BigDecimal(5)).longValue() * 100, accountInfo.getBalance());
assertFalse(accountInfo.getChanges().isEmpty());
assertEquals(change, accountInfo.getChanges().get(0));
String eventId = x.genId().asString();
AccountTransactionInfo ti = new AccountTransactionInfo(transactionId, accountId, accountId, 5, new Date(), "A transfer");
accountInfoUpdateService.addTransaction(eventId, accountId, ti);
accountInfo = accountQueryService.findByAccountId(accountId).get();
assertFalse(accountInfo.getTransactions().isEmpty());
assertEquals(ti, accountInfo.getTransactions().get(0));
}
@Test
public void shouldHandleDuplicateSaveAccountInfo() throws ExecutionException, InterruptedException {
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);
accountInfoUpdateService.create(accountId, customerId, title, initialBalance, description, version);
}
}

View File

@@ -1,5 +1,6 @@
apply plugin: VerifyMongoDBConfigurationPlugin
apply plugin: VerifyEventStoreEnvironmentPlugin
apply plugin: EventuateDependencyPlugin
apply plugin: 'spring-boot'
@@ -10,8 +11,6 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "net.chrisrichardson.eventstore.client:eventstore-http-stomp-client_2.10:$eventStoreClientVersion"
testCompile project(":testutil")
testCompile "org.springframework.boot:spring-boot-starter-test"

View File

@@ -1,6 +1,6 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web;
import net.chrisrichardson.eventstore.client.config.EventStoreHttpClientConfiguration;
import io.eventuate.javaclient.spring.httpstomp.EventuateHttpStompClientConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.QuerySideWebConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -13,7 +13,7 @@ import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
@Configuration
@Import({QuerySideWebConfiguration.class, EventStoreHttpClientConfiguration.class, CommonSwaggerConfiguration.class})
@Import({QuerySideWebConfiguration.class, EventuateHttpStompClientConfiguration.class, CommonSwaggerConfiguration.class})
@EnableAutoConfiguration
@ComponentScan
public class AccountsQuerySideServiceConfiguration {

View File

@@ -13,10 +13,10 @@ import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.util.concurrent.CompletableFuture;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import rx.Observable;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
@@ -51,8 +51,8 @@ public class AccountsQuerySideServiceIntegrationTest {
eventually(
new Producer<GetAccountResponse>() {
@Override
public Observable<GetAccountResponse> produce() {
return Observable.just(restTemplate.getForEntity(baseUrl("/accounts/" + fromAccountId), GetAccountResponse.class).getBody());
public CompletableFuture<GetAccountResponse> produce() {
return CompletableFuture.completedFuture(restTemplate.getForEntity(baseUrl("/accounts/" + fromAccountId), GetAccountResponse.class).getBody());
}
},
new Verifier<GetAccountResponse>() {

View File

@@ -1,7 +1,6 @@
dependencies {
compile project(":accounts-query-side-backend")
compile project(":common-web")
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"

View File

@@ -1,36 +1,14 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.queryside;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.QuerySideAccountConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.util.ObservableReturnValueHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Bean;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
@Configuration
@Import({QuerySideAccountConfiguration.class})
@ComponentScan
public class QuerySideWebConfiguration extends WebMvcConfigurerAdapter {
public class QuerySideWebConfiguration {
class FakeThing {}
@Bean
public FakeThing init(RequestMappingHandlerAdapter adapter) {
// https://jira.spring.io/browse/SPR-13083
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>(adapter.getReturnValueHandlers());
handlers.add(0, new ObservableReturnValueHandler());
adapter.setReturnValueHandlers(handlers);
return new FakeThing();
}
}

View File

@@ -1,14 +1,18 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.accounts;
import net.chrisrichardson.eventstore.EntityIdentifier;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountInfo;
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.AccountTransactionInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import rx.Observable;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@RestController
public class AccountQueryController {
@@ -20,17 +24,26 @@ public class AccountQueryController {
this.accountInfoQueryService = accountInfoQueryService;
}
@RequestMapping(value="/accounts/{accountId}", method = RequestMethod.GET)
public Observable<GetAccountResponse> get(@PathVariable String accountId) {
return accountInfoQueryService.findByAccountId(new EntityIdentifier(accountId))
.map(accountInfo -> new GetAccountResponse(accountInfo.getId(), new BigDecimal(accountInfo.getBalance())));
@RequestMapping(value = "/accounts/{accountId}", method = RequestMethod.GET)
public CompletableFuture<GetAccountResponse> get(@PathVariable String accountId) {
return accountInfoQueryService.findByAccountId(accountId)
.thenApply(accountInfo -> new GetAccountResponse(accountInfo.getId(), new BigDecimal(accountInfo.getBalance()), accountInfo.getTitle(), accountInfo.getDescription()));
}
@RequestMapping(value = "/accounts", method = RequestMethod.GET)
public CompletableFuture<List<GetAccountResponse>> getAccountsForCustomer(@RequestParam("customerId") String customerId) {
return accountInfoQueryService.findByCustomerId(customerId)
.thenApply(accountInfoList -> accountInfoList.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)
public CompletableFuture<List<AccountTransactionInfo>> getTransactionsHistory(@PathVariable String accountId) {
return accountInfoQueryService.findByAccountId(accountId)
.thenApply(AccountInfo::getTransactions);
}
@ResponseStatus(value= HttpStatus.NOT_FOUND, reason="account not found")
@ExceptionHandler(AccountNotFoundException.class)
public void accountNotFound() {
}
}

View File

@@ -4,30 +4,50 @@ import java.math.BigDecimal;
public class GetAccountResponse {
private String accountId;
private BigDecimal balance;
private String accountId;
private BigDecimal balance;
private String title;
private String description;
public GetAccountResponse() {
}
public GetAccountResponse() {
}
public GetAccountResponse(String accountId, BigDecimal balance) {
this.accountId = accountId;
this.balance = balance;
}
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 setBalance(BigDecimal balance) {
this.balance = balance;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public String getAccountId() {
return accountId;
}
public String getAccountId() {
return accountId;
}
public BigDecimal getBalance() {
return balance;
}
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,22 @@
apply plugin: 'java'
apply plugin: 'spring-boot'
apply plugin: EventuateDependencyPlugin
dependencies {
compile project(":common-auth-web")
compile "org.apache.httpcomponents:httpclient:4.5"
compile "org.apache.httpcomponents:fluent-hc:4.5.1"
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
testCompile "junit:junit:4.11"
}
task copyWebStatic(type: Copy) {
from "../../prebuilt-web-client"
into "build/resources/main/static"
}
jar.dependsOn(copyWebStatic)

View File

@@ -0,0 +1,60 @@
package net.chrisrichardson.eventstore.javaexamples.banking.apigateway;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.List;
/**
* Created by popikyardo on 15.01.16.
*/
@ConfigurationProperties(prefix = "api.gateway")
public class ApiGatewayProperties {
private List<Endpoint> endpoints;
public static class Endpoint {
private String path;
private RequestMethod method;
private String location;
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() {
return endpoints;
}
public void setEndpoints(List<Endpoint> endpoints) {
this.endpoints = endpoints;
}
}

View File

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

View File

@@ -0,0 +1,24 @@
package net.chrisrichardson.eventstore.javaexamples.banking.apigateway;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.ResponseErrorHandler;
import java.io.IOException;
public class RestTemplateErrorHandler implements ResponseErrorHandler {
private static final Logger log = LoggerFactory.getLogger(RestTemplateErrorHandler.class);
@Override
public void handleError(ClientHttpResponse response) throws IOException {
log.error("Response error: {} {}", response.getStatusCode(), response.getStatusText());
}
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return RestUtil.isError(response.getStatusCode());
}
}

View File

@@ -0,0 +1,15 @@
package net.chrisrichardson.eventstore.javaexamples.banking.apigateway;
import org.springframework.http.HttpStatus;
/**
* Created by popikyardo on 07.12.15.
*/
public class RestUtil {
public static boolean isError(HttpStatus status) {
HttpStatus.Series series = status.series();
return (HttpStatus.Series.CLIENT_ERROR.equals(series)
|| HttpStatus.Series.SERVER_ERROR.equals(series));
}
}

View File

@@ -0,0 +1,75 @@
package net.chrisrichardson.eventstore.javaexamples.banking.apigateway.controller;
import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.ApiGatewayProperties;
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.URLRequestTransformer;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.stream.Collectors;
import static org.springframework.web.bind.annotation.RequestMethod.*;
/**
* Created by popikyardo on 15.01.16.
*/
@RestController
public class GatewayController {
Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
private ApiGatewayProperties apiGatewayProperties;
private HttpClient httpClient;
@PostConstruct
public void init() {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
}
@RequestMapping(value = "/**", method = {GET, POST})
public String proxyRequest(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, IOException, URISyntaxException {
HttpUriRequest proxiedRequest = createHttpUriRequest(request);
log.info("request: {}", proxiedRequest);
HttpResponse proxiedResponse = httpClient.execute(proxiedRequest);
return read(proxiedResponse.getEntity().getContent());
}
private HttpUriRequest createHttpUriRequest(HttpServletRequest request) throws URISyntaxException, NoSuchRequestHandlingMethodException, IOException {
URLRequestTransformer urlRequestTransformer = new URLRequestTransformer(apiGatewayProperties);
ContentRequestTransformer contentRequestTransformer = new ContentRequestTransformer();
HeadersRequestTransformer headersRequestTransformer = new HeadersRequestTransformer();
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

@@ -0,0 +1,13 @@
package net.chrisrichardson.eventstore.javaexamples.banking.apigateway.main;
import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.ApiGatewayServiceConfiguration;
import org.springframework.boot.SpringApplication;
/**
* Created by Main on 19.01.2016.
*/
public class ApiGatewayServiceMain {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayServiceConfiguration.class, args);
}
}

View File

@@ -0,0 +1,30 @@
package net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.stream.Collectors;
/**
* Created by popikyardo on 21.01.16.
*/
public class ContentRequestTransformer extends ProxyRequestTransformer {
@Override
public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException {
RequestBuilder requestBuilder = predecessor.transform(request);
String requestContent = request.getReader().lines().collect(Collectors.joining(""));
if(!requestContent.isEmpty()) {
StringEntity entity = new StringEntity(requestContent, ContentType.APPLICATION_JSON);
requestBuilder.setEntity(entity);
}
return requestBuilder;
}
}

View File

@@ -0,0 +1,32 @@
package net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils;
import org.apache.http.client.methods.RequestBuilder;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Enumeration;
/**
* Created by popikyardo on 21.01.16.
*/
public class HeadersRequestTransformer extends ProxyRequestTransformer {
@Override
public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException {
RequestBuilder requestBuilder = predecessor.transform(request);
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
if(headerName.equals("x-access-token")) {
requestBuilder.addHeader(headerName, headerValue);
}
}
return requestBuilder;
}
}

View File

@@ -0,0 +1,22 @@
package net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils;
import org.apache.http.client.methods.RequestBuilder;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URISyntaxException;
/**
* Created by popikyardo on 21.01.16.
*/
public abstract class ProxyRequestTransformer {
protected ProxyRequestTransformer predecessor;
public abstract RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException;
public void setPredecessor(ProxyRequestTransformer transformer) {
this.predecessor = transformer;
};
}

View File

@@ -0,0 +1,48 @@
package net.chrisrichardson.eventstore.javaexamples.banking.apigateway.utils;
import net.chrisrichardson.eventstore.javaexamples.banking.apigateway.ApiGatewayProperties;
import org.apache.http.client.methods.RequestBuilder;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.net.URISyntaxException;
/**
* Created by popikyardo on 21.01.16.
*/
public class URLRequestTransformer extends ProxyRequestTransformer {
private ApiGatewayProperties apiGatewayProperties;
public URLRequestTransformer(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));
}
RequestBuilder rb = RequestBuilder.create(request.getMethod());
rb.setUri(uri);
return rb;
}
private String getServiceUrl(String requestURI, HttpServletRequest httpServletRequest) throws NoSuchRequestHandlingMethodException {
ApiGatewayProperties.Endpoint endpoint =
apiGatewayProperties.getEndpoints().stream()
.filter(e ->
requestURI.matches(e.getPath()) && e.getMethod() == RequestMethod.valueOf(httpServletRequest.getMethod())
)
.findFirst().orElseThrow(() -> new NoSuchRequestHandlingMethodException(httpServletRequest));
return endpoint.getLocation() + requestURI;
}
}

View File

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

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- [%thread] -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<root level="error">
<appender-ref ref="STDOUT" />
</root>
<logger name="org.springframework" level='info'>
</logger>
<logger name="net.chrisrichardson.eventstore.client" level='info'>
</logger>
</configuration>

View File

@@ -5,9 +5,11 @@ dependencies {
testCompile project(":accounts-command-side-backend")
testCompile project(":transactions-command-side-backend")
testCompile project(":accounts-query-side-backend")
testCompile project(":testutil")
testCompile project(":customers-command-side-backend")
testCompile project(":customers-query-side-backend")
testCompile project(":testutil-customers")
testCompile "junit:junit:4.11"
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
testCompile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion"
testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"
}

View File

@@ -1,14 +1,15 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend;
import io.eventuate.javaclient.spring.jdbc.EventuateJdbcEventStoreConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.AccountConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransferConfiguration;
import net.chrisrichardson.eventstore.jdbc.config.JdbcEventStoreConfiguration;
import net.chrisrichardson.utils.config.MetricRegistryConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({AccountConfiguration.class, MoneyTransferConfiguration.class, JdbcEventStoreConfiguration.class})
@Import({AccountConfiguration.class, MoneyTransferConfiguration.class, EventuateJdbcEventStoreConfiguration.class})
@EnableAutoConfiguration
public class BankingTestConfiguration {
}

View File

@@ -1,14 +1,13 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend;
import net.chrisrichardson.eventstore.EntityWithIdAndVersion;
import net.chrisrichardson.eventstore.EntityWithMetadata;
import net.chrisrichardson.eventstore.EventStore;
import io.eventuate.EntityWithIdAndVersion;
import io.eventuate.EventuateAggregateStore;
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.transactions.MoneyTransfer;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransferService;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.TransferDetails;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.TransferState;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.TransferDetails;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -16,12 +15,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import rx.Observable;
import java.math.BigDecimal;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.await;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
@@ -38,117 +34,57 @@ public class MoneyTransferIntegrationTest {
private MoneyTransferService moneyTransferService;
@Autowired
private EventStore eventStore;
private EventuateAggregateStore eventStore;
@Test
public void shouldTransferMoney() {
final EntityWithIdAndVersion<Account> fromAccount= await(accountService.openAccount(new BigDecimal(150)));
final EntityWithIdAndVersion<Account> fromAccount= await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(150), ""));
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount(new BigDecimal(300)));
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(300), ""));
final EntityWithIdAndVersion<MoneyTransfer> transaction = await(
moneyTransferService.transferMoney(new TransferDetails(fromAccount.getEntityIdentifier(),
toAccount.getEntityIdentifier(),
moneyTransferService.transferMoney(new TransferDetails(fromAccount.getEntityId(),
toAccount.getEntityId(),
new BigDecimal(80))));
eventually (
new Producer<EntityWithMetadata<Account>>() {
@Override
public Observable<EntityWithMetadata<Account>> produce() {
return (Observable<EntityWithMetadata<Account>>)eventStore.find(Account.class, fromAccount.getEntityIdentifier());
}
},
new Verifier<EntityWithMetadata<Account>>() {
@Override
public void verify(EntityWithMetadata<Account> account) {
Assert.assertEquals(new BigDecimal(70), account.entity().getBalance());
}
});
() -> eventStore.find(Account.class, fromAccount.getEntityId()),
account -> Assert.assertEquals(new BigDecimal(70), account.getEntity().getBalance()));
eventually (
new Producer<EntityWithMetadata<Account>>() {
@Override
public Observable<EntityWithMetadata<Account>> produce() {
return (Observable<EntityWithMetadata<Account>>)eventStore.find(Account.class, toAccount.getEntityIdentifier());
}
},
new Verifier<EntityWithMetadata<Account>>() {
@Override
public void verify(EntityWithMetadata<Account> account) {
Assert.assertEquals(new BigDecimal(380), account.entity().getBalance());
}
});
() -> eventStore.find(Account.class, toAccount.getEntityId()),
account -> Assert.assertEquals(new BigDecimal(380), account.getEntity().getBalance()));
eventually (
new Producer<EntityWithMetadata<MoneyTransfer>>() {
@Override
public Observable<EntityWithMetadata<MoneyTransfer>> produce() {
return (Observable<EntityWithMetadata<MoneyTransfer>>)eventStore.find(MoneyTransfer.class, transaction.getEntityIdentifier());
}
},
new Verifier<EntityWithMetadata<MoneyTransfer>>() {
@Override
public void verify(EntityWithMetadata<MoneyTransfer> updatedTransaction) {
Assert.assertEquals(TransferState.COMPLETED, updatedTransaction.entity().getState());
}
});
() -> eventStore.find(MoneyTransfer.class, transaction.getEntityId()),
updatedTransaction -> Assert.assertEquals(TransferState.COMPLETED, updatedTransaction.getEntity().getState()));
}
@Test
public void shouldFailDueToInsufficientFunds() {
final EntityWithIdAndVersion<Account> fromAccount= await(accountService.openAccount(new BigDecimal(150)));
final EntityWithIdAndVersion<Account> fromAccount= await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(150), ""));
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount(new BigDecimal(300)));
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(300), ""));
final EntityWithIdAndVersion<MoneyTransfer> transaction = await(
moneyTransferService.transferMoney(new TransferDetails(fromAccount.getEntityIdentifier(),
toAccount.getEntityIdentifier(),
moneyTransferService.transferMoney(new TransferDetails(fromAccount.getEntityId(),
toAccount.getEntityId(),
new BigDecimal(200))));
eventually (
new Producer<EntityWithMetadata<MoneyTransfer>>() {
@Override
public Observable<EntityWithMetadata<MoneyTransfer>> produce() {
return (Observable<EntityWithMetadata<MoneyTransfer>>)eventStore.find(MoneyTransfer.class, transaction.getEntityIdentifier());
}
},
new Verifier<EntityWithMetadata<MoneyTransfer>>() {
@Override
public void verify(EntityWithMetadata<MoneyTransfer> updatedTransaction) {
Assert.assertEquals(TransferState.FAILED_DUE_TO_INSUFFICIENT_FUNDS, updatedTransaction.entity().getState());
}
});
() -> eventStore.find(MoneyTransfer.class, transaction.getEntityId()),
updatedTransaction -> Assert.assertEquals(TransferState.FAILED_DUE_TO_INSUFFICIENT_FUNDS, updatedTransaction.getEntity().getState()));
eventually (
new Producer<EntityWithMetadata<Account>>() {
@Override
public Observable<EntityWithMetadata<Account>> produce() {
return (Observable<EntityWithMetadata<Account>>)eventStore.find(Account.class, fromAccount.getEntityIdentifier());
}
},
new Verifier<EntityWithMetadata<Account>>() {
@Override
public void verify(EntityWithMetadata<Account> account) {
Assert.assertEquals(new BigDecimal(150), account.entity().getBalance());
}
});
() -> eventStore.find(Account.class, fromAccount.getEntityId()),
account -> Assert.assertEquals(new BigDecimal(150), account.getEntity().getBalance()));
eventually (
new Producer<EntityWithMetadata<Account>>() {
@Override
public Observable<EntityWithMetadata<Account>> produce() {
return (Observable<EntityWithMetadata<Account>>)eventStore.find(Account.class, toAccount.getEntityIdentifier());
}
},
new Verifier<EntityWithMetadata<Account>>() {
@Override
public void verify(EntityWithMetadata<Account> account) {
Assert.assertEquals(new BigDecimal(300), account.entity().getBalance());
}
});
() -> eventStore.find(Account.class, toAccount.getEntityId()),
account -> Assert.assertEquals(new BigDecimal(300), account.getEntity().getBalance()));
}

View File

@@ -1,8 +1,8 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import net.chrisrichardson.eventstore.EntityWithIdAndVersion;
import net.chrisrichardson.eventstore.EntityWithMetadata;
import net.chrisrichardson.eventstore.EventStore;
import io.eventuate.EntityWithIdAndVersion;
import io.eventuate.EntityWithMetadata;
import io.eventuate.EventuateAggregateStore;
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.transactions.MoneyTransfer;
@@ -37,7 +37,7 @@ public class AccountQuerySideIntegrationTest {
private MoneyTransferService moneyTransferService;
@Autowired
private EventStore eventStore;
private EventuateAggregateStore eventStore;
@Autowired
private AccountQueryService accountQueryService;
@@ -45,54 +45,24 @@ public class AccountQuerySideIntegrationTest {
@Test
public void shouldUpdateQuerySide() throws Exception {
final EntityWithIdAndVersion<Account> fromAccount = await(accountService.openAccount(new BigDecimal(150)));
final EntityWithIdAndVersion<Account> fromAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(150), ""));
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount(new BigDecimal(300)));
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(300), ""));
final EntityWithIdAndVersion<MoneyTransfer> transaction = await(
moneyTransferService.transferMoney(new TransferDetails(fromAccount.getEntityIdentifier(),
toAccount.getEntityIdentifier(),
moneyTransferService.transferMoney(new TransferDetails(fromAccount.getEntityId(),
toAccount.getEntityId(),
new BigDecimal(80))));
eventually(
new Producer<EntityWithMetadata<MoneyTransfer>>() {
@Override
public Observable<EntityWithMetadata<MoneyTransfer>> produce() {
return eventStore.find(MoneyTransfer.class, transaction.getEntityIdentifier());
}
},
new Verifier<EntityWithMetadata<MoneyTransfer>>() {
@Override
public void verify(EntityWithMetadata<MoneyTransfer> updatedTransaction) {
Assert.assertEquals(TransferState.COMPLETED, updatedTransaction.entity().getState());
}
});
() -> eventStore.find(MoneyTransfer.class, transaction.getEntityId()),
updatedTransaction -> Assert.assertEquals(TransferState.COMPLETED, updatedTransaction.getEntity().getState()));
eventually(
new Producer<AccountInfo>() {
@Override
public Observable<AccountInfo> produce() {
return accountQueryService.findByAccountId(fromAccount.getEntityIdentifier());
}
},
new Verifier<AccountInfo>() {
@Override
public void verify(AccountInfo accountInfo) {
Assert.assertEquals(70*100, accountInfo.getBalance());
}
});
() -> accountQueryService.findByAccountId(fromAccount.getEntityId()),
accountInfo -> Assert.assertEquals(70*100, accountInfo.getBalance()));
eventually(
new Producer<AccountInfo>() {
@Override
public Observable<AccountInfo> produce() {
return accountQueryService.findByAccountId(toAccount.getEntityIdentifier());
}
},
new Verifier<AccountInfo>() {
@Override
public void verify(AccountInfo accountInfo) {
Assert.assertEquals(380*100, accountInfo.getBalance());
}
});
() -> accountQueryService.findByAccountId(toAccount.getEntityId()),
accountInfo -> Assert.assertEquals(380*100, accountInfo.getBalance()));
}
}

View File

@@ -1,12 +1,15 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import io.eventuate.javaclient.spring.jdbc.EventuateJdbcEventStoreConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.AccountConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransferConfiguration;
import net.chrisrichardson.eventstore.jdbc.config.JdbcEventStoreConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({AccountConfiguration.class, MoneyTransferConfiguration.class, JdbcEventStoreConfiguration.class, QuerySideAccountConfiguration.class})
@Import({AccountConfiguration.class, MoneyTransferConfiguration.class, EventuateJdbcEventStoreConfiguration.class,
QuerySideAccountConfiguration.class})
@EnableAutoConfiguration
public class AccountQuerySideTestConfiguration {
}

View File

@@ -0,0 +1,74 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
import io.eventuate.EntityWithIdAndVersion;
import io.eventuate.EventuateAggregateStore;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.Customer;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.CustomerService;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.QuerySideCustomer;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.concurrent.CompletableFuture;
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.customers.CustomersTestUtils.generateCustomerInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils.generateToAccountInfo;
/**
* Created by Main on 10.02.2016.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = CustomerQuerySideTestConfiguration.class)
@IntegrationTest
public class CustomerQuerySideIntegrationTest {
@Autowired
private CustomerService customerService;
@Autowired
private CustomerQueryService customerQueryService;
@Autowired
private EventuateAggregateStore eventStore;
@Test
public void shouldCreateCustomerAndAddToAccount() throws Exception {
CustomerInfo customerInfo = generateCustomerInfo();
EntityWithIdAndVersion<Customer> customer = await(customerService.createCustomer(customerInfo));
ToAccountInfo toAccountInfo = generateToAccountInfo();
EntityWithIdAndVersion<Customer> customerWithNewAccount = await(customerService.addToAccount(customer.getEntityId(), toAccountInfo));
eventually(
new Producer<QuerySideCustomer>() {
@Override
public CompletableFuture<QuerySideCustomer> produce() {
return customerQueryService.findByCustomerId(customer.getEntityId());
}
},
new Verifier<QuerySideCustomer>() {
@Override
public void verify(QuerySideCustomer querySideCustomer) {
Assert.assertEquals(customerInfo.getName(), querySideCustomer.getName());
Assert.assertEquals(customerInfo.getSsn(), querySideCustomer.getSsn());
Assert.assertEquals(customerInfo.getEmail(), querySideCustomer.getEmail());
Assert.assertEquals(customerInfo.getPhoneNumber(), querySideCustomer.getPhoneNumber());
Assert.assertEquals(customerInfo.getAddress(), querySideCustomer.getAddress());
Assert.assertNotNull(querySideCustomer.getToAccounts());
Assert.assertFalse(querySideCustomer.getToAccounts().isEmpty());
Assert.assertEquals(querySideCustomer.getToAccounts().get("11111111-11111111"), toAccountInfo);
}
});
}
}

View File

@@ -0,0 +1,14 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
import io.eventuate.javaclient.spring.jdbc.EventuateJdbcEventStoreConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.CustomerConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({CustomerConfiguration.class, EventuateJdbcEventStoreConfiguration.class, QuerySideCustomerConfiguration.class})
@EnableAutoConfiguration
public class CustomerQuerySideTestConfiguration {
}

View File

@@ -0,0 +1,16 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
class EventuateDependencyPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.dependencies {
if (project.hasProperty("eventuateLocal")) {
compile "io.eventuate.local.java:eventuate-local-java-jdbc:${project.eventuateLocalVersion}"
compile "io.eventuate.local.java:eventuate-local-java-embedded-cdc-autoconfigure:${project.eventuateLocalVersion}"
} else
compile "io.eventuate.client.java:eventuate-client-java-http-stomp-spring:${project.eventuateClientVersion}"
}
}
}

View File

@@ -0,0 +1,11 @@
apply plugin: 'java'
dependencies {
compile project(":common-auth")
compile project(":common-customers")
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
compile "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
testCompile "junit:junit:4.11"
}

View File

@@ -0,0 +1,64 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.QuerySideCustomer;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.CustomerAuthService;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.AuthRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.ErrorResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.token.Token;
import org.springframework.security.core.token.TokenService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.io.IOException;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
/**
* Created by popikyardo on 21.09.15.
*/
@RestController
@Validated
public class AuthController {
@Autowired
private TokenService tokenService;
@Autowired
private CustomerAuthService customerAuthService;
private static ObjectMapper objectMapper = new ObjectMapper();
@RequestMapping(value = "/login", method = POST)
public ResponseEntity<QuerySideCustomer> doAuth(@RequestBody @Valid AuthRequest request) throws IOException {
QuerySideCustomer customer = customerAuthService.findByEmail(request.getEmail());
Token token = tokenService.allocateToken(objectMapper.writeValueAsString(new User(request.getEmail())));
return ResponseEntity.status(HttpStatus.OK).header("access-token", token.getKey())
.body(customer);
}
@ResponseStatus(value = HttpStatus.NOT_FOUND)
@ExceptionHandler(IncorrectResultSizeDataAccessException.class)
public ErrorResponse customersNotFound() {
return new ErrorResponse("Customer not found");
}
@RequestMapping(value = "/user", method = GET)
public ResponseEntity<QuerySideCustomer> getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return ResponseEntity.status(HttpStatus.OK).body(customerAuthService.findByEmail(auth.getName()));
}
}

View File

@@ -0,0 +1,29 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotBlank;
/**
* Created by popikyardo on 19.10.15.
*/
public class AuthRequest {
@NotBlank
@Email
private String email;
public AuthRequest() {
}
public AuthRequest(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

View File

@@ -0,0 +1,24 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model;
/**
* Created by Main on 17.02.2016.
*/
public class ErrorResponse {
private String message;
public ErrorResponse() {
}
public ErrorResponse(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@@ -0,0 +1,14 @@
apply plugin: 'java'
dependencies {
compile project(":common-customers")
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion"
compile "org.springframework.security:spring-security-config:4.0.2.RELEASE"
compile "org.springframework.security:spring-security-web:4.0.2.RELEASE"
testCompile "junit:junit:4.11"
}

View File

@@ -0,0 +1,97 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.filter.StatelessAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.token.KeyBasedPersistenceTokenService;
import org.springframework.security.core.token.TokenService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import java.security.SecureRandom;
/**
* Created by popikyardo on 21.09.15.
*/
@Configuration
@ComponentScan
@EnableWebSecurity
@EnableMongoRepositories
@EnableConfigurationProperties({AuthProperties.class})
public class AuthConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private AuthProperties securityProperties;
@Autowired
private TokenAuthenticationService tokenAuthenticationService;
@Autowired
CustomerAuthService customerAuthService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//auth.inMemoryAuthentication();
auth.userDetailsService(userDetailsServiceBean());
}
@Override
public UserDetailsService userDetailsServiceBean() {
return email -> {
/* QuerySideCustomer customer = customerAuthService.findByEmail(email);
if (customer != null) {
return new User(email);
} else {
throw new UsernameNotFoundException(String.format("could not find the customer '%s'", email));
}*/
//authorize everyone with basic authentication
return new User(email, "", true, true, true, true,
AuthorityUtils.createAuthorityList("USER"));
};
}
@Bean
public CustomerAuthService customerAuthService(CustomerAuthRepository customerAuthRepository) {
return new CustomerAuthService(customerAuthRepository);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.httpBasic().and()
.authorizeRequests()
.antMatchers("/index.html", "/", "/**.js", "/**.css").permitAll()
.antMatchers("/swagger-ui.html", "/v2/api-docs").permitAll()
.antMatchers(HttpMethod.POST, "/customers", "/login").permitAll()
.anyRequest().authenticated().and()
.addFilterAfter(new StatelessAuthenticationFilter(tokenAuthenticationService), BasicAuthenticationFilter.class);
}
@Bean
public TokenService tokenService() {
KeyBasedPersistenceTokenService res = new KeyBasedPersistenceTokenService();
res.setSecureRandom(new SecureRandom());
res.setServerSecret(securityProperties.getServerSecret());
res.setServerInteger(securityProperties.getServerInteger());
return res;
}
}

View File

@@ -0,0 +1,28 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Created by popikyardo on 21.09.15.
*/
@ConfigurationProperties(locations = "classpath:auth.properties", ignoreUnknownFields = false, prefix = "auth")
public class AuthProperties {
private String serverSecret;
private Integer serverInteger;
public String getServerSecret() {
return serverSecret;
}
public void setServerSecret(String serverSecret) {
this.serverSecret = serverSecret;
}
public Integer getServerInteger() {
return serverInteger;
}
public void setServerInteger(Integer serverInteger) {
this.serverInteger = serverInteger;
}
}

View File

@@ -0,0 +1,11 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.QuerySideCustomer;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
interface CustomerAuthRepository extends MongoRepository<QuerySideCustomer, String> {
List<QuerySideCustomer> findByEmail(String email);
}

View File

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

View File

@@ -0,0 +1,43 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.User;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.UserAuthentication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.token.Token;
import org.springframework.security.core.token.TokenService;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Created by popikyardo on 23.09.15.
*/
@Service
public class TokenAuthenticationService {
@Autowired
private TokenService tokenService;
private static final String AUTH_HEADER_NAME = "access-token";
private static final long DAY = 1000 * 60 * 60 * 24;
private ObjectMapper mapper = new ObjectMapper();
public Authentication getAuthentication(HttpServletRequest request) throws IOException {
final String tokenString = request.getHeader(AUTH_HEADER_NAME);
if (tokenString != null) {
Token token = tokenService.verifyToken(tokenString);
final User user = mapper.readValue(token.getExtendedInformation(), User.class);
if (user != null && (System.currentTimeMillis() - token.getKeyCreationTime()) < DAY) {
return new UserAuthentication(user);
}
}
return null;
}
}

View File

@@ -0,0 +1,33 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.filter;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.TokenAuthenticationService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Created by popikyardo on 23.09.15.
*/
public class StatelessAuthenticationFilter extends GenericFilterBean {
private final TokenAuthenticationService tokenAuthenticationService;
public StatelessAuthenticationFilter(TokenAuthenticationService taService) {
this.tokenAuthenticationService = taService;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
if (SecurityContextHolder.getContext().getAuthentication()==null) {
SecurityContextHolder.getContext().setAuthentication(
tokenAuthenticationService.getAuthentication((HttpServletRequest) req));
}
chain.doFilter(req, res);
}
}

View File

@@ -0,0 +1,78 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* Created by popikyardo on 23.09.15.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class User implements UserDetails {
private String email;
public User() {
}
public User(String email) {
this.email = email;
}
public void setUsername(String username) {
this.email = username;
}
@Override
@JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("USER");
Set<GrantedAuthority> res = new HashSet<GrantedAuthority>();
res.add(authority);
return res;
}
@Override
public String getPassword() {
return "";
}
@Override
public String getUsername() {
return this.email;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

View File

@@ -0,0 +1,54 @@
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
/**
* Created by popikyardo on 23.09.15.
*/
public class UserAuthentication implements Authentication {
private final User user;
private boolean authenticated = true;
public UserAuthentication(User user) {
this.user = user;
}
@Override
public String getName() {
return user.getUsername();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
@Override
public Object getCredentials() {
return user.getPassword();
}
@Override
public User getDetails() {
return user;
}
@Override
public Object getPrincipal() {
return user.getUsername();
}
@Override
public boolean isAuthenticated() {
return authenticated;
}
@Override
public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
}

View File

@@ -0,0 +1,45 @@
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

@@ -0,0 +1,2 @@
auth.serverSecret=the_cake_is_a_lie
auth.serverInteger=1

View File

@@ -1,11 +1,13 @@
apply plugin: 'java'
dependencies {
compile "net.chrisrichardson.eventstore.client:eventstore-java-client_2.10:$eventStoreClientVersion"
compile project(":common-customers")
compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion"
testCompile "junit:junit:4.11"
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
testCompile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion"
testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"
}

View File

@@ -1,16 +1,16 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts;
import net.chrisrichardson.eventstore.Aggregate;
import net.chrisrichardson.eventstore.EntityIdentifier;
import net.chrisrichardson.eventstore.Event;
import io.eventuate.Aggregate;
import io.eventuate.Event;
import java.math.BigDecimal;
public class AccountChangedEvent implements Event {
protected BigDecimal amount;
protected EntityIdentifier transactionId;
protected String transactionId;
public AccountChangedEvent(BigDecimal amount, EntityIdentifier transactionId) {
public AccountChangedEvent(BigDecimal amount, String transactionId) {
this.amount = amount;
this.transactionId = transactionId;
}
@@ -18,7 +18,7 @@ public class AccountChangedEvent implements Event {
public AccountChangedEvent() {
}
public EntityIdentifier getTransactionId() {
public String getTransactionId() {
return transactionId;
}

View File

@@ -1,7 +1,7 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts;
import net.chrisrichardson.eventstore.Aggregate;
import net.chrisrichardson.eventstore.EntityIdentifier;
import io.eventuate.Aggregate;
import java.math.BigDecimal;
@@ -10,7 +10,7 @@ public class AccountCreditedEvent extends AccountChangedEvent {
private AccountCreditedEvent() {
}
public AccountCreditedEvent(BigDecimal amount, EntityIdentifier transactionId) {
public AccountCreditedEvent(BigDecimal amount, String transactionId) {
super(amount, transactionId);
}

View File

@@ -1,20 +1,20 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts;
import net.chrisrichardson.eventstore.Aggregate;
import net.chrisrichardson.eventstore.EntityIdentifier;
import net.chrisrichardson.eventstore.Event;
import io.eventuate.Aggregate;
import io.eventuate.Event;
public class AccountDebitFailedDueToInsufficientFundsEvent implements Event {
private EntityIdentifier transactionId;
private String transactionId;
private AccountDebitFailedDueToInsufficientFundsEvent() {
}
public AccountDebitFailedDueToInsufficientFundsEvent(EntityIdentifier transactionId) {
public AccountDebitFailedDueToInsufficientFundsEvent(String transactionId) {
this.transactionId = transactionId;
}
public EntityIdentifier getTransactionId() {
public String getTransactionId() {
return transactionId;
}
}

View File

@@ -1,7 +1,7 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts;
import net.chrisrichardson.eventstore.Aggregate;
import net.chrisrichardson.eventstore.EntityIdentifier;
import io.eventuate.Aggregate;
import java.math.BigDecimal;
@@ -10,7 +10,7 @@ public class AccountDebitedEvent extends AccountChangedEvent {
private AccountDebitedEvent() {
}
public AccountDebitedEvent(BigDecimal amount, EntityIdentifier transactionId) {
public AccountDebitedEvent(BigDecimal amount, String transactionId) {
super(amount, transactionId);
}

View File

@@ -1,22 +1,40 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts;
import net.chrisrichardson.eventstore.Event;
import io.eventuate.Event;
import java.math.BigDecimal;
public class AccountOpenedEvent implements Event {
private String customerId;
private String title;
private BigDecimal initialBalance;
private String description;
private AccountOpenedEvent() {
}
public AccountOpenedEvent(BigDecimal initialBalance) {
public AccountOpenedEvent(String customerId, String title, BigDecimal initialBalance, String description) {
this.customerId = customerId;
this.title = title;
this.initialBalance = initialBalance;
this.description = description;
}
public String getCustomerId() {
return customerId;
}
public String getTitle() {
return title;
}
public BigDecimal getInitialBalance() {
return initialBalance;
}
public String getDescription() {
return description;
}
}

View File

@@ -1,2 +1,2 @@
@net.chrisrichardson.eventstore.EventEntity(entity="net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.Account")
@io.eventuate.EventEntity(entity="net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.Account")
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts;

View File

@@ -0,0 +1,26 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
/**
* Created by Main on 08.02.2016.
*/
public class CustomerAddedToAccount extends CustomerEvent {
private ToAccountInfo toAccountInfo;
public CustomerAddedToAccount() {
}
public CustomerAddedToAccount(ToAccountInfo toAccountInfo) {
this.toAccountInfo = toAccountInfo;
}
public ToAccountInfo getToAccountInfo() {
return toAccountInfo;
}
public void setToAccountInfo(ToAccountInfo toAccountInfo) {
this.toAccountInfo = toAccountInfo;
}
}

View File

@@ -0,0 +1,26 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
/**
* Created by popikyardo on 02.02.16.
*/
public class CustomerCreatedEvent extends CustomerEvent {
private CustomerInfo customerInfo;
public CustomerCreatedEvent() {
}
public CustomerCreatedEvent(CustomerInfo customerInfo) {
this.customerInfo = customerInfo;
}
public CustomerInfo getCustomerInfo() {
return customerInfo;
}
public void setCustomerInfo(CustomerInfo customerInfo) {
this.customerInfo = customerInfo;
}
}

View File

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

View File

@@ -1,6 +1,6 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions;
import net.chrisrichardson.eventstore.Event;
import io.eventuate.Event;
public class CreditRecordedEvent implements Event {
private TransferDetails details;

View File

@@ -1,6 +1,8 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions;
import net.chrisrichardson.eventstore.Event;
// import io.eventuate.Event;
import io.eventuate.Event;
public class DebitRecordedEvent implements Event {
private TransferDetails details;

View File

@@ -1,6 +1,6 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions;
import net.chrisrichardson.eventstore.Event;
import io.eventuate.Event;
public class FailedDebitRecordedEvent implements Event {
private TransferDetails details;

View File

@@ -1,7 +1,7 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions;
import net.chrisrichardson.eventstore.Event;
import io.eventuate.Event;
public class MoneyTransferCreatedEvent implements Event {
private TransferDetails details;

View File

@@ -4,34 +4,50 @@ package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.trans
case class TransferDetails(fromAccountId : EntityIdentifier, toAccountId : EntityIdentifier, amount : BigDecimal)
*/
import net.chrisrichardson.eventstore.EntityIdentifier;
import java.math.BigDecimal;
import java.util.Date;
public class TransferDetails {
private EntityIdentifier fromAccountId;
private EntityIdentifier toAccountId;
private String fromAccountId;
private String toAccountId;
private BigDecimal amount;
private Date date;
private String description;
private TransferDetails() {
}
public TransferDetails(EntityIdentifier fromAccountId, EntityIdentifier toAccountId, BigDecimal amount) {
public TransferDetails(String fromAccountId, String toAccountId, BigDecimal amount) {
this(fromAccountId, toAccountId, amount, new Date(), "");
}
public TransferDetails(String fromAccountId, String toAccountId, BigDecimal amount, Date date, String description) {
this.fromAccountId = fromAccountId;
this.toAccountId = toAccountId;
this.amount = amount;
this.date = date;
this.description = description;
}
public EntityIdentifier getFromAccountId() {
public String getFromAccountId() {
return fromAccountId;
}
public EntityIdentifier getToAccountId() {
public String getToAccountId() {
return toAccountId;
}
public BigDecimal getAmount() {
return amount;
}
public Date getDate() {
return date;
}
public String getDescription() {
return description;
}
}

View File

@@ -1,2 +1,2 @@
@net.chrisrichardson.eventstore.EventEntity(entity="net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransfer")
@io.eventuate.EventEntity(entity="net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransfer")
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions;

View File

@@ -16,7 +16,7 @@
<logger name="org.springframework" level='info'>
</logger>
<logger name="net.chrisrichardson.eventstore.client" level='info'>
<logger name="io.eventuate" level='debug'>
</logger>
</configuration>

View File

@@ -1,23 +1,21 @@
package net.chrisrichardson.eventstore.javaexamples.banking.common.accounts;
import io.eventuate.javaclient.commonimpl.JSonMapper;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accounts.AccountOpenedEvent;
import net.chrisrichardson.eventstore.json.EventStoreCommonObjectMapping;
import net.chrisrichardson.utils.json.JSonMapper;
import org.junit.Assert;
import org.junit.Test;
import java.math.BigDecimal;
public class AccountOpenEventSerializationTest {
@Test
public void shouldSerde() {
AccountOpenedEvent event = new AccountOpenedEvent(new BigDecimal(55));
String json = JSonMapper.toJson(event, EventStoreCommonObjectMapping.getObjectMapper());
AccountOpenedEvent event = new AccountOpenedEvent("00000000-00000000", "My Account", new BigDecimal(55), "");
String json = JSonMapper.toJson(event);
System.out.println("json=" + json);
AccountOpenedEvent event2 = JSonMapper.fromJSon(AccountOpenedEvent.class, json, EventStoreCommonObjectMapping.getObjectMapper());
AccountOpenedEvent event2 = JSonMapper.fromJson(json, AccountOpenedEvent.class);
Assert.assertEquals(event.getInitialBalance(), event2.getInitialBalance());
}

View File

@@ -0,0 +1,8 @@
apply plugin: 'java'
dependencies {
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'
}

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;
}
}

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