Merge remote-tracking branch 'remotes/upstream/wip-eventuate-local' into wip-customer

This commit is contained in:
dartpopikyardo
2016-08-30 19:05:44 +03:00
21 changed files with 514 additions and 64 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,96 @@
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));
}
}

View File

@@ -1,5 +1,6 @@
apply plugin: VerifyMongoDBConfigurationPlugin
apply plugin: VerifyEventStoreEnvironmentPlugin
apply plugin: EventuateDependencyPlugin
apply plugin: 'spring-boot'
@@ -10,8 +11,6 @@ dependencies {
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "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,5 +1,6 @@
apply plugin: 'java'
apply plugin: 'spring-boot'
apply plugin: EventuateDependencyPlugin
dependencies {
compile project(":common-auth-web")

View File

@@ -3,11 +3,13 @@ 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;
@Configuration
@Import({AccountConfiguration.class, MoneyTransferConfiguration.class, EventuateJdbcEventStoreConfiguration.class})
@EnableAutoConfiguration
public class BankingTestConfiguration {
}

View File

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

View File

@@ -16,9 +16,6 @@
<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

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

@@ -2,6 +2,7 @@ apply plugin: VerifyMongoDBConfigurationPlugin
apply plugin: VerifyEventStoreEnvironmentPlugin
apply plugin: 'spring-boot'
apply plugin: EventuateDependencyPlugin
dependencies {
compile project(":customers-command-side-web")
@@ -10,8 +11,6 @@ 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 project(":testutil-customers")
testCompile "org.springframework.boot:spring-boot-starter-test"
}

View File

@@ -1,5 +1,6 @@
apply plugin: VerifyMongoDBConfigurationPlugin
apply plugin: VerifyEventStoreEnvironmentPlugin
apply plugin: EventuateDependencyPlugin
apply plugin: 'spring-boot'
@@ -10,9 +11,7 @@ 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 project(":testutil-customers")
testCompile project(":customers-command-side-service")
testCompile "org.springframework.boot:spring-boot-starter-test"
}

View File

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

@@ -1,39 +1,199 @@
package net.chrisrichardson.eventstore.examples.bank.web;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.AbstractRestAPITest;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.AuthenticatedRestTemplate;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils;
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 org.springframework.web.client.RestTemplate;
public class EndToEndTest extends AbstractRestAPITest {
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 {
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, baseUrl("/customers/"));
CustomersTestUtils customersTestUtils = new CustomersTestUtils(restTemplate, customersQuerySideBaseUrl("/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;
}
@Override
public CustomersTestUtils getCustomersTestUtils() {
return customersTestUtils;
@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());
}
@Override
public AuthenticatedRestTemplate getAuthenticatedRestTemplate() {
return new AuthenticatedRestTemplate(restTemplate);
private BigDecimal toCents(BigDecimal dollarAmount) {
return dollarAmount.multiply(new BigDecimal(100));
}
@Override
public RestTemplate getRestTemplate() {
return restTemplate;
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));
}
},
new Verifier<GetAccountResponse>() {
@Override
public void verify(GetAccountResponse accountInfo) {
Assert.assertEquals(accountId, accountInfo.getAccountId());
Assert.assertEquals(accountId, inCents, accountInfo.getBalance());
}
});
}
}

View File

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

View File

@@ -1,18 +1,41 @@
package net.chrisrichardson.eventstore.javaexamples.banking.web;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.AbstractRestAPITest;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.AuthenticatedRestTemplate;
import net.chrisrichardson.eventstorestore.javaexamples.testutil.CustomersTestUtils;
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 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;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BankingWebTestConfiguration.class)

View File

@@ -55,7 +55,7 @@ public class TestUtil {
}
public static <T> void eventually(final Producer<T> producer, final Verifier<T> verifier) {
final int n = 50;
final int n = 150;
Object possibleException = Observable.timer(0, 200, TimeUnit.MILLISECONDS).flatMap(new Func1<Long, Observable<Outcome<T>>>() {
@Override

View File

@@ -1,4 +1,5 @@
apply plugin: 'spring-boot'
apply plugin: EventuateDependencyPlugin
apply plugin: VerifyEventStoreEnvironmentPlugin
@@ -9,8 +10,6 @@ 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"
}