17 Commits

Author SHA1 Message Date
Chris Richardson
d78f88337a Migrated to new Eventuate Java Client 2016-08-30 19:15:50 -07:00
Chris Richardson
e14787bce8 Fixed misc @Configuration issues and commented out incorrect version check that causing failure in AccountInfoUpdateService 2016-08-30 16:26:21 -07:00
Chris Richardson
ef444bde9b Fixed API gateway misconfiguration 2016-08-25 14:48:30 -07:00
Chris Richardson
4b73b9beed Misc bug fixes 2016-08-25 14:21:44 -07:00
Chris Richardson
d0bdd51406 Fixed typo 2016-08-25 12:15:54 -07:00
Chris Richardson
b0b32d0a3f Merge pull request #22 from dartpopikyardo/wip-customer
refactored the tests
2016-08-25 12:14:35 -07:00
Main
e9517fe30f unified code format 2016-08-24 21:22:12 +03:00
Main
5f8475d675 - fixed the tests
- updated eventuate-client version
- fixed api-gateway and common module's dependencies
2016-08-10 23:39:00 +03:00
Main
12e46582cd Merge remote-tracking branch 'remotes/upstream/wip-customer' into wip-customer
# Conflicts:
#	java-spring/monolithic-service/src/test/java/net/chrisrichardson/eventstore/javaexamples/banking/web/BankingWebIntegrationTest.java
2016-08-09 22:59:59 +03:00
Main
b0855ebd81 - refactored the tests
- moved some dto classes into common module
2016-08-09 22:58:00 +03:00
Main
39309f23a1 - refactored the tests
- moved some dto classes into common module
2016-08-09 22:43:41 +03:00
Chris Richardson
e488df3d06 Fixed typo 2016-06-10 16:45:02 -07:00
Chris Richardson
4f11433390 Merge branch 'master' of github.com:cer/event-sourcing-examples 2016-06-10 16:43:32 -07:00
Chris Richardson
b573027fc2 Added Swagger to monolithic deployment, Added mongodb-cli.sh 2016-06-10 16:43:14 -07:00
Chris Richardson
3117f12402 Fixed typo 2016-06-06 20:48:58 -07:00
Chris Richardson
5b029d8307 Merge branch 'master' of github.com:cer/event-sourcing-examples 2016-05-31 15:08:46 -07:00
Chris Richardson
6d8376cfd5 Use specific Java 8 Docker image 2016-05-31 15:08:34 -07:00
124 changed files with 1760 additions and 2078 deletions

View File

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

View File

@@ -6,12 +6,6 @@ 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
@@ -19,13 +13,6 @@ 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
@@ -60,7 +47,5 @@ set -e
./gradlew -a $* :e2e-test:cleanTest :e2e-test:test -P ignoreE2EFailures=false
if [ $NO_RM = false ] ; then
${DOCKER_COMPOSE?} stop
${DOCKER_COMPOSE?} rm -v --force
fi

View File

@@ -28,10 +28,8 @@ The Query Service consists the following modules:
# Deploying the application
These services can be deployed either as either separate standalone services using the Event Store server, or they can be deployed as a monolithic application for simpified integration testing.
These services can be deployed either as either separate standalone services using the Event Store server, or they can be deployed as a monolithic application for simplified integration testing.
The three services can also be packaged as a single monolithic web application in order to be used with the embedded Event Store:
* monolithic-service - all-in-one, monolithic packaging of the application

View File

@@ -22,7 +22,13 @@ public class AccountWorkflow {
String fromAccountId = event.getDetails().getFromAccountId();
return ctx.update(Account.class, fromAccountId, new DebitAccountCommand(amount, transactionId));
return ctx.update(Account.class, fromAccountId, new DebitAccountCommand(amount, transactionId)).handle((x, e) -> {
if (e != null) {
e.printStackTrace();
}
return x;
}
);
}
@EventHandlerMethod
@@ -32,7 +38,13 @@ public class AccountWorkflow {
String fromAccountId = event.getDetails().getToAccountId();
String transactionId = ctx.getEntityId();
return ctx.update(Account.class, fromAccountId, new CreditAccountCommand(amount, transactionId));
return ctx.update(Account.class, fromAccountId, new CreditAccountCommand(amount, transactionId)).handle((x, e) -> {
if (e != null) {
e.printStackTrace();
}
return x;
}
);
}
}

View File

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

View File

@@ -1,7 +1,7 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.CreateAccountResponse;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@@ -1,6 +1,8 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.accounts.AccountService;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.CreateAccountResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;

View File

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

View File

@@ -1,9 +1,9 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
import com.mongodb.WriteResult;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountTransactionInfo;
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;
@@ -17,39 +17,40 @@ 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(MongoTemplate mongoTemplate) {
public AccountInfoUpdateService(AccountInfoRepository accountInfoRepository, MongoTemplate mongoTemplate) {
this.accountInfoRepository = accountInfoRepository;
this.mongoTemplate = mongoTemplate;
}
public void create(String accountId, String customerId, String title, BigDecimal initialBalance, String description, String version) {
try {
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);
accountInfoRepository.save(new AccountInfo(
accountId,
customerId,
title,
description,
toIntegerRepr(initialBalance),
Collections.<AccountChangeInfo>emptyList(),
Collections.<AccountTransactionInfo>emptyList(),
version));
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 accountId, AccountTransactionInfo ti) {
mongoTemplate.upsert(new Query(where("id").is(accountId)),
public void addTransaction(String eventId, String fromAccountId, AccountTransactionInfo ti) {
mongoTemplate.updateMulti(new Query(where("id").is(fromAccountId)), /* wrong .and("version").lt(eventId) */
new Update().
set("transactions." + eventId, ti),
push("transactions", ti).
set("version", eventId),
AccountInfo.class);
}

View File

@@ -8,6 +8,7 @@ import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.accoun
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.javaexamples.banking.common.accounts.AccountTransactionInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

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

View File

@@ -1,112 +0,0 @@
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,6 +1,5 @@
apply plugin: VerifyMongoDBConfigurationPlugin
apply plugin: VerifyEventStoreEnvironmentPlugin
apply plugin: EventuateDependencyPlugin
apply plugin: 'spring-boot'
@@ -11,6 +10,8 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "io.eventuate.client.java:eventuate-client-java-http-stomp-spring:$eventuateClientVersion"
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.javaexamples.banking.web.queryside.accounts.GetAccountResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.GetAccountResponse;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@@ -4,7 +4,8 @@ import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.acc
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountNotFoundException;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountQueryService;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountTransactionInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountTransactionInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.GetAccountResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

View File

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

View File

@@ -1,6 +1,5 @@
apply plugin: 'java'
apply plugin: 'spring-boot'
apply plugin: EventuateDependencyPlugin
dependencies {
compile project(":common-auth-web")

View File

@@ -1,7 +1,6 @@
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;
@@ -27,7 +26,7 @@ import java.util.Collections;
@Configuration
@ComponentScan
@EnableAutoConfiguration
@Import({EventuateHttpStompClientConfiguration.class, AuthConfiguration.class})
@Import({AuthConfiguration.class})
@EnableConfigurationProperties({ApiGatewayProperties.class})
public class ApiGatewayServiceConfiguration extends WebMvcConfigurerAdapter {

View File

@@ -12,6 +12,8 @@ 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.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
@@ -25,7 +27,8 @@ import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.stream.Collectors;
import static org.springframework.web.bind.annotation.RequestMethod.*;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
/**
* Created by popikyardo on 15.01.16.
@@ -33,7 +36,7 @@ import static org.springframework.web.bind.annotation.RequestMethod.*;
@RestController
public class GatewayController {
Logger log = LoggerFactory.getLogger(this.getClass());
Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ApiGatewayProperties apiGatewayProperties;
@@ -50,11 +53,12 @@ public class GatewayController {
}
@RequestMapping(value = "/**", method = {GET, POST})
public String proxyRequest(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, IOException, URISyntaxException {
public ResponseEntity<String> proxyRequest(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, IOException, URISyntaxException {
HttpUriRequest proxiedRequest = createHttpUriRequest(request);
log.info("request: {}", proxiedRequest);
logger.info("request: {}", proxiedRequest);
HttpResponse proxiedResponse = httpClient.execute(proxiedRequest);
return read(proxiedResponse.getEntity().getContent());
logger.info("Response {}", proxiedResponse.getStatusLine().getStatusCode());
return new ResponseEntity<>(read(proxiedResponse.getEntity().getContent()), HttpStatus.valueOf(proxiedResponse.getStatusLine().getStatusCode()));
}
private HttpUriRequest createHttpUriRequest(HttpServletRequest request) throws URISyntaxException, NoSuchRequestHandlingMethodException, IOException {

View File

@@ -13,7 +13,6 @@ import java.util.Enumeration;
*/
public class HeadersRequestTransformer extends ProxyRequestTransformer {
@Override
public RequestBuilder transform(HttpServletRequest request) throws NoSuchRequestHandlingMethodException, URISyntaxException, IOException {
RequestBuilder requestBuilder = predecessor.transform(request);

View File

@@ -18,5 +18,5 @@ public abstract class ProxyRequestTransformer {
public void setPredecessor(ProxyRequestTransformer transformer) {
this.predecessor = transformer;
};
}
}

View File

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

View File

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

View File

@@ -3,9 +3,9 @@ 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 org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@Configuration
@Import({AccountConfiguration.class, MoneyTransferConfiguration.class, EventuateJdbcEventStoreConfiguration.class})

View File

@@ -1,7 +1,6 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
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;
@@ -9,8 +8,6 @@ import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.t
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.MoneyTransferService;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.transactions.TransferState;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.transactions.TransferDetails;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -18,7 +15,6 @@ 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;

View File

@@ -19,10 +19,10 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.concurrent.CompletableFuture;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateCustomerInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateToAccountInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.await;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.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.

View File

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

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

View File

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

View File

@@ -2,7 +2,6 @@ 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;

View File

@@ -1,9 +1,8 @@
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.http.*;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.Charset;
@@ -32,14 +31,16 @@ public class BasicAuthUtils {
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"));
httpEntity = new HttpEntity<>(requestObject, BasicAuthUtils.basicAuthHeaders("test_user@mail.com"));
} else {
httpEntity = new HttpEntity(BasicAuthUtils.basicAuthHeaders("test_user@mail.com"));
}
return restTemplate.exchange(url,
ResponseEntity<T> responseEntity = restTemplate.exchange(url,
httpMethod,
httpEntity,
responseType).getBody();
responseType);
Assert.isTrue(HttpStatus.OK == responseEntity.getStatusCode(), "Bad response: " + responseEntity.getStatusCode());
return responseEntity.getBody();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,6 +16,9 @@
<logger name="org.springframework" level='info'>
</logger>
<logger name="net.chrisrichardson.eventstore.javaexamples.banking" level='info'>
</logger>
<logger name="io.eventuate" level='debug'>
</logger>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
apply plugin: 'java'
dependencies {
compile "commons-lang:commons-lang:2.6"
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

@@ -1,8 +1,7 @@
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts;
package net.chrisrichardson.eventstore.javaexamples.banking.common.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;
@@ -22,12 +21,6 @@ public class AccountTransactionInfo {
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ import org.junit.Test;
import java.util.List;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils.generateCustomerInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateCustomerInfo;
public class CustomerTest {
@@ -26,4 +26,6 @@ public class CustomerTest {
customer.applyEvent(events.get(0));
Assert.assertEquals(customerInfo, customer.getCustomerInfo());
}
}

View File

@@ -2,7 +2,6 @@ apply plugin: VerifyMongoDBConfigurationPlugin
apply plugin: VerifyEventStoreEnvironmentPlugin
apply plugin: 'spring-boot'
apply plugin: EventuateDependencyPlugin
dependencies {
compile project(":customers-command-side-web")
@@ -11,6 +10,8 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-actuator"
testCompile project(":testutil-customers")
compile "io.eventuate.client.java:eventuate-client-java-http-stomp-spring:$eventuateClientVersion"
testCompile project(":testutil")
testCompile "org.springframework.boot:spring-boot-starter-test"
}

View File

@@ -24,6 +24,4 @@ public class CustomersCommandSideServiceConfiguration {
HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter();
return new HttpMessageConverters(additional);
}
}

View File

@@ -13,7 +13,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils.generateCustomerInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateCustomerInfo;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = CustomersCommandSideServiceTestConfiguration.class)

View File

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

View File

@@ -5,7 +5,6 @@ import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Quer
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import java.util.Collections;
@@ -16,15 +15,15 @@ public class CustomerInfoUpdateService {
private Logger logger = LoggerFactory.getLogger(getClass());
private QuerySideCustomerRepository querySideCustomerRepository;
private QuerySideCustomerRepository accountInfoRepository;
public CustomerInfoUpdateService(QuerySideCustomerRepository querySideCustomerRepository) {
this.querySideCustomerRepository = querySideCustomerRepository;
public CustomerInfoUpdateService(QuerySideCustomerRepository accountInfoRepository) {
this.accountInfoRepository = accountInfoRepository;
}
public void create(String id, CustomerInfo customerInfo) {
try {
querySideCustomerRepository.save(new QuerySideCustomer(id,
accountInfoRepository.save(new QuerySideCustomer(id,
customerInfo.getName(),
customerInfo.getEmail(),
customerInfo.getSsn(),
@@ -34,8 +33,6 @@ public class CustomerInfoUpdateService {
)
);
logger.info("Saved in mongo");
} catch (DuplicateKeyException t) {
logger.warn("When saving ", t);
} catch (Throwable t) {
logger.error("Error during saving: ", t);
throw new RuntimeException(t);
@@ -43,9 +40,9 @@ public class CustomerInfoUpdateService {
}
public void addToAccount(String id, ToAccountInfo accountInfo) {
QuerySideCustomer customer = querySideCustomerRepository.findOne(id);
QuerySideCustomer customer = accountInfoRepository.findOne(id);
customer.getToAccounts().put(accountInfo.getId(), accountInfo);
querySideCustomerRepository.save(customer);
accountInfoRepository.save(customer);
}
}

View File

@@ -5,7 +5,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
public class QuerySideDependencyChecker {
private Logger logger = LoggerFactory.getLogger(getClass());

View File

@@ -1,6 +1,5 @@
apply plugin: VerifyMongoDBConfigurationPlugin
apply plugin: VerifyEventStoreEnvironmentPlugin
apply plugin: EventuateDependencyPlugin
apply plugin: 'spring-boot'
@@ -11,7 +10,9 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-actuator"
testCompile project(":testutil-customers")
compile "io.eventuate.client.java:eventuate-client-java-http-stomp-spring:$eventuateClientVersion"
testCompile project(":testutil")
testCompile project(":customers-command-side-service")
testCompile "org.springframework.boot:spring-boot-starter-test"
}

View File

@@ -1,9 +1,7 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.*;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -18,8 +16,7 @@ import rx.Observable;
import javax.annotation.PostConstruct;
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.CustomersTestUtils.generateCustomerInfo;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = CustomersQuerySideServiceTestConfiguration.class)

View File

@@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;

View File

@@ -1,129 +0,0 @@
apigateway:
image: java:openjdk-8u91-jdk
working_dir: /app
volumes:
- ./api-gateway-service/build/libs:/app
command: java -jar /app/api-gateway-service.jar --accounts.commandside.service.host=accountscommandside --transactions.commandside.service.host=transactionscommandside --accounts.queryside.service.host=accountsqueryside --customers.commandside.service.host=customerscommandside --customers.queryside.service.host=customersqueryside
ports:
- "8080:8080"
links:
- accountscommandside
- transactionscommandside
- accountsqueryside
- customerscommandside
- customersqueryside
- mongodb
environment:
SPRING_DATASOURCE_URL:
SPRING_DATASOURCE_USERNAME:
SPRING_DATASOURCE_PASSWORD:
SPRING_DATASOURCE_DRIVER_CLASS_NAME:
EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS:
EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING:
SPRING_DATA_MONGODB_URI: mongodb://mongodb/mydb
EVENTUATELOCAL_EMBEDDED_CDC_DB_USER_NAME:
EVENTUATELOCAL_EMBEDDED_CDC_DB_PASSWORD:
accountscommandside:
image: java:openjdk-8u91-jdk
working_dir: /app
volumes:
- ./accounts-command-side-service/build/libs:/app
command: java -jar /app/accounts-command-side-service.jar
ports:
- "8085:8080"
environment:
SPRING_DATASOURCE_URL:
SPRING_DATASOURCE_USERNAME:
SPRING_DATASOURCE_PASSWORD:
SPRING_DATASOURCE_DRIVER_CLASS_NAME:
EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS:
EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING:
EVENTUATELOCAL_EMBEDDED_CDC_DB_USER_NAME:
EVENTUATELOCAL_EMBEDDED_CDC_DB_PASSWORD:
transactionscommandside:
image: java:openjdk-8u91-jdk
working_dir: /app
volumes:
- ./transactions-command-side-service/build/libs:/app
command: java -jar /app/transactions-command-side-service.jar
ports:
- "8082:8080"
environment:
SPRING_DATASOURCE_URL:
SPRING_DATASOURCE_USERNAME:
SPRING_DATASOURCE_PASSWORD:
SPRING_DATASOURCE_DRIVER_CLASS_NAME:
EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS:
EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING:
EVENTUATELOCAL_EMBEDDED_CDC_DB_USER_NAME:
EVENTUATELOCAL_EMBEDDED_CDC_DB_PASSWORD:
accountsqueryside:
image: java:openjdk-8u91-jdk
working_dir: /app
volumes:
- ./accounts-query-side-service/build/libs:/app
command: java -jar /app/accounts-query-side-service.jar
ports:
- "8081:8080"
links:
- mongodb
environment:
SPRING_DATASOURCE_URL:
SPRING_DATASOURCE_USERNAME:
SPRING_DATASOURCE_PASSWORD:
SPRING_DATASOURCE_DRIVER_CLASS_NAME:
EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS:
SPRING_DATA_MONGODB_URI: mongodb://mongodb/mydb
EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING:
EVENTUATELOCAL_EMBEDDED_CDC_DB_USER_NAME:
EVENTUATELOCAL_EMBEDDED_CDC_DB_PASSWORD:
customerscommandside:
image: java:openjdk-8u91-jdk
working_dir: /app
volumes:
- ./customers-command-side-service/build/libs:/app
command: java -jar /app/customers-command-side-service.jar
ports:
- "8083:8080"
environment:
SPRING_DATASOURCE_URL:
SPRING_DATASOURCE_USERNAME:
SPRING_DATASOURCE_PASSWORD:
SPRING_DATASOURCE_DRIVER_CLASS_NAME:
EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS:
EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING:
EVENTUATELOCAL_EMBEDDED_CDC_DB_USER_NAME:
EVENTUATELOCAL_EMBEDDED_CDC_DB_PASSWORD:
customersqueryside:
image: java:openjdk-8u91-jdk
working_dir: /app
volumes:
- ./customers-query-side-service/build/libs:/app
command: java -jar /app/customers-query-side-service.jar
ports:
- "8084:8080"
links:
- mongodb
environment:
SPRING_DATASOURCE_URL:
SPRING_DATASOURCE_USERNAME:
SPRING_DATASOURCE_PASSWORD:
SPRING_DATASOURCE_DRIVER_CLASS_NAME:
EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS:
EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING:
SPRING_DATA_MONGODB_URI: mongodb://mongodb/mydb
EVENTUATELOCAL_EMBEDDED_CDC_DB_USER_NAME:
EVENTUATELOCAL_EMBEDDED_CDC_DB_PASSWORD:
mongodb:
image: mongo:3.0.4
hostname: mongodb
command: mongod --smallfiles
ports:
- "27017:27017"

View File

@@ -3,7 +3,7 @@ apigateway:
working_dir: /app
volumes:
- ./api-gateway-service/build/libs:/app
command: java -jar /app/api-gateway-service.jar --accounts.commandside.service.host=accountscommandside --transactions.commandside.service.host=transactionscommandside --accounts.queryside.service.host=accountsqueryside --customers.commandside.service.host=customerscommandside --customers.queryside.service.host=customersqueryside
command: java -jar /app/api-gateway-service.jar --accounts.commandside.service.host=accountscommandside --transfers.commandside.service.host=transactionscommandside --accounts.queryside.service.host=accountsqueryside --customers.commandside.service.host=customerscommandside --customers.queryside.service.host=customersqueryside
ports:
- "8080:8080"
links:

View File

@@ -5,7 +5,7 @@ dependencies {
testCompile project(":transactions-command-side-web")
testCompile project(":accounts-query-side-web")
testCompile project(":testutil-customers")
testCompile project(":testutil")
testCompile project(":common-auth")
testCompile "junit:junit:4.11"
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"

View File

@@ -1,199 +1,39 @@
package net.chrisrichardson.eventstore.examples.bank.web;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountTransactionInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.utils.BasicAuthUtils;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions.CreateMoneyTransferRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions.CreateMoneyTransferResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.accounts.GetAccountResponse;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.AbstractRestAPITest;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.AuthenticatedRestTemplate;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils.generateCustomerInfo;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class EndToEndTest {
public class EndToEndTest extends AbstractRestAPITest {
private String getenv(String name, String defaultValue) {
String x = System.getenv(name);
return x == null ? defaultValue : x;
}
private String makeBaseUrl(int port, String path) {
return "http://" + getenv("SERVICE_HOST", "localhost") + ":" + port + "/" + path;
}
private String accountsCommandSideBaseUrl(String path) {
return makeBaseUrl(8080, path);
}
private String accountsQuerySideBaseUrl(String path) {
return makeBaseUrl(8081, path);
}
private String transactionsCommandSideBaseUrl(String path) {
return makeBaseUrl(8082, path);
}
private String customersCommandSideBaseUrl(String path) {
return makeBaseUrl(8083, path);
}
private String customersQuerySideBaseUrl(String path) {
return makeBaseUrl(8084, path);
}
RestTemplate restTemplate = new RestTemplate();
CustomersTestUtils customersTestUtils = new CustomersTestUtils(restTemplate, customersQuerySideBaseUrl("/customers/"));
CustomersTestUtils customersTestUtils = new CustomersTestUtils(restTemplate, baseUrl("/customers/"));
@Test
public void shouldCreateCustomerAndAccountsAndTransferMoney() {
CustomerInfo customerInfo = generateCustomerInfo();
BigDecimal initialFromAccountBalance = new BigDecimal(500);
BigDecimal initialToAccountBalance = new BigDecimal(100);
BigDecimal amountToTransfer = new BigDecimal(150);
BigDecimal finalFromAccountBalance = initialFromAccountBalance.subtract(amountToTransfer);
BigDecimal finalToAccountBalance = initialToAccountBalance.add(amountToTransfer);
final CustomerResponse customerResponse = restTemplate.postForEntity(customersCommandSideBaseUrl("/customers"),customerInfo, CustomerResponse.class).getBody();
final String customerId = customerResponse.getId();
customersTestUtils.assertCustomerResponse(customerId, customerInfo);
final CreateAccountResponse fromAccount = BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
accountsCommandSideBaseUrl("/accounts"),
HttpMethod.POST,
CreateAccountResponse.class,
new CreateAccountRequest(customerId, "My #1 Account", "", initialFromAccountBalance)
);
final String fromAccountId = fromAccount.getAccountId();
CreateAccountResponse toAccount = BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
accountsCommandSideBaseUrl("/accounts"),
HttpMethod.POST,
CreateAccountResponse.class,
new CreateAccountRequest(customerId, "My #2 Account", "", initialToAccountBalance)
);
String toAccountId = toAccount.getAccountId();
Assert.assertNotNull(fromAccountId);
Assert.assertNotNull(toAccountId);
assertAccountBalance(fromAccountId, initialFromAccountBalance);
assertAccountBalance(toAccountId, initialToAccountBalance);
final CreateMoneyTransferResponse moneyTransfer = BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
transactionsCommandSideBaseUrl("/transfers"),
HttpMethod.POST,
CreateMoneyTransferResponse.class,
new CreateMoneyTransferRequest(fromAccountId, toAccountId, amountToTransfer, "")
);
assertAccountBalance(fromAccountId, finalFromAccountBalance);
assertAccountBalance(toAccountId, finalToAccountBalance);
// TOOD - check state of money transfer
eventually(
() -> CompletableFuture.completedFuture(restTemplate.exchange(accountsQuerySideBaseUrl("/accounts/"+fromAccountId+"/history"),
HttpMethod.GET,
new HttpEntity(BasicAuthUtils.basicAuthHeaders("test_user@mail.com")),
new ParameterizedTypeReference<List<AccountTransactionInfo>>() {}).getBody()),
transactionInfoList -> {
if (!(transactionInfoList.stream().filter(ti -> ti.getTransactionId().equals(moneyTransfer.getMoneyTransferId()) &&
ti.getFromAccountId().equals(fromAccountId) &&
ti.getToAccountId().equals(toAccountId) &&
ti.getAmount() == toCents(amountToTransfer).longValue()).findFirst().isPresent())) {
fail(String.format("%s does not contain %s %s %s",
moneyTransfer.getMoneyTransferId(),
fromAccount,
toAccount,
toCents(amountToTransfer).longValue()));
public String baseUrl(String path) {
return "http://" + getenv("SERVICE_HOST", "localhost") + ":" + 8080 + "/" + path;
}
}
);
}
@Test
public void shouldCreateAccountsAndGetByCustomer() {
BigDecimal initialFromAccountBalance = new BigDecimal(500);
CustomerInfo customerInfo = generateCustomerInfo();
final CustomerResponse customerResponse = restTemplate.postForEntity(customersCommandSideBaseUrl("/customers"), customerInfo, CustomerResponse.class).getBody();
final String customerId = customerResponse.getId();
Assert.assertNotNull(customerId);
Assert.assertEquals(customerInfo, customerResponse.getCustomerInfo());
customersTestUtils.assertCustomerResponse(customerId, customerInfo);
final CreateAccountResponse account = BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
accountsCommandSideBaseUrl("/accounts"),
HttpMethod.POST,
CreateAccountResponse.class,
new CreateAccountRequest(customerId, "My 1 Account", "", initialFromAccountBalance)
);
final String accountId = account.getAccountId();
Assert.assertNotNull(accountId);
assertAccountBalance(accountId, initialFromAccountBalance);
List<GetAccountResponse> accountResponseList = restTemplate.exchange(accountsQuerySideBaseUrl("/accounts?customerId="+customerId),
HttpMethod.GET,
new HttpEntity(BasicAuthUtils.basicAuthHeaders("test_user@mail.com")),
new ParameterizedTypeReference<List<GetAccountResponse>>() {}).getBody();
assertTrue(accountResponseList.stream().filter(acc -> acc.getAccountId().equals(accountId)).findFirst().isPresent());
}
private BigDecimal toCents(BigDecimal dollarAmount) {
return dollarAmount.multiply(new BigDecimal(100));
}
private void assertAccountBalance(final String accountId, final BigDecimal expectedBalanceInDollars) {
final BigDecimal inCents = toCents(expectedBalanceInDollars);
eventually(
new Producer<GetAccountResponse>() {
@Override
public CompletableFuture<GetAccountResponse> produce() {
return CompletableFuture.completedFuture(BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
accountsQuerySideBaseUrl("/accounts/" + accountId),
HttpMethod.GET,
GetAccountResponse.class));
public CustomersTestUtils getCustomersTestUtils() {
return customersTestUtils;
}
},
new Verifier<GetAccountResponse>() {
@Override
public void verify(GetAccountResponse accountInfo) {
Assert.assertEquals(accountId, accountInfo.getAccountId());
Assert.assertEquals(accountId, inCents, accountInfo.getBalance());
public AuthenticatedRestTemplate getAuthenticatedRestTemplate() {
return new AuthenticatedRestTemplate(restTemplate);
}
});
@Override
public RestTemplate getRestTemplate() {
return restTemplate;
}
}

View File

@@ -6,5 +6,4 @@ eventuateMavenRepoUrl=http://mavenrepo.eventuate.io/release
springBootVersion=1.3.5.RELEASE
eventuateClientVersion=0.8.0.RELEASE
eventuateLocalVersion=0.2.0.RELEASE

View File

@@ -1,4 +1,3 @@
#! /bin/bash
docker run --link javaspring_mongodb_1:mongodb -i -t mongo:3.0.4 /usr/bin/mongo --host mongodb
docker run --rm --link javaspring_mongodb_1:mongodb -i -t mongo:3.0.4 /usr/bin/mongo --host mongodb

View File

@@ -14,11 +14,12 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"
compile project(":common-swagger")
compile "io.eventuate.client.java:eventuate-client-java-http-stomp-spring:$eventuateClientVersion"
testCompile project(":testutil-customers")
testCompile project(":testutil")
testCompile "org.springframework.boot:spring-boot-starter-test"
testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"
}
task copyWebStatic(type: Copy) {

View File

@@ -1,8 +1,8 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web;
import io.eventuate.javaclient.spring.httpstomp.EventuateHttpStompClientConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.AuthConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration;
import io.eventuate.javaclient.spring.jdbc.EventuateJdbcEventStoreConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CommandSideWebAccountsConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.customers.CustomersCommandSideWebConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions.CommandSideWebTransactionsConfiguration;
@@ -11,7 +11,6 @@ import net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.QuerySi
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
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.converter.HttpMessageConverter;
@@ -20,9 +19,8 @@ import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
@Import({CommandSideWebAccountsConfiguration.class, CommandSideWebTransactionsConfiguration.class, EventuateJdbcEventStoreConfiguration.class, QuerySideWebConfiguration.class, CustomersQuerySideWebConfiguration.class, CustomersCommandSideWebConfiguration.class, AuthConfiguration.class, CommonSwaggerConfiguration.class})
@Import({CommandSideWebAccountsConfiguration.class, CommandSideWebTransactionsConfiguration.class, EventuateHttpStompClientConfiguration.class, QuerySideWebConfiguration.class, CustomersQuerySideWebConfiguration.class, CustomersCommandSideWebConfiguration.class, AuthConfiguration.class, CommonSwaggerConfiguration.class})
@EnableAutoConfiguration
@ComponentScan
public class BankingWebConfiguration extends WebMvcConfigurerAdapter {

View File

@@ -4,7 +4,7 @@ import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Cust
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.QuerySideCustomer;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.AuthRequest;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -18,7 +18,7 @@ import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils.generateCustomerInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateCustomerInfo;
/**
* Created by Main on 15.02.2016.

View File

@@ -1,58 +1,28 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web;
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountTransactionInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.*;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.utils.BasicAuthUtils;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CreateAccountResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions.CreateMoneyTransferRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions.CreateMoneyTransferResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.accounts.GetAccountResponse;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.customers.CustomersTestUtils;
import org.junit.Assert;
import org.junit.Test;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.AbstractRestAPITest;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.AuthenticatedRestTemplate;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import javax.annotation.PostConstruct;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BankingWebTestConfiguration.class)
@WebAppConfiguration
@IntegrationTest({"server.port=0", "management.port=0"})
public class BankingWebIntegrationTest {
public class BankingWebIntegrationTest extends AbstractRestAPITest {
@Value("${local.server.port}")
private int port;
private String baseUrl(String path) {
return "http://localhost:" + port + "/" + path;
}
@Autowired
RestTemplate restTemplate;
CustomersTestUtils customersTestUtils;
@PostConstruct
@@ -60,176 +30,26 @@ public class BankingWebIntegrationTest {
customersTestUtils = new CustomersTestUtils(restTemplate, baseUrl("/customers/"));
}
@Test
public void shouldCreateAccountsAndTransferMoney() {
BigDecimal initialFromAccountBalance = new BigDecimal(500);
BigDecimal initialToAccountBalance = new BigDecimal(100);
BigDecimal amountToTransfer = new BigDecimal(150);
BigDecimal finalFromAccountBalance = initialFromAccountBalance.subtract(amountToTransfer);
BigDecimal finalToAccountBalance = initialToAccountBalance.add(amountToTransfer);
final CreateAccountResponse fromAccount = BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
baseUrl("/accounts"),
HttpMethod.POST,
CreateAccountResponse.class,
new CreateAccountRequest("00000000-00000000", "My 1 Account", "", initialFromAccountBalance)
);
final String fromAccountId = fromAccount.getAccountId();
CreateAccountResponse toAccount = BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
baseUrl("/accounts"),
HttpMethod.POST,
CreateAccountResponse.class,
new CreateAccountRequest("00000000-00000000", "My 2 Account", "", initialToAccountBalance)
);
String toAccountId = toAccount.getAccountId();
Assert.assertNotNull(fromAccountId);
Assert.assertNotNull(toAccountId);
assertAccountBalance(fromAccountId, initialFromAccountBalance);
assertAccountBalance(toAccountId, initialToAccountBalance);
final CreateMoneyTransferResponse moneyTransfer = BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
baseUrl("/transfers"),
HttpMethod.POST,
CreateMoneyTransferResponse.class,
new CreateMoneyTransferRequest(fromAccountId, toAccountId, amountToTransfer, "")
);
assertAccountBalance(fromAccountId, finalFromAccountBalance);
assertAccountBalance(toAccountId, finalToAccountBalance);
eventually(
() -> CompletableFuture.completedFuture(restTemplate.exchange(baseUrl("/accounts/"+fromAccountId+"/history"),
HttpMethod.GET,
new HttpEntity(BasicAuthUtils.basicAuthHeaders("test_user@mail.com")),
new ParameterizedTypeReference<List<AccountTransactionInfo>>() {}).getBody()),
transactionInfoList -> {
Optional<AccountTransactionInfo> txn = transactionInfoList.stream()
.filter(ti -> ti.getTransactionId().equals(moneyTransfer.getMoneyTransferId()))
.findFirst();
if (!txn.isPresent()) {
fail(String.format("%s does not contain %s", transactionInfoList, moneyTransfer.getMoneyTransferId()));
}
AccountTransactionInfo ti = txn.get();
assertEquals(fromAccountId, ti.getFromAccountId());
assertEquals(toAccountId, ti.getToAccountId());
assertEquals(toAccountId, ti.getToAccountId());
assertEquals(fromAccountId, ti.getFromAccountId());
assertEquals(toCents(amountToTransfer).longValue(), ti.getAmount());
}
);
}
@Test
public void shouldCreateAccountsAndGetByCustomer() {
BigDecimal initialFromAccountBalance = new BigDecimal(500);
CustomerInfo customerInfo = generateCustomerInfo();
final CustomerResponse customerResponse = restTemplate.postForEntity(baseUrl("/customers"), customerInfo, CustomerResponse.class).getBody();
final String customerId = customerResponse.getId();
Assert.assertNotNull(customerId);
assertEquals(customerInfo, customerResponse.getCustomerInfo());
customersTestUtils.assertCustomerResponse(customerId, customerInfo);
final CreateAccountResponse account = BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
baseUrl("/accounts"),
HttpMethod.POST,
CreateAccountResponse.class,
new CreateAccountRequest(customerId, "My 1 Account", "", initialFromAccountBalance)
);
final String accountId = account.getAccountId();
Assert.assertNotNull(accountId);
assertAccountBalance(accountId, initialFromAccountBalance);
List<GetAccountResponse> accountResponseList = restTemplate.exchange(baseUrl("/accounts?customerId="+customerId),
HttpMethod.GET,
new HttpEntity(BasicAuthUtils.basicAuthHeaders("test_user@mail.com")),
new ParameterizedTypeReference<List<GetAccountResponse>>() {}).getBody();
assertTrue(accountResponseList.stream().filter(acc -> acc.getAccountId().equals(accountId)).findFirst().isPresent());
}
@Test
public void shouldCreateCustomersAndAddToAccount() {
CustomerInfo customerInfo = generateCustomerInfo();
final CustomerResponse customerResponse = restTemplate.postForEntity(baseUrl("/customers"), customerInfo, CustomerResponse.class).getBody();
final String customerId = customerResponse.getId();
Assert.assertNotNull(customerId);
assertEquals(customerInfo, customerResponse.getCustomerInfo());
customersTestUtils.assertCustomerResponse(customerId, customerInfo);
ToAccountInfo toAccountInfo = generateToAccountInfo();
BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
baseUrl("/customers/"+customerId+"/toaccounts"),
HttpMethod.POST,
null,
toAccountInfo
);
assertToAccountsContains(customerId, toAccountInfo);
}
private BigDecimal toCents(BigDecimal dollarAmount) {
return dollarAmount.multiply(new BigDecimal(100));
}
private void assertAccountBalance(final String fromAccountId, final BigDecimal expectedBalanceInDollars) {
final BigDecimal inCents = toCents(expectedBalanceInDollars);
eventually(
new Producer<GetAccountResponse>() {
@Override
public CompletableFuture<GetAccountResponse> produce() {
return CompletableFuture.completedFuture(BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
baseUrl("/accounts/" + fromAccountId),
HttpMethod.GET,
GetAccountResponse.class));
}
},
new Verifier<GetAccountResponse>() {
@Override
public void verify(GetAccountResponse accountInfo) {
assertEquals(fromAccountId, accountInfo.getAccountId());
assertEquals(inCents, accountInfo.getBalance());
}
});
public String baseUrl(String path) {
return "http://localhost:" + port + "/" + path;
}
private void assertToAccountsContains(final String customerId, final ToAccountInfo toAccountInfo) {
eventually(
new Producer<QuerySideCustomer>() {
@Override
public CompletableFuture<QuerySideCustomer> produce() {
return CompletableFuture.completedFuture(BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
baseUrl("/customers/" + customerId),
HttpMethod.GET,
QuerySideCustomer.class));
public CustomersTestUtils getCustomersTestUtils() {
return customersTestUtils;
}
},
new Verifier<QuerySideCustomer>() {
@Autowired
RestTemplate restTemplate;
@Override
public void verify(QuerySideCustomer customerResponse) {
assertEquals(customerId, customerResponse.getId());
assertTrue(customerResponse.getToAccounts().values().stream().anyMatch(t -> t.equals(toAccountInfo)));
public AuthenticatedRestTemplate getAuthenticatedRestTemplate() {
return new AuthenticatedRestTemplate(restTemplate);
}
});
@Override
public RestTemplate getRestTemplate() {
return restTemplate;
}
}

View File

@@ -1,23 +1,39 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import io.eventuate.javaclient.spring.jdbc.EventuateJdbcEventStoreConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.AuthConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.accounts.CommandSideWebAccountsConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.customers.CustomersCommandSideWebConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.transactions.CommandSideWebTransactionsConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.customers.queryside.CustomersQuerySideWebConfiguration;
import net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.QuerySideWebConfiguration;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.RestTemplateErrorHandler;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
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.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;
@Configuration
@Import(BankingWebConfiguration.class)
public class BankingWebTestConfiguration {
@Import({CommandSideWebAccountsConfiguration.class, CommandSideWebTransactionsConfiguration.class, EventuateJdbcEventStoreConfiguration.class, QuerySideWebConfiguration.class, CustomersQuerySideWebConfiguration.class, CustomersCommandSideWebConfiguration.class, AuthConfiguration.class, CommonSwaggerConfiguration.class})
@EnableAutoConfiguration
public class BankingWebTestConfiguration extends WebMvcConfigurerAdapter {
@Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter();
return new HttpMessageConverters(additional);
}
@Bean
public RestTemplate restTemplate(HttpMessageConverters converters) {
@@ -25,6 +41,8 @@ public class BankingWebTestConfiguration {
HttpMessageConverter<?> httpMessageConverter = converters.getConverters().get(0);
List<? extends HttpMessageConverter<?>> httpMessageConverters = Arrays.asList(new MappingJackson2HttpMessageConverter());
restTemplate.setMessageConverters((List<HttpMessageConverter<?>>) httpMessageConverters);
restTemplate.setErrorHandler(new RestTemplateErrorHandler());
return restTemplate;
}
}

View File

@@ -1,37 +1,35 @@
include 'testutil'
include 'common'
include 'common-backend'
include 'common-auth'
include 'common-auth-web'
include 'common-swagger'
include 'common-backend'
include 'accounts-command-side-backend'
include 'transactions-command-side-backend'
include 'accounts-command-side-web'
include 'transactions-command-side-web'
include 'accounts-command-side-service'
include 'accounts-query-side-backend'
include 'accounts-query-side-web'
include 'accounts-query-side-service'
include 'backend-integration-tests'
include 'transactions-command-side-backend'
include 'transactions-command-side-web'
include 'transactions-command-side-service'
include 'customers-command-side-backend'
include 'customers-command-side-web'
include 'customers-command-side-service'
include 'customers-query-side-backend'
include 'customers-query-side-web'
include 'customers-query-side-service'
include 'api-gateway-service'
include 'monolithic-service'
include 'accounts-command-side-service'
include 'accounts-query-side-service'
include 'transactions-command-side-service'
include 'backend-integration-tests'
include 'e2e-test'
rootProject.name = 'java-spring-event-sourcing-example'
include 'common-auth'
include 'customers-command-side-backend'
include 'customers-command-side-web'
include 'customers-query-side-backend'
include 'customers-query-side-web'
include 'common-customers'
include 'customers-command-side-service'
include 'customers-query-side-service'
include 'common-auth-web'
include 'api-gateway-service'
include 'testutil-customers'

7
java-spring/show-urls.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash -e
IP=$(docker-machine ip default)
echo Accounts command-side service = http://${IP}:8080/swagger-ui.html
echo Money Transfers command-side service = http://${IP}:8082/swagger-ui.html
echo Accounts query-side service = http://${IP}:8081/swagger-ui.html

View File

@@ -1,10 +0,0 @@
apply plugin: 'java'
dependencies {
compile project(":testutil")
compile project(":common-auth")
compile project(":common-customers")
compile "io.reactivex:rxjava:1.1.5"
compile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
}

View File

@@ -1,77 +0,0 @@
package net.chrisrichardson.eventstorestore.javaexamples.testutil.customers;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.*;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.utils.BasicAuthUtils;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
import org.junit.Assert;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.CompletableFuture;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
/**
* Created by popikyardo on 02.03.16.
*/
public class CustomersTestUtils {
private RestTemplate restTemplate;
private String customersBaseUrl;
public CustomersTestUtils(RestTemplate restTemplate, String customersBaseUrl) {
this.restTemplate = restTemplate;
this.customersBaseUrl = customersBaseUrl;
}
public void assertCustomerResponse(final String customerId, final CustomerInfo customerInfo) {
eventually(
new Producer<QuerySideCustomer>() {
@Override
public CompletableFuture<QuerySideCustomer> produce() {
return CompletableFuture.completedFuture(BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
customersBaseUrl + customerId,
HttpMethod.GET,
QuerySideCustomer.class));
}
},
new Verifier<QuerySideCustomer>() {
@Override
public void verify(QuerySideCustomer querySideCustomer) {
Assert.assertEquals(customerId, querySideCustomer.getId());
assertQuerySideCustomerEqualscCustomerInfo(querySideCustomer, customerInfo);
}
});
}
public void assertQuerySideCustomerEqualscCustomerInfo(QuerySideCustomer querySideCustomer, CustomerInfo customerInfo) {
Assert.assertEquals(querySideCustomer.getName(), customerInfo.getName());
Assert.assertEquals(querySideCustomer.getEmail(), customerInfo.getEmail());
Assert.assertEquals(querySideCustomer.getPhoneNumber(), customerInfo.getPhoneNumber());
Assert.assertEquals(querySideCustomer.getSsn(), customerInfo.getSsn());
Assert.assertEquals(querySideCustomer.getAddress(), customerInfo.getAddress());
}
public static CustomerInfo generateCustomerInfo() {
return generateCustomerInfo("current@email.com");
}
public static CustomerInfo generateCustomerInfo(String email) {
return new CustomerInfo(
new Name("John", "Doe"),
email,
"000-00-0000",
"1-111-111-1111",
new Address("street 1",
"street 2",
"City",
"State",
"1111111")
);
}
public static ToAccountInfo generateToAccountInfo() {
return new ToAccountInfo("11111111-11111111", "New Account", "John Doe","");
}
}

View File

@@ -1,13 +1,12 @@
apply plugin: 'java'
dependencies {
compile project(":common")
compile project(":common-auth")
compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion"
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
compile "junit:junit:4.11"
compile "io.reactivex:rxjava:1.1.5"
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion"
}

View File

@@ -0,0 +1,198 @@
package net.chrisrichardson.eventstorestore.javaexamples.testutil;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.AccountTransactionInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.CreateAccountRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.CreateAccountResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.common.accounts.GetAccountResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerResponse;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.QuerySideCustomer;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
import net.chrisrichardson.eventstore.javaexamples.banking.common.transactions.CreateMoneyTransferRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.common.transactions.CreateMoneyTransferResponse;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateCustomerInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils.generateToAccountInfo;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public abstract class AbstractRestAPITest {
@Test
public void shouldCreateAccountsAndTransferMoney() {
BigDecimal initialFromAccountBalance = new BigDecimal(500);
BigDecimal initialToAccountBalance = new BigDecimal(100);
BigDecimal amountToTransfer = new BigDecimal(150);
BigDecimal finalFromAccountBalance = initialFromAccountBalance.subtract(amountToTransfer);
BigDecimal finalToAccountBalance = initialToAccountBalance.add(amountToTransfer);
final CreateAccountResponse fromAccount = getAuthenticatedRestTemplate().postForEntity(baseUrl("/accounts"),
new CreateAccountRequest("00000000-00000000", "My 1 Account", "", initialFromAccountBalance),
CreateAccountResponse.class);
final String fromAccountId = fromAccount.getAccountId();
CreateAccountResponse toAccount = getAuthenticatedRestTemplate().postForEntity(baseUrl("/accounts"),
new CreateAccountRequest("00000000-00000000", "My 2 Account", "", initialToAccountBalance),
CreateAccountResponse.class);
String toAccountId = toAccount.getAccountId();
Assert.assertNotNull(fromAccountId);
Assert.assertNotNull(toAccountId);
assertAccountBalance(fromAccountId, initialFromAccountBalance);
assertAccountBalance(toAccountId, initialToAccountBalance);
final CreateMoneyTransferResponse moneyTransfer = getAuthenticatedRestTemplate().postForEntity(baseUrl("/transfers"),
new CreateMoneyTransferRequest(fromAccountId, toAccountId, amountToTransfer, ""),
CreateMoneyTransferResponse.class);
assertAccountBalance(fromAccountId, finalFromAccountBalance);
assertAccountBalance(toAccountId, finalToAccountBalance);
eventually(
new Producer<AccountTransactionInfo[]>() {
@Override
public CompletableFuture<AccountTransactionInfo[]> produce() {
return CompletableFuture.completedFuture(getAuthenticatedRestTemplate().getForEntity(baseUrl("/accounts/" + fromAccountId + "/history"),
AccountTransactionInfo[].class));
}
},
new Verifier<AccountTransactionInfo[]>() {
@Override
public void verify(AccountTransactionInfo[] transactionInfos) {
Optional<AccountTransactionInfo> first = Arrays.asList(transactionInfos).stream().filter(ti -> ti.getTransactionId().equals(moneyTransfer.getMoneyTransferId())).findFirst();
assertTrue(first.isPresent());
AccountTransactionInfo ti = first.get();
assertEquals(fromAccountId, ti.getFromAccountId());
assertEquals(toAccountId, ti.getToAccountId());
assertEquals(toAccountId, ti.getToAccountId());
assertEquals(fromAccountId, ti.getFromAccountId());
assertEquals(toCents(amountToTransfer).longValue(), ti.getAmount());
}
});
}
@Test
public void shouldCreateAccountsAndGetByCustomer() {
BigDecimal initialFromAccountBalance = new BigDecimal(500);
CustomerInfo customerInfo = generateCustomerInfo();
final CustomerResponse customerResponse = getRestTemplate().postForEntity(baseUrl("/customers"), customerInfo, CustomerResponse.class).getBody();
final String customerId = customerResponse.getId();
Assert.assertNotNull(customerId);
assertEquals(customerInfo, customerResponse.getCustomerInfo());
getCustomersTestUtils().assertCustomerResponse(customerId, customerInfo);
final CreateAccountResponse account = getAuthenticatedRestTemplate().postForEntity(baseUrl("/accounts"),
new CreateAccountRequest(customerId, "My 1 Account", "", initialFromAccountBalance),
CreateAccountResponse.class);
final String accountId = account.getAccountId();
Assert.assertNotNull(accountId);
assertAccountBalance(accountId, initialFromAccountBalance);
eventually(
new Producer<GetAccountResponse[]>() {
@Override
public CompletableFuture<GetAccountResponse[]> produce() {
return CompletableFuture.completedFuture(getAuthenticatedRestTemplate().getForEntity(baseUrl("/accounts?customerId=" + customerId),
GetAccountResponse[].class));
}
},
new Verifier<GetAccountResponse[]>() {
@Override
public void verify(GetAccountResponse[] accountResponses) {
assertTrue(Arrays.asList(accountResponses).stream().filter(acc -> acc.getAccountId().equals(accountId)).findFirst().isPresent());
}
});
}
@Test
public void shouldCreateCustomersAndAddToAccount() {
CustomerInfo customerInfo = generateCustomerInfo();
final CustomerResponse customerResponse = getRestTemplate().postForEntity(baseUrl("/customers"), customerInfo, CustomerResponse.class).getBody();
final String customerId = customerResponse.getId();
Assert.assertNotNull(customerId);
assertEquals(customerInfo, customerResponse.getCustomerInfo());
getCustomersTestUtils().assertCustomerResponse(customerId, customerInfo);
ToAccountInfo toAccountInfo = generateToAccountInfo();
getAuthenticatedRestTemplate().postForEntity(baseUrl("/customers/" + customerId + "/toaccounts"),
toAccountInfo,
null);
assertToAccountsContains(customerId, toAccountInfo);
}
private BigDecimal toCents(BigDecimal dollarAmount) {
return dollarAmount.multiply(new BigDecimal(100));
}
private void assertAccountBalance(final String fromAccountId, final BigDecimal expectedBalanceInDollars) {
final BigDecimal inCents = toCents(expectedBalanceInDollars);
eventually(
new Producer<GetAccountResponse>() {
@Override
public CompletableFuture<GetAccountResponse> produce() {
return CompletableFuture.completedFuture(getAuthenticatedRestTemplate().getForEntity(baseUrl("/accounts/" + fromAccountId),
GetAccountResponse.class));
}
},
new Verifier<GetAccountResponse>() {
@Override
public void verify(GetAccountResponse accountInfo) {
assertEquals(fromAccountId, accountInfo.getAccountId());
assertEquals(inCents, accountInfo.getBalance());
}
});
}
private void assertToAccountsContains(final String customerId, final ToAccountInfo toAccountInfo) {
eventually(
new Producer<QuerySideCustomer>() {
@Override
public CompletableFuture<QuerySideCustomer> produce() {
return CompletableFuture.completedFuture(getAuthenticatedRestTemplate().getForEntity(baseUrl("/customers/" + customerId),
QuerySideCustomer.class));
}
},
new Verifier<QuerySideCustomer>() {
@Override
public void verify(QuerySideCustomer customerResponse) {
assertEquals(customerId, customerResponse.getId());
assertTrue(customerResponse.getToAccounts().values().stream().anyMatch(t -> t.equals(toAccountInfo)));
}
});
}
public abstract AuthenticatedRestTemplate getAuthenticatedRestTemplate();
public abstract RestTemplate getRestTemplate();
public abstract String baseUrl(String path);
public abstract CustomersTestUtils getCustomersTestUtils();
}

View File

@@ -0,0 +1,30 @@
package net.chrisrichardson.eventstorestore.javaexamples.testutil;
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.utils.BasicAuthUtils;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RestTemplate;
public class AuthenticatedRestTemplate {
private RestTemplate restTemplate;
public AuthenticatedRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public <T> T getForEntity(String url, Class<T> clazz) {
return BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
url,
HttpMethod.GET,
clazz);
}
public <T> T postForEntity(String url, Object requestObject, Class<T> clazz) {
return BasicAuthUtils.doBasicAuthenticatedRequest(restTemplate,
url,
HttpMethod.POST,
clazz,
requestObject
);
}
}

View File

@@ -0,0 +1,71 @@
package net.chrisrichardson.eventstorestore.javaexamples.testutil;
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.*;
import org.junit.Assert;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.CompletableFuture;
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
/**
* Created by popikyardo on 02.03.16.
*/
public class CustomersTestUtils {
private RestTemplate restTemplate;
private String customersBaseUrl;
public CustomersTestUtils(RestTemplate restTemplate, String customersBaseUrl) {
this.restTemplate = restTemplate;
this.customersBaseUrl = customersBaseUrl;
}
public void assertCustomerResponse(final String customerId, final CustomerInfo customerInfo) {
AuthenticatedRestTemplate art = new AuthenticatedRestTemplate(restTemplate);
eventually(
new Producer<QuerySideCustomer>() {
@Override
public CompletableFuture<QuerySideCustomer> produce() {
return CompletableFuture.completedFuture(art.getForEntity(customersBaseUrl + customerId, QuerySideCustomer.class));
}
},
new Verifier<QuerySideCustomer>() {
@Override
public void verify(QuerySideCustomer querySideCustomer) {
Assert.assertEquals(customerId, querySideCustomer.getId());
assertQuerySideCustomerEqualscCustomerInfo(querySideCustomer, customerInfo);
}
});
}
public void assertQuerySideCustomerEqualscCustomerInfo(QuerySideCustomer querySideCustomer, CustomerInfo customerInfo) {
Assert.assertEquals(querySideCustomer.getName(), customerInfo.getName());
Assert.assertEquals(querySideCustomer.getEmail(), customerInfo.getEmail());
Assert.assertEquals(querySideCustomer.getPhoneNumber(), customerInfo.getPhoneNumber());
Assert.assertEquals(querySideCustomer.getSsn(), customerInfo.getSsn());
Assert.assertEquals(querySideCustomer.getAddress(), customerInfo.getAddress());
}
public static CustomerInfo generateCustomerInfo() {
return generateCustomerInfo("current@email.com");
}
public static CustomerInfo generateCustomerInfo(String email) {
return new CustomerInfo(
new Name("John", "Doe"),
email,
"000-00-0000",
"1-111-111-1111",
new Address("street 1",
"street 2",
"City",
"State",
"1111111")
);
}
public static ToAccountInfo generateToAccountInfo() {
return new ToAccountInfo("11111111-11111111", "New Account", "John Doe", "");
}
}

View File

@@ -0,0 +1,24 @@
package net.chrisrichardson.eventstorestore.javaexamples.testutil;
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,12 @@
package net.chrisrichardson.eventstorestore.javaexamples.testutil;
import org.springframework.http.HttpStatus;
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

@@ -54,26 +54,51 @@ public class TestUtil {
}
}
public static <T> void eventually(Producer<T> producer, Verifier<T> predicate) {
Throwable laste = null;
for (int i = 0; i < 30 ; i++) {
public static <T> void eventually(final Producer<T> producer, final Verifier<T> verifier) {
final int n = 50;
Object possibleException = Observable.timer(0, 200, TimeUnit.MILLISECONDS).flatMap(new Func1<Long, Observable<Outcome<T>>>() {
@Override
public Observable<Outcome<T>> call(Long aLong) {
try {
T x = producer.produce().get(30, TimeUnit.SECONDS);
predicate.verify(x);
return;
} catch (Throwable t) {
laste = t;
return fromCompletableFuture(producer.produce()).map(new Func1<T, Outcome<T>>() {
@Override
public Outcome<T> call(T t) {
return new Success<T>(t);
}
});
} catch (Exception e) {
Outcome<T> value = new Failure<T>(e);
return Observable.just(value);
}
}
}).map(new Func1<Outcome<T>, Throwable>() {
@Override
public Throwable call(Outcome<T> t) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
if (t instanceof Success) {
verifier.verify(((Success<T>) t).value);
return null;
} else
return ((Failure<T>) t).t;
} catch (Throwable e) {
return e;
}
}
if (laste != null)
throw new RuntimeException("Last exception was", laste);
else
throw new RuntimeException("predicate never satisfied");
}).take(n).zipWith(Observable.range(0, n), new Func2<Throwable, Integer, Tuple2<Throwable, Integer>>() {
@Override
public Tuple2<Throwable, Integer> call(Throwable e, Integer idx) {
return new Tuple2<Throwable, Integer>(e, idx);
}
}).skipWhile(new Func1<Tuple2<Throwable, Integer>, Boolean>() {
@Override
public Boolean call(Tuple2<Throwable, Integer> tuple2) {
return tuple2.first != null && tuple2.second < n - 1;
}
}).first().toBlocking().getIterator().next().first;
if (possibleException != null)
throw new RuntimeException((Throwable) possibleException);
}
private static <T> Observable<T> fromCompletableFuture(CompletableFuture<T> future) {

View File

@@ -1,5 +1,4 @@
apply plugin: 'spring-boot'
apply plugin: EventuateDependencyPlugin
apply plugin: VerifyEventStoreEnvironmentPlugin
@@ -10,6 +9,8 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "io.eventuate.client.java:eventuate-client-java-http-stomp-spring:$eventuateClientVersion"
testCompile "org.springframework.boot:spring-boot-starter-test"
}

View File

@@ -3,6 +3,8 @@ package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.tran
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.common.transactions.CreateMoneyTransferRequest;
import net.chrisrichardson.eventstore.javaexamples.banking.common.transactions.CreateMoneyTransferResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
@@ -10,9 +12,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
@RestController
@RequestMapping("/transfers")