Merge commit '5c85418cc43318eb2a9c3fa1a4c94c124a17676c'
* commit '5c85418cc43318eb2a9c3fa1a4c94c124a17676c':
- completed moving "toAccounts" from List to Map - created CustomerEvent superclass - removed redundant CustomersNotFoundException - added integration tests for customers
refactoring
- updated swagger description - changed customer registration endpoint - updated tests
- updated swagger description - added /customers/{id}/toaccounts endpoint
updated swagger description
removed customerId from Account aggregate
added customers-query-side part
added customer-command-side
fixed fields names added swagger definition for new endpoints
added customer backend module added auth module added customerId to Account aggregate
This commit is contained in:
@@ -16,7 +16,7 @@ public class Account extends ReflectiveMutableCommandProcessingAggregate<Account
|
||||
private BigDecimal balance;
|
||||
|
||||
public List<Event> process(OpenAccountCommand cmd) {
|
||||
return EventUtil.events(new AccountOpenedEvent(cmd.getInitialBalance()));
|
||||
return EventUtil.events(new AccountOpenedEvent(cmd.getCustomerId(), cmd.getTitle(), cmd.getInitialBalance()));
|
||||
}
|
||||
|
||||
public List<Event> process(DebitAccountCommand cmd) {
|
||||
|
||||
@@ -14,8 +14,8 @@ public class AccountService {
|
||||
this.accountRepository = accountRepository;
|
||||
}
|
||||
|
||||
public rx.Observable<EntityWithIdAndVersion<Account>> openAccount(BigDecimal initialBalance) {
|
||||
return accountRepository.save(new OpenAccountCommand(initialBalance));
|
||||
public rx.Observable<EntityWithIdAndVersion<Account>> openAccount(String customerId, String title, BigDecimal initialBalance) {
|
||||
return accountRepository.save(new OpenAccountCommand(customerId, title, initialBalance));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,13 +5,25 @@ import java.math.BigDecimal;
|
||||
|
||||
public class OpenAccountCommand implements AccountCommand {
|
||||
|
||||
private String customerId;
|
||||
private String title;
|
||||
private BigDecimal initialBalance;
|
||||
|
||||
public OpenAccountCommand(BigDecimal initialBalance) {
|
||||
public OpenAccountCommand(String customerId, String title, BigDecimal initialBalance) {
|
||||
this.customerId = customerId;
|
||||
this.title = title;
|
||||
this.initialBalance = initialBalance;
|
||||
}
|
||||
|
||||
public BigDecimal getInitialBalance() {
|
||||
return initialBalance;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,15 @@ public class AccountTest {
|
||||
@Test
|
||||
public void testSomething() {
|
||||
Account account = new Account();
|
||||
String title = "My Account";
|
||||
String customerId = "00000000-00000000";
|
||||
BigDecimal initialBalance = new BigDecimal(512);
|
||||
List<Event> events = CommandProcessingAggregates.processToList(account, (AccountCommand)new OpenAccountCommand(initialBalance));
|
||||
List<Event> events = CommandProcessingAggregates.processToList(account, (AccountCommand)new OpenAccountCommand(customerId, title, initialBalance));
|
||||
|
||||
Assert.assertEquals(1, events.size());
|
||||
Assert.assertEquals(AccountOpenedEvent.class, events.get(0).getClass());
|
||||
|
||||
account.applyEvent(events.get(0));
|
||||
|
||||
Assert.assertEquals(initialBalance, account.getBalance());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,11 +37,13 @@ public class AccountsCommandSideServiceIntegrationTest {
|
||||
BigDecimal initialFromAccountBalance = new BigDecimal(500);
|
||||
BigDecimal initialToAccountBalance = new BigDecimal(100);
|
||||
BigDecimal amountToTransfer = new BigDecimal(150);
|
||||
String customerId = "00000000-00000000";
|
||||
String title = "My Account";
|
||||
|
||||
final CreateAccountResponse fromAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest(initialFromAccountBalance), CreateAccountResponse.class).getBody();
|
||||
final CreateAccountResponse fromAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest(customerId, title, initialFromAccountBalance), CreateAccountResponse.class).getBody();
|
||||
final String fromAccountId = fromAccount.getAccountId();
|
||||
|
||||
CreateAccountResponse toAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest(initialToAccountBalance), CreateAccountResponse.class).getBody();
|
||||
CreateAccountResponse toAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest(customerId, title, initialToAccountBalance), CreateAccountResponse.class).getBody();
|
||||
String toAccountId = toAccount.getAccountId();
|
||||
|
||||
Assert.assertNotNull(fromAccountId);
|
||||
|
||||
@@ -22,7 +22,7 @@ public class AccountController {
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public Observable<CreateAccountResponse> createAccount(@Validated @RequestBody CreateAccountRequest request) {
|
||||
return accountService.openAccount(request.getInitialBalance())
|
||||
return accountService.openAccount(request.getCustomerId(), request.getTitle(), request.getInitialBalance())
|
||||
.map(entityAndEventInfo -> new CreateAccountResponse(entityAndEventInfo.getEntityIdentifier().getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,11 @@ import java.math.BigDecimal;
|
||||
|
||||
public class CreateAccountRequest {
|
||||
|
||||
@NotNull
|
||||
private String customerId;
|
||||
|
||||
private String title;
|
||||
|
||||
@NotNull
|
||||
@DecimalMin("0")
|
||||
private BigDecimal initialBalance;
|
||||
@@ -14,11 +19,20 @@ public class CreateAccountRequest {
|
||||
public CreateAccountRequest() {
|
||||
}
|
||||
|
||||
public CreateAccountRequest(BigDecimal initialBalance) {
|
||||
|
||||
public CreateAccountRequest(String customerId, String title, BigDecimal initialBalance) {
|
||||
this.customerId = customerId;
|
||||
this.title = title;
|
||||
this.initialBalance = initialBalance;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public void setCustomerId(String customerId) {
|
||||
this.customerId = customerId;
|
||||
}
|
||||
|
||||
public BigDecimal getInitialBalance() {
|
||||
return initialBalance;
|
||||
}
|
||||
@@ -26,4 +40,12 @@ public class CreateAccountRequest {
|
||||
public void setInitialBalance(BigDecimal initialBalance) {
|
||||
this.initialBalance = initialBalance;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class AccountControllerIntegrationTest {
|
||||
public void shouldCreateAccount() throws Exception {
|
||||
mockMvc.perform(post("/accounts")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"initialBalance\" : 500}")
|
||||
.content("{\"customerId\" : \"00000000-00000000\", \"initialBalance\" : 500}")
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import java.util.List;
|
||||
public class AccountInfo {
|
||||
|
||||
private String id;
|
||||
private String customerId;
|
||||
private String title;
|
||||
private long balance;
|
||||
private List<AccountChangeInfo> changes;
|
||||
private List<AccountTransactionInfo> transactions;
|
||||
@@ -16,9 +18,11 @@ public class AccountInfo {
|
||||
private AccountInfo() {
|
||||
}
|
||||
|
||||
public AccountInfo(String id, long balance, List<AccountChangeInfo> changes, List<AccountTransactionInfo> transactions, String version) {
|
||||
public AccountInfo(String id, String customerId, String title, long balance, List<AccountChangeInfo> changes, List<AccountTransactionInfo> transactions, String version) {
|
||||
|
||||
this.id = id;
|
||||
this.customerId = customerId;
|
||||
this.title = title;
|
||||
this.balance = balance;
|
||||
this.changes = changes;
|
||||
this.transactions = transactions;
|
||||
@@ -29,6 +33,14 @@ public class AccountInfo {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public long getBalance() {
|
||||
return balance;
|
||||
}
|
||||
|
||||
@@ -26,10 +26,12 @@ public class AccountInfoUpdateService {
|
||||
|
||||
|
||||
|
||||
public void create(String accountId, BigDecimal initialBalance, String version) {
|
||||
public void create(String accountId, String customerId, String title, BigDecimal initialBalance, String version) {
|
||||
try {
|
||||
accountInfoRepository.save(new AccountInfo(
|
||||
accountId,
|
||||
customerId,
|
||||
title,
|
||||
toIntegerRepr(initialBalance),
|
||||
Collections.<AccountChangeInfo>emptyList(),
|
||||
Collections.<AccountTransactionInfo>emptyList(),
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.math.BigDecimal;
|
||||
|
||||
import static net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.MoneyUtil.toIntegerRepr;
|
||||
|
||||
@EventSubscriber(id="querySideEventHandlers")
|
||||
@EventSubscriber(id="accountQuerySideEventHandlers")
|
||||
public class AccountQueryWorkflow implements CompoundEventHandler {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
@@ -37,7 +37,9 @@ public class AccountQueryWorkflow implements CompoundEventHandler {
|
||||
String eventId = de.eventId().asString();
|
||||
logger.info("**************** account version=" + id + ", " + eventId);
|
||||
BigDecimal initialBalance = event.getInitialBalance();
|
||||
accountInfoUpdateService.create(id, initialBalance, eventId);
|
||||
String customerId = event.getCustomerId();
|
||||
String title = event.getTitle();
|
||||
accountInfoUpdateService.create(id, customerId, title, initialBalance, eventId);
|
||||
return Observable.just(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ dependencies {
|
||||
testCompile project(":accounts-command-side-backend")
|
||||
testCompile project(":transactions-command-side-backend")
|
||||
testCompile project(":accounts-query-side-backend")
|
||||
testCompile project(":customers-command-side-backend")
|
||||
testCompile project(":customers-query-side-backend")
|
||||
testCompile project(":testutil")
|
||||
testCompile "junit:junit:4.11"
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
|
||||
@@ -43,9 +43,9 @@ public class MoneyTransferIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void shouldTransferMoney() {
|
||||
final EntityWithIdAndVersion<Account> fromAccount= await(accountService.openAccount(new BigDecimal(150)));
|
||||
final EntityWithIdAndVersion<Account> fromAccount= await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(150)));
|
||||
|
||||
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount(new BigDecimal(300)));
|
||||
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(300)));
|
||||
|
||||
final EntityWithIdAndVersion<MoneyTransfer> transaction = await(
|
||||
moneyTransferService.transferMoney(new TransferDetails(fromAccount.getEntityIdentifier(),
|
||||
@@ -98,9 +98,9 @@ public class MoneyTransferIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void shouldFailDueToInsufficientFunds() {
|
||||
final EntityWithIdAndVersion<Account> fromAccount= await(accountService.openAccount(new BigDecimal(150)));
|
||||
final EntityWithIdAndVersion<Account> fromAccount= await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(150)));
|
||||
|
||||
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount(new BigDecimal(300)));
|
||||
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(300)));
|
||||
|
||||
final EntityWithIdAndVersion<MoneyTransfer> transaction = await(
|
||||
moneyTransferService.transferMoney(new TransferDetails(fromAccount.getEntityIdentifier(),
|
||||
|
||||
@@ -45,9 +45,9 @@ public class AccountQuerySideIntegrationTest {
|
||||
@Test
|
||||
public void shouldUpdateQuerySide() throws Exception {
|
||||
|
||||
final EntityWithIdAndVersion<Account> fromAccount = await(accountService.openAccount(new BigDecimal(150)));
|
||||
final EntityWithIdAndVersion<Account> fromAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(150)));
|
||||
|
||||
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount(new BigDecimal(300)));
|
||||
final EntityWithIdAndVersion<Account> toAccount = await(accountService.openAccount("00000000-00000000", "My Account", new BigDecimal(300)));
|
||||
|
||||
final EntityWithIdAndVersion<MoneyTransfer> transaction = await(
|
||||
moneyTransferService.transferMoney(new TransferDetails(fromAccount.getEntityIdentifier(),
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.EntityWithIdAndVersion;
|
||||
import net.chrisrichardson.eventstore.EventStore;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.Customer;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.CustomerService;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.accounts.AccountInfo;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Address;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Name;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
|
||||
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
|
||||
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import rx.Observable;
|
||||
|
||||
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.await;
|
||||
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
|
||||
|
||||
/**
|
||||
* Created by Main on 10.02.2016.
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = CustomerQuerySideTestConfiguration.class)
|
||||
@IntegrationTest
|
||||
public class CustomerQuerySideIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private CustomerService customerService;
|
||||
|
||||
@Autowired
|
||||
private CustomerQueryService customerQueryService;
|
||||
|
||||
@Autowired
|
||||
private EventStore eventStore;
|
||||
|
||||
@Test
|
||||
public void shouldCreateCustomerAndAddToAccount() throws Exception {
|
||||
CustomerInfo customerInfo = generateCustomerInfo();
|
||||
EntityWithIdAndVersion<Customer> customer = await(customerService.createCustomer(customerInfo));
|
||||
|
||||
ToAccountInfo toAccountInfo = generateToAccountInfo();
|
||||
EntityWithIdAndVersion<Customer> customerWithNewAccount = await(customerService.addToAccount(customer.getEntityIdentifier().getId(), toAccountInfo));
|
||||
|
||||
eventually(
|
||||
new Producer<QuerySideCustomer>() {
|
||||
@Override
|
||||
public Observable<QuerySideCustomer> produce() {
|
||||
return customerQueryService.findByCustomerId(customer.getEntityIdentifier());
|
||||
}
|
||||
},
|
||||
new Verifier<QuerySideCustomer>() {
|
||||
@Override
|
||||
public void verify(QuerySideCustomer querySideCustomer) {
|
||||
Assert.assertEquals(customerInfo.getName(), querySideCustomer.getName());
|
||||
Assert.assertEquals(customerInfo.getSsn(), querySideCustomer.getSsn());
|
||||
Assert.assertEquals(customerInfo.getEmail(), querySideCustomer.getEmail());
|
||||
Assert.assertEquals(customerInfo.getPhoneNumber(), querySideCustomer.getPhoneNumber());
|
||||
Assert.assertEquals(customerInfo.getAddress(), querySideCustomer.getAddress());
|
||||
|
||||
Assert.assertNotNull(querySideCustomer.getToAccounts());
|
||||
Assert.assertFalse(querySideCustomer.getToAccounts().isEmpty());
|
||||
Assert.assertEquals(querySideCustomer.getToAccounts().get("11111111-11111111"), toAccountInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private CustomerInfo generateCustomerInfo() {
|
||||
return new CustomerInfo(
|
||||
new Name("John", "Doe"),
|
||||
"current@email.com",
|
||||
"000-00-0000",
|
||||
"1-111-111-1111",
|
||||
new Address("street 1",
|
||||
"street 2",
|
||||
"City",
|
||||
"State",
|
||||
"1111111")
|
||||
);
|
||||
}
|
||||
|
||||
private ToAccountInfo generateToAccountInfo() {
|
||||
return new ToAccountInfo("11111111-11111111", "New Account", "John Doe");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.CustomerConfiguration;
|
||||
import net.chrisrichardson.eventstore.jdbc.config.JdbcEventStoreConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@Configuration
|
||||
@Import({CustomerConfiguration.class, JdbcEventStoreConfiguration.class, QuerySideCustomerConfiguration.class})
|
||||
public class CustomerQuerySideTestConfiguration {
|
||||
}
|
||||
13
java-spring/common-auth-controller/build.gradle
Normal file
13
java-spring/common-auth-controller/build.gradle
Normal file
@@ -0,0 +1,13 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
dependencies {
|
||||
compile project(":common-auth")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
|
||||
|
||||
compile "org.springframework.security:spring-security-config:4.0.2.RELEASE"
|
||||
compile "org.springframework.security:spring-security-web:4.0.2.RELEASE"
|
||||
|
||||
|
||||
testCompile "junit:junit:4.11"
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.AuthRequest;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.AuthResponse;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.token.Token;
|
||||
import org.springframework.security.core.token.TokenService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 21.09.15.
|
||||
*/
|
||||
@RestController
|
||||
@Validated
|
||||
public class AuthController {
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@RequestMapping(value = "/login", method = POST)
|
||||
public ResponseEntity<AuthResponse> doAuth(@RequestBody @Valid AuthRequest request) throws IOException {
|
||||
User user = new User();
|
||||
user.setEmail(request.getEmail());
|
||||
|
||||
Token token = tokenService.allocateToken(objectMapper.writeValueAsString(user));
|
||||
return ResponseEntity.status(HttpStatus.OK)
|
||||
.body(new AuthResponse(token.getKey()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model;
|
||||
|
||||
import org.hibernate.validator.constraints.Email;
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 19.10.15.
|
||||
*/
|
||||
public class AuthRequest {
|
||||
|
||||
@NotBlank
|
||||
@Email
|
||||
private String email;
|
||||
|
||||
public AuthRequest() {
|
||||
}
|
||||
|
||||
public AuthRequest(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 21.09.15.
|
||||
*/
|
||||
public class AuthResponse {
|
||||
private String token;
|
||||
|
||||
public AuthResponse() {
|
||||
}
|
||||
|
||||
public AuthResponse(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
}
|
||||
11
java-spring/common-auth/build.gradle
Normal file
11
java-spring/common-auth/build.gradle
Normal file
@@ -0,0 +1,11 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
dependencies {
|
||||
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
|
||||
|
||||
compile "org.springframework.security:spring-security-config:4.0.2.RELEASE"
|
||||
compile "org.springframework.security:spring-security-web:4.0.2.RELEASE"
|
||||
|
||||
|
||||
testCompile "junit:junit:4.11"
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.filter.StatelessAuthenticationFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.token.KeyBasedPersistenceTokenService;
|
||||
import org.springframework.security.core.token.TokenService;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 21.09.15.
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableWebSecurity
|
||||
@EnableConfigurationProperties({AuthProperties.class})
|
||||
public class AuthConfiguration extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Autowired
|
||||
private AuthProperties securityProperties;
|
||||
|
||||
@Autowired
|
||||
private TokenAuthenticationService tokenAuthenticationService;
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public AuthenticationManager authenticationManagerBean() throws Exception {
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.inMemoryAuthentication();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf().disable()
|
||||
.formLogin().loginPage("/index.html").and()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/health").permitAll()
|
||||
.antMatchers("/swagger-ui.html").permitAll()
|
||||
.antMatchers("/v2/api-docs").permitAll()
|
||||
.antMatchers("/js/**").permitAll()
|
||||
.antMatchers("/styles/**").permitAll()
|
||||
.antMatchers("/views/**").permitAll()
|
||||
.antMatchers(HttpMethod.POST, "/register/step_1").permitAll()
|
||||
.antMatchers(HttpMethod.POST, "/register/step_2").permitAll()
|
||||
.antMatchers(HttpMethod.POST, "/login").permitAll()
|
||||
.anyRequest().authenticated().and()
|
||||
.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService), UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TokenService tokenService() {
|
||||
KeyBasedPersistenceTokenService res = new KeyBasedPersistenceTokenService();
|
||||
res.setSecureRandom(new SecureRandom());
|
||||
res.setServerSecret(securityProperties.getServerSecret());
|
||||
res.setServerInteger(securityProperties.getServerInteger());
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 21.09.15.
|
||||
*/
|
||||
@ConfigurationProperties(locations = "classpath:auth.properties", ignoreUnknownFields = false, prefix = "auth")
|
||||
public class AuthProperties {
|
||||
private String serverSecret;
|
||||
private Integer serverInteger;
|
||||
|
||||
public String getServerSecret() {
|
||||
return serverSecret;
|
||||
}
|
||||
|
||||
public void setServerSecret(String serverSecret) {
|
||||
this.serverSecret = serverSecret;
|
||||
}
|
||||
|
||||
public Integer getServerInteger() {
|
||||
return serverInteger;
|
||||
}
|
||||
|
||||
public void setServerInteger(Integer serverInteger) {
|
||||
this.serverInteger = serverInteger;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.User;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model.UserAuthentication;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.token.Token;
|
||||
import org.springframework.security.core.token.TokenService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 23.09.15.
|
||||
*/
|
||||
@Service
|
||||
public class TokenAuthenticationService {
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
private static final String AUTH_HEADER_NAME = "x-access-token";
|
||||
private static final long DAY = 1000 * 60 * 60 * 24;
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
public Authentication getAuthentication(HttpServletRequest request) throws IOException {
|
||||
final String tokenString = request.getHeader(AUTH_HEADER_NAME);
|
||||
|
||||
if (tokenString != null) {
|
||||
Token token = tokenService.verifyToken(tokenString);
|
||||
final User user = mapper.readValue(token.getExtendedInformation(), User.class);
|
||||
|
||||
if (user != null && (System.currentTimeMillis() - token.getKeyCreationTime()) < DAY) {
|
||||
return new UserAuthentication(user);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.filter;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.commonauth.TokenAuthenticationService;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 23.09.15.
|
||||
*/
|
||||
public class StatelessAuthenticationFilter extends GenericFilterBean {
|
||||
|
||||
private final TokenAuthenticationService tokenAuthenticationService;
|
||||
|
||||
public StatelessAuthenticationFilter(TokenAuthenticationService taService) {
|
||||
this.tokenAuthenticationService = taService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
|
||||
ServletException {
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
tokenAuthenticationService.getAuthentication((HttpServletRequest) req));
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 23.09.15.
|
||||
*/
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class User implements UserDetails {
|
||||
|
||||
private String email;
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.email = username;
|
||||
}
|
||||
|
||||
@Override
|
||||
@JsonIgnore
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("USER");
|
||||
Set<GrantedAuthority> res = new HashSet<GrantedAuthority>();
|
||||
res.add(authority);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return this.email;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.commonauth.model;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 23.09.15.
|
||||
*/
|
||||
public class UserAuthentication implements Authentication {
|
||||
|
||||
private final User user;
|
||||
private boolean authenticated = true;
|
||||
|
||||
public UserAuthentication(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return user.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return user.getAuthorities();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return user.getPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getDetails() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return user.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthenticated() {
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuthenticated(boolean authenticated) {
|
||||
this.authenticated = authenticated;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
dependencies {
|
||||
compile project(":common-customers")
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-java-client_2.10:$eventStoreClientVersion"
|
||||
|
||||
testCompile "junit:junit:4.11"
|
||||
|
||||
@@ -7,15 +7,27 @@ import java.math.BigDecimal;
|
||||
|
||||
public class AccountOpenedEvent implements Event {
|
||||
|
||||
private String customerId;
|
||||
private String title;
|
||||
private BigDecimal initialBalance;
|
||||
|
||||
private AccountOpenedEvent() {
|
||||
}
|
||||
|
||||
public AccountOpenedEvent(BigDecimal initialBalance) {
|
||||
public AccountOpenedEvent(String customerId, String title, BigDecimal initialBalance) {
|
||||
this.customerId = customerId;
|
||||
this.title = title;
|
||||
this.initialBalance = initialBalance;
|
||||
}
|
||||
|
||||
public String getCustomerId() {
|
||||
return customerId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public BigDecimal getInitialBalance() {
|
||||
return initialBalance;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
|
||||
|
||||
/**
|
||||
* Created by Main on 08.02.2016.
|
||||
*/
|
||||
public class CustomerAddedToAccount extends CustomerEvent {
|
||||
|
||||
private ToAccountInfo toAccountInfo;
|
||||
|
||||
public CustomerAddedToAccount() {
|
||||
}
|
||||
|
||||
public CustomerAddedToAccount(ToAccountInfo toAccountInfo) {
|
||||
this.toAccountInfo = toAccountInfo;
|
||||
}
|
||||
|
||||
public ToAccountInfo getToAccountInfo() {
|
||||
return toAccountInfo;
|
||||
}
|
||||
|
||||
public void setToAccountInfo(ToAccountInfo toAccountInfo) {
|
||||
this.toAccountInfo = toAccountInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 02.02.16.
|
||||
*/
|
||||
public class CustomerCreatedEvent extends CustomerEvent {
|
||||
|
||||
private CustomerInfo customerInfo;
|
||||
|
||||
public CustomerCreatedEvent() {
|
||||
}
|
||||
|
||||
public CustomerCreatedEvent(CustomerInfo customerInfo) {
|
||||
this.customerInfo = customerInfo;
|
||||
}
|
||||
|
||||
public CustomerInfo getCustomerInfo() {
|
||||
return customerInfo;
|
||||
}
|
||||
|
||||
public void setCustomerInfo(CustomerInfo customerInfo) {
|
||||
this.customerInfo = customerInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.Event;
|
||||
import net.chrisrichardson.eventstore.EventEntity;
|
||||
|
||||
/**
|
||||
* Created by Main on 11.02.2016.
|
||||
*/
|
||||
@EventEntity(entity="net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.Customer")
|
||||
public abstract class CustomerEvent implements Event {
|
||||
}
|
||||
@@ -13,7 +13,7 @@ public class AccountOpenEventSerializationTest {
|
||||
|
||||
@Test
|
||||
public void shouldSerde() {
|
||||
AccountOpenedEvent event = new AccountOpenedEvent(new BigDecimal(55));
|
||||
AccountOpenedEvent event = new AccountOpenedEvent("00000000-00000000", "My Account", new BigDecimal(55));
|
||||
String json = JSonMapper.toJson(event, EventStoreCommonObjectMapping.getObjectMapper());
|
||||
System.out.println("json=" + json);
|
||||
|
||||
|
||||
8
java-spring/common-customers/build.gradle
Normal file
8
java-spring/common-customers/build.gradle
Normal file
@@ -0,0 +1,8 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
dependencies {
|
||||
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-java-client_2.10:$eventStoreClientVersion"
|
||||
|
||||
testCompile group: 'junit', name: 'junit', version: '4.11'
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.common.customers;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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;
|
||||
|
||||
public ToAccountInfo() {
|
||||
}
|
||||
|
||||
public ToAccountInfo(String id, String title, String owner) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return EqualsBuilder.reflectionEquals(this, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return HashCodeBuilder.reflectionHashCode(this);
|
||||
}
|
||||
}
|
||||
12
java-spring/customers-command-side-backend/build.gradle
Normal file
12
java-spring/customers-command-side-backend/build.gradle
Normal file
@@ -0,0 +1,12 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
dependencies {
|
||||
compile project(":common-customers")
|
||||
compile project(":common-backend")
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-java-client_2.10:$eventStoreClientVersion"
|
||||
|
||||
testCompile project(":testutil")
|
||||
testCompile "junit:junit:4.11"
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
testCompile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
|
||||
|
||||
/**
|
||||
* Created by Main on 08.02.2016.
|
||||
*/
|
||||
public class AddToAccountCommand implements CustomerCommand {
|
||||
|
||||
private ToAccountInfo toAccountInfo;
|
||||
|
||||
public AddToAccountCommand(ToAccountInfo toAccountInfo) {
|
||||
this.toAccountInfo = toAccountInfo;
|
||||
}
|
||||
|
||||
public ToAccountInfo getToAccountInfo() {
|
||||
return toAccountInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 02.02.16.
|
||||
*/
|
||||
public class CreateCustomerCommand implements CustomerCommand {
|
||||
private CustomerInfo customerInfo;
|
||||
|
||||
public CreateCustomerCommand(CustomerInfo customerInfo) {
|
||||
this.customerInfo = customerInfo;
|
||||
}
|
||||
|
||||
public CustomerInfo getCustomerInfo() {
|
||||
return customerInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.Event;
|
||||
import net.chrisrichardson.eventstore.EventUtil;
|
||||
import net.chrisrichardson.eventstore.ReflectiveMutableCommandProcessingAggregate;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers.CustomerAddedToAccount;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers.CustomerCreatedEvent;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 02.02.16.
|
||||
*/
|
||||
public class Customer extends ReflectiveMutableCommandProcessingAggregate<Customer, CustomerCommand> {
|
||||
|
||||
private CustomerInfo customerInfo;
|
||||
|
||||
public List<Event> process(CreateCustomerCommand cmd) {
|
||||
return EventUtil.events(new CustomerCreatedEvent(cmd.getCustomerInfo()));
|
||||
}
|
||||
|
||||
public List<Event> process(AddToAccountCommand cmd) {
|
||||
return EventUtil.events(new CustomerAddedToAccount(cmd.getToAccountInfo()));
|
||||
}
|
||||
|
||||
public void apply(CustomerCreatedEvent event) {
|
||||
customerInfo = event.getCustomerInfo();
|
||||
}
|
||||
|
||||
public void apply(CustomerAddedToAccount event) {
|
||||
}
|
||||
|
||||
public CustomerInfo getCustomerInfo() {
|
||||
return customerInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.Command;
|
||||
|
||||
interface CustomerCommand extends Command {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.EventStore;
|
||||
import net.chrisrichardson.eventstore.javaapi.consumer.EnableJavaEventHandlers;
|
||||
import net.chrisrichardson.eventstore.repository.AggregateRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@EnableJavaEventHandlers
|
||||
public class CustomerConfiguration {
|
||||
|
||||
@Bean
|
||||
public CustomerService customerService(AggregateRepository<Customer, CustomerCommand> customerRepository) {
|
||||
return new CustomerService(customerRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AggregateRepository<Customer, CustomerCommand> customerRepository(EventStore eventStore) {
|
||||
return new AggregateRepository<Customer, CustomerCommand>(Customer.class, eventStore);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers;
|
||||
|
||||
|
||||
import net.chrisrichardson.eventstore.EntityIdentifier;
|
||||
import net.chrisrichardson.eventstore.EntityWithIdAndVersion;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
|
||||
import net.chrisrichardson.eventstore.repository.AggregateRepository;
|
||||
|
||||
public class CustomerService {
|
||||
|
||||
private final AggregateRepository<Customer, CustomerCommand> accountRepository;
|
||||
|
||||
public CustomerService(AggregateRepository<Customer, CustomerCommand> accountRepository) {
|
||||
this.accountRepository = accountRepository;
|
||||
}
|
||||
|
||||
public rx.Observable<EntityWithIdAndVersion<Customer>> createCustomer(CustomerInfo customerInfo) {
|
||||
return accountRepository.save(new CreateCustomerCommand(customerInfo));
|
||||
}
|
||||
|
||||
public rx.Observable<EntityWithIdAndVersion<Customer>> addToAccount(String customerId, ToAccountInfo toAccountInfo) {
|
||||
return accountRepository.update(new EntityIdentifier(customerId), new AddToAccountCommand(toAccountInfo));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers;
|
||||
|
||||
|
||||
import net.chrisrichardson.eventstorestore.javaexamples.testutil.AbstractEntityEventTest;
|
||||
|
||||
public class CustomerEventTest extends AbstractEntityEventTest {
|
||||
|
||||
@Override
|
||||
protected Class<Customer> entityClass() {
|
||||
return Customer.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.CommandProcessingAggregates;
|
||||
import net.chrisrichardson.eventstore.Event;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers.CustomerCreatedEvent;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Address;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Name;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CustomerTest {
|
||||
|
||||
@Test
|
||||
public void testSomething() {
|
||||
Customer customer = new Customer();
|
||||
|
||||
CustomerInfo customerInfo = generateCustomerInfo();
|
||||
|
||||
List<Event> events = CommandProcessingAggregates.processToList(customer, new CreateCustomerCommand(customerInfo));
|
||||
|
||||
Assert.assertEquals(1, events.size());
|
||||
Assert.assertEquals(CustomerCreatedEvent.class, events.get(0).getClass());
|
||||
|
||||
customer.applyEvent(events.get(0));
|
||||
Assert.assertEquals(customerInfo, customer.getCustomerInfo());
|
||||
}
|
||||
|
||||
private CustomerInfo generateCustomerInfo() {
|
||||
return new CustomerInfo(
|
||||
new Name("John", "Doe"),
|
||||
"current@email.com",
|
||||
"000-00-0000",
|
||||
"1-111-111-1111",
|
||||
new Address("street 1",
|
||||
"street 2",
|
||||
"City",
|
||||
"State",
|
||||
"1111111")
|
||||
);
|
||||
}
|
||||
}
|
||||
22
java-spring/customers-command-side-service/build.gradle
Normal file
22
java-spring/customers-command-side-service/build.gradle
Normal file
@@ -0,0 +1,22 @@
|
||||
apply plugin: VerifyMongoDBConfigurationPlugin
|
||||
apply plugin: VerifyEventStoreEnvironmentPlugin
|
||||
|
||||
apply plugin: 'spring-boot'
|
||||
|
||||
dependencies {
|
||||
compile project(":customers-command-side-web")
|
||||
compile project(":common-swagger")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator"
|
||||
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-http-stomp-client_2.10:$eventStoreClientVersion"
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
}
|
||||
|
||||
run {
|
||||
environment 'EVENTUATE_API_KEY_ID', '20CAXGPA3DE56WXO2SFBUDGZ9'
|
||||
environment 'EVENTUATE_API_KEY_SECRET', 'gU6n78drWCIgkgzVStvI3BhV3MfzDyjWKCN7p0PBimI'
|
||||
environment 'SPRING_DATA_MONGODB_URI', 'mongodb://198.50.218.51/mydb'
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web;
|
||||
|
||||
import net.chrisrichardson.eventstore.client.config.EventStoreHttpClientConfiguration;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.customers.CustomersCommandSideWebConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
|
||||
import org.springframework.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;
|
||||
|
||||
@Configuration
|
||||
@Import({CustomersCommandSideWebConfiguration.class, EventStoreHttpClientConfiguration.class, CommonSwaggerConfiguration.class})
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan
|
||||
public class CustomersCommandSideServiceConfiguration {
|
||||
|
||||
|
||||
@Bean
|
||||
public HttpMessageConverters customConverters() {
|
||||
HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter();
|
||||
return new HttpMessageConverters(additional);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web.main;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.web.CustomersCommandSideServiceConfiguration;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
|
||||
public class CustomersCommandSideServiceMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CustomersCommandSideServiceConfiguration.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Address;
|
||||
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.Name;
|
||||
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.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = CustomersCommandSideServiceTestConfiguration.class)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest({"server.port=0", "management.port=0"})
|
||||
public class CustomersCommandSideServiceIntegrationTest {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port;
|
||||
|
||||
private String baseUrl(String path) {
|
||||
return "http://localhost:" + port + "/" + path;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldCreateCustomer() {
|
||||
CustomerInfo customerInfo = generateCustomerInfo();
|
||||
|
||||
final CustomerResponse customerResponse = restTemplate.postForEntity(baseUrl("/customers"), customerInfo, CustomerResponse.class).getBody();
|
||||
final String customerId = customerResponse.getId();
|
||||
|
||||
Assert.assertNotNull(customerId);
|
||||
Assert.assertEquals(customerInfo, customerResponse.getCustomerInfo());
|
||||
}
|
||||
|
||||
private CustomerInfo generateCustomerInfo() {
|
||||
return new CustomerInfo(
|
||||
new Name("John", "Doe"),
|
||||
"current@email.com",
|
||||
"000-00-0000",
|
||||
"1-111-111-1111",
|
||||
new Address("street 1",
|
||||
"street 2",
|
||||
"City",
|
||||
"State",
|
||||
"1111111")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
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 java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@Import(CustomersCommandSideServiceConfiguration.class)
|
||||
public class CustomersCommandSideServiceTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(HttpMessageConverters converters) {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
HttpMessageConverter<?> httpMessageConverter = converters.getConverters().get(0);
|
||||
List<? extends HttpMessageConverter<?>> httpMessageConverters = Arrays.asList(new MappingJackson2HttpMessageConverter());
|
||||
restTemplate.setMessageConverters((List<HttpMessageConverter<?>>) httpMessageConverters);
|
||||
return restTemplate;
|
||||
}
|
||||
}
|
||||
12
java-spring/customers-command-side-web/build.gradle
Normal file
12
java-spring/customers-command-side-web/build.gradle
Normal file
@@ -0,0 +1,12 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
dependencies {
|
||||
compile project(":common-customers")
|
||||
compile project(":customers-command-side-backend")
|
||||
compile project(":common-web")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
|
||||
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
testCompile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.CustomerService;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerResponse;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 03.02.16.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/customers")
|
||||
public class CustomerController {
|
||||
|
||||
private CustomerService customerService;
|
||||
|
||||
@Autowired
|
||||
public CustomerController(CustomerService customerService) {
|
||||
this.customerService = customerService;
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public Observable<CustomerResponse> createCustomer(@Validated @RequestBody CustomerInfo request) {
|
||||
return customerService.createCustomer(request)
|
||||
.map(entityAndEventInfo -> new CustomerResponse(entityAndEventInfo.getEntityIdentifier().getId(), request));
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/{id}/toaccounts", method = RequestMethod.POST)
|
||||
public Observable<ResponseEntity<?>> addToAccount(@PathVariable String id, @Validated @RequestBody ToAccountInfo request) {
|
||||
return customerService.addToAccount(id, request)
|
||||
.map(entityAndEventInfo -> ResponseEntity.ok().build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web.commandside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.commandside.customers.CustomerConfiguration;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.web.util.ObservableReturnValueHandler;
|
||||
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.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by popikyardo on 03.02.16.
|
||||
*/
|
||||
@Configuration
|
||||
@Import({CustomerConfiguration.class})
|
||||
@ComponentScan
|
||||
public class CustomersCommandSideWebConfiguration extends WebMvcConfigurerAdapter {
|
||||
|
||||
class FakeThing {}
|
||||
|
||||
@Bean
|
||||
public FakeThing init(RequestMappingHandlerAdapter adapter) {
|
||||
// https://jira.spring.io/browse/SPR-13083
|
||||
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>(adapter.getReturnValueHandlers());
|
||||
handlers.add(0, new ObservableReturnValueHandler());
|
||||
adapter.setReturnValueHandlers(handlers);
|
||||
return new FakeThing();
|
||||
}
|
||||
|
||||
}
|
||||
18
java-spring/customers-query-side-backend/build.gradle
Normal file
18
java-spring/customers-query-side-backend/build.gradle
Normal file
@@ -0,0 +1,18 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
dependencies {
|
||||
compile project(":common-backend")
|
||||
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-java-client_2.10:$eventStoreClientVersion"
|
||||
compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion"
|
||||
|
||||
compile 'com.fasterxml.jackson.core:jackson-core:2.4.3'
|
||||
compile 'com.fasterxml.jackson.core:jackson-databind:2.4.3'
|
||||
compile 'com.fasterxml.jackson.module:jackson-module-scala_2.10:2.4.3'
|
||||
|
||||
testCompile project(":testutil")
|
||||
testCompile "junit:junit:4.11"
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
testCompile "net.chrisrichardson.eventstore.client:eventstore-jdbc_2.10:$eventStoreClientVersion"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Created by Main on 04.02.2016.
|
||||
*/
|
||||
public class CustomerInfoUpdateService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private QuerySideCustomerRepository accountInfoRepository;
|
||||
|
||||
public CustomerInfoUpdateService(QuerySideCustomerRepository accountInfoRepository) {
|
||||
this.accountInfoRepository = accountInfoRepository;
|
||||
}
|
||||
|
||||
public void create(String id, CustomerInfo customerInfo) {
|
||||
try {
|
||||
accountInfoRepository.save(new QuerySideCustomer(id,
|
||||
customerInfo.getName(),
|
||||
customerInfo.getEmail(),
|
||||
customerInfo.getSsn(),
|
||||
customerInfo.getPhoneNumber(),
|
||||
customerInfo.getAddress(),
|
||||
Collections.<String, ToAccountInfo>emptyMap()
|
||||
)
|
||||
);
|
||||
logger.info("Saved in mongo");
|
||||
} catch (Throwable t) {
|
||||
logger.error("Error during saving: ", t);
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToAccount(String id, ToAccountInfo accountInfo) {
|
||||
QuerySideCustomer customer = accountInfoRepository.findOne(id);
|
||||
customer.getToAccounts().put(accountInfo.getId(), accountInfo);
|
||||
accountInfoRepository.save(customer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.EntityIdentifier;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import rx.Observable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CustomerQueryService {
|
||||
|
||||
private QuerySideCustomerRepository querySideCustomerRepository;
|
||||
|
||||
public CustomerQueryService(QuerySideCustomerRepository querySideCustomerRepository) {
|
||||
this.querySideCustomerRepository = querySideCustomerRepository;
|
||||
}
|
||||
|
||||
public Observable<QuerySideCustomer> findByCustomerId(EntityIdentifier customerId) {
|
||||
QuerySideCustomer customer = querySideCustomerRepository.findOne(customerId.getId());
|
||||
if (customer == null)
|
||||
return Observable.error(new EmptyResultDataAccessException(1));
|
||||
else
|
||||
return Observable.just(customer);
|
||||
}
|
||||
|
||||
public Observable<List<QuerySideCustomer>> findByEmail(String email){
|
||||
List<QuerySideCustomer> customers = querySideCustomerRepository.findByEmailLike(email);
|
||||
if (customers.isEmpty())
|
||||
return Observable.error(new EmptyResultDataAccessException(1));
|
||||
else
|
||||
return Observable.just(customers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers.CustomerAddedToAccount;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.common.customers.CustomerCreatedEvent;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
|
||||
import net.chrisrichardson.eventstore.subscriptions.CompoundEventHandler;
|
||||
import net.chrisrichardson.eventstore.subscriptions.DispatchedEvent;
|
||||
import net.chrisrichardson.eventstore.subscriptions.EventHandlerMethod;
|
||||
import net.chrisrichardson.eventstore.subscriptions.EventSubscriber;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by Main on 04.02.2016.
|
||||
*/
|
||||
@EventSubscriber(id = "customerQuerySideEventHandlers")
|
||||
public class CustomerQueryWorkflow implements CompoundEventHandler {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private CustomerInfoUpdateService customerInfoUpdateService;
|
||||
|
||||
|
||||
public CustomerQueryWorkflow(CustomerInfoUpdateService customerInfoUpdateService) {
|
||||
this.customerInfoUpdateService = customerInfoUpdateService;
|
||||
}
|
||||
|
||||
@EventHandlerMethod
|
||||
public Observable<Object> create(DispatchedEvent<CustomerCreatedEvent> de) {
|
||||
CustomerCreatedEvent event = de.event();
|
||||
String id = de.getEntityIdentifier().getId();
|
||||
|
||||
customerInfoUpdateService.create(id, event.getCustomerInfo());
|
||||
return Observable.just(null);
|
||||
}
|
||||
|
||||
@EventHandlerMethod
|
||||
public Observable<Object> addToAccount(DispatchedEvent<CustomerAddedToAccount> de) {
|
||||
CustomerAddedToAccount event = de.event();
|
||||
String id = de.getEntityIdentifier().getId();
|
||||
|
||||
ToAccountInfo toAccountInfo = event.getToAccountInfo();
|
||||
|
||||
customerInfoUpdateService.addToAccount(id, toAccountInfo);
|
||||
return Observable.just(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Address;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerInfo;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Name;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.ToAccountInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by Main on 05.02.2016.
|
||||
*/
|
||||
public class QuerySideCustomer extends CustomerInfo {
|
||||
private String id;
|
||||
private Map<String, ToAccountInfo> toAccounts;
|
||||
|
||||
public QuerySideCustomer(String id, Name name, String email, String ssn, String phoneNumber, Address address, Map<String, ToAccountInfo> toAccounts) {
|
||||
super(name, email, ssn, phoneNumber, address);
|
||||
this.id = id;
|
||||
this.toAccounts = toAccounts;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Map<String, ToAccountInfo> getToAccounts() {
|
||||
return toAccounts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaapi.consumer.EnableJavaEventHandlers;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
|
||||
|
||||
/**
|
||||
* Created by Main on 04.02.2016.
|
||||
*/
|
||||
@Configuration
|
||||
@EnableMongoRepositories
|
||||
@EnableJavaEventHandlers
|
||||
public class QuerySideCustomerConfiguration {
|
||||
@Bean
|
||||
public CustomerQueryWorkflow customerQueryWorkflow(CustomerInfoUpdateService accountInfoUpdateService) {
|
||||
return new CustomerQueryWorkflow(accountInfoUpdateService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CustomerInfoUpdateService customerInfoUpdateService(QuerySideCustomerRepository querySideCustomerRepository) {
|
||||
return new CustomerInfoUpdateService(querySideCustomerRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CustomerQueryService customerQueryService(QuerySideCustomerRepository accountInfoRepository) {
|
||||
return new CustomerQueryService(accountInfoRepository);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public QuerySideDependencyChecker querysideDependencyChecker(MongoTemplate mongoTemplate) {
|
||||
return new QuerySideDependencyChecker(mongoTemplate);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
|
||||
|
||||
import org.springframework.data.mongodb.repository.MongoRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
interface QuerySideCustomerRepository extends MongoRepository<QuerySideCustomer, String> {
|
||||
|
||||
List<QuerySideCustomer> findByEmailLike(String email);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import rx.Observable;
|
||||
import rx.Subscriber;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class QuerySideDependencyChecker {
|
||||
private Logger logger = LoggerFactory.getLogger(getClass());
|
||||
private MongoTemplate mongoTemplate;
|
||||
|
||||
public QuerySideDependencyChecker(MongoTemplate mongoTemplate) {
|
||||
this.mongoTemplate = mongoTemplate;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void checkDependencies() {
|
||||
try {
|
||||
logger.info("Checking mongodb connectivity {}", System.getenv("SPRING_DATA_MONGODB_URI"));
|
||||
|
||||
Observable.<Object>create(new Observable.OnSubscribe<Object>() {
|
||||
@Override
|
||||
public void call(Subscriber<? super Object> subscriber) {
|
||||
try {
|
||||
subscriber.onNext(mongoTemplate.getDb().getCollectionNames());
|
||||
subscriber.onCompleted();
|
||||
} catch (Throwable t) {
|
||||
subscriber.onError(t);
|
||||
}
|
||||
}
|
||||
}).timeout(5, TimeUnit.SECONDS).toBlocking().first();
|
||||
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Error connecting to Mongo - have you set SPRING_DATA_MONGODB_URI or --spring.data.mongodb_uri?", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
java-spring/customers-query-side-service/build.gradle
Normal file
27
java-spring/customers-query-side-service/build.gradle
Normal file
@@ -0,0 +1,27 @@
|
||||
apply plugin: VerifyMongoDBConfigurationPlugin
|
||||
apply plugin: VerifyEventStoreEnvironmentPlugin
|
||||
|
||||
apply plugin: 'spring-boot'
|
||||
|
||||
dependencies {
|
||||
compile project(":customers-query-side-web")
|
||||
compile project(":common-swagger")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator"
|
||||
|
||||
compile "net.chrisrichardson.eventstore.client:eventstore-http-stomp-client_2.10:$eventStoreClientVersion"
|
||||
|
||||
testCompile project(":testutil")
|
||||
testCompile project(":customers-command-side-service")
|
||||
testCompile "org.springframework.boot:spring-boot-starter-test"
|
||||
}
|
||||
|
||||
test {
|
||||
ignoreFailures true
|
||||
|
||||
environment 'EVENTUATE_API_KEY_ID', '20CAXGPA3DE56WXO2SFBUDGZ9'
|
||||
environment 'EVENTUATE_API_KEY_SECRET', 'gU6n78drWCIgkgzVStvI3BhV3MfzDyjWKCN7p0PBimI'
|
||||
environment 'SPRING_DATA_MONGODB_URI', 'mongodb://198.50.218.51/mydb'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web;
|
||||
|
||||
import net.chrisrichardson.eventstore.client.config.EventStoreHttpClientConfiguration;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers.QuerySideCustomerConfiguration;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.commonswagger.CommonSwaggerConfiguration;
|
||||
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;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
|
||||
@Configuration
|
||||
@Import({QuerySideCustomerConfiguration.class, EventStoreHttpClientConfiguration.class, CommonSwaggerConfiguration.class})
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan
|
||||
public class CustomersQuerySideServiceConfiguration {
|
||||
|
||||
|
||||
@Bean
|
||||
public HttpMessageConverters customConverters() {
|
||||
HttpMessageConverter<?> additional = new MappingJackson2HttpMessageConverter();
|
||||
return new HttpMessageConverters(additional);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web.main;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.web.CustomersQuerySideServiceConfiguration;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
|
||||
public class CustomersQuerySideServiceMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CustomersQuerySideServiceConfiguration.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.Address;
|
||||
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.Name;
|
||||
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Producer;
|
||||
import net.chrisrichardson.eventstorestore.javaexamples.testutil.Verifier;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import rx.Observable;
|
||||
|
||||
import static net.chrisrichardson.eventstorestore.javaexamples.testutil.TestUtil.eventually;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = CustomersQuerySideServiceTestConfiguration.class)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest({"server.port=0", "management.port=0"})
|
||||
public class CustomersQuerySideServiceIntegrationTest {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private int port;
|
||||
|
||||
private String baseUrl(String path) {
|
||||
return "http://localhost:" + port + "/" + path;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldGetCustomerById() {
|
||||
|
||||
CustomerInfo customerInfo = generateCustomerInfo();
|
||||
|
||||
final CustomerResponse customerResponse = restTemplate.postForEntity(baseUrl("/customers"),customerInfo, CustomerResponse.class).getBody();
|
||||
final String customerId = customerResponse.getId();
|
||||
|
||||
assertCustomerResponse(customerId, customerInfo);
|
||||
}
|
||||
|
||||
private void assertCustomerResponse(final String customerId, final CustomerInfo customerInfo) {
|
||||
eventually(
|
||||
new Producer<CustomerResponse>() {
|
||||
@Override
|
||||
public Observable<CustomerResponse> produce() {
|
||||
return Observable.just(restTemplate.getForEntity(baseUrl("/customers/" + customerId), CustomerResponse.class).getBody());
|
||||
}
|
||||
},
|
||||
new Verifier<CustomerResponse>() {
|
||||
@Override
|
||||
public void verify(CustomerResponse customerResponse) {
|
||||
Assert.assertEquals(customerId, customerResponse.getId());
|
||||
Assert.assertEquals(customerInfo, customerResponse.getCustomerInfo());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private CustomerInfo generateCustomerInfo() {
|
||||
return new CustomerInfo(
|
||||
new Name("John", "Doe"),
|
||||
"current@email.com",
|
||||
"000-00-0000",
|
||||
"1-111-111-1111",
|
||||
new Address("street 1",
|
||||
"street 2",
|
||||
"City",
|
||||
"State",
|
||||
"1111111")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
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 java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@Import({CustomersQuerySideServiceConfiguration.class, CustomersCommandSideServiceConfiguration.class})
|
||||
public class CustomersQuerySideServiceTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(HttpMessageConverters converters) {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
HttpMessageConverter<?> httpMessageConverter = converters.getConverters().get(0);
|
||||
List<? extends HttpMessageConverter<?>> httpMessageConverters = Arrays.asList(new MappingJackson2HttpMessageConverter());
|
||||
restTemplate.setMessageConverters((List<HttpMessageConverter<?>>) httpMessageConverters);
|
||||
return restTemplate;
|
||||
}
|
||||
}
|
||||
9
java-spring/customers-query-side-web/build.gradle
Normal file
9
java-spring/customers-query-side-web/build.gradle
Normal file
@@ -0,0 +1,9 @@
|
||||
apply plugin: 'java'
|
||||
|
||||
dependencies {
|
||||
compile project(":customers-query-side-backend")
|
||||
compile project(":common-web")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion"
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web.queryside;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers.QuerySideCustomerConfiguration;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.web.util.ObservableReturnValueHandler;
|
||||
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.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@Import({QuerySideCustomerConfiguration.class})
|
||||
@ComponentScan
|
||||
public class CustomersQuerySideWebConfiguration extends WebMvcConfigurerAdapter {
|
||||
|
||||
class FakeThing {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FakeThing init(RequestMappingHandlerAdapter adapter) {
|
||||
// https://jira.spring.io/browse/SPR-13083
|
||||
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>(adapter.getReturnValueHandlers());
|
||||
handlers.add(0, new ObservableReturnValueHandler());
|
||||
adapter.setReturnValueHandlers(handlers);
|
||||
return new FakeThing();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.EntityIdentifier;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers.CustomerQueryService;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.backend.queryside.customers.QuerySideCustomer;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import rx.Observable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created by Main on 05.02.2016.
|
||||
*/
|
||||
@RestController
|
||||
public class CustomerQueryController {
|
||||
|
||||
private CustomerQueryService customerQueryService;
|
||||
|
||||
@Autowired
|
||||
public CustomerQueryController(CustomerQueryService customerQueryService) {
|
||||
this.customerQueryService = customerQueryService;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/customers/{customerId}", method = RequestMethod.GET)
|
||||
public Observable<CustomerResponse> getCustomer(@PathVariable String customerId) {
|
||||
return customerQueryService.findByCustomerId(new EntityIdentifier(customerId))
|
||||
.map(this::getCustomerResponse);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/customers", method = RequestMethod.GET)
|
||||
public Observable<CustomersQueryResponse> getCustomersByEmail(@RequestParam String email) {
|
||||
return customerQueryService.findByEmail(email)
|
||||
.map(this::getCustomersQueryResponse);
|
||||
}
|
||||
|
||||
|
||||
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "customers not found")
|
||||
@ExceptionHandler(EmptyResultDataAccessException.class)
|
||||
public void customersNotFound() {
|
||||
|
||||
}
|
||||
|
||||
private CustomerResponse getCustomerResponse(QuerySideCustomer querySideCustomer) {
|
||||
return new CustomerResponse(querySideCustomer.getId(), querySideCustomer);
|
||||
}
|
||||
|
||||
private CustomersQueryResponse getCustomersQueryResponse(List<QuerySideCustomer> customersList) {
|
||||
return new CustomersQueryResponse(customersList
|
||||
.stream()
|
||||
.map(this::getCustomerResponse)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.customers;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.CustomerResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Main on 05.02.2016.
|
||||
*/
|
||||
public class CustomersQueryResponse {
|
||||
|
||||
private List<CustomerResponse> customers;
|
||||
|
||||
public CustomersQueryResponse() {
|
||||
}
|
||||
|
||||
public CustomersQueryResponse(List<CustomerResponse> customers) {
|
||||
this.customers = customers;
|
||||
}
|
||||
|
||||
public List<CustomerResponse> getCustomers() {
|
||||
return customers;
|
||||
}
|
||||
|
||||
public void setCustomers(List<CustomerResponse> customers) {
|
||||
this.customers = customers;
|
||||
}
|
||||
}
|
||||
@@ -63,10 +63,10 @@ public class EndToEndTest {
|
||||
BigDecimal finalFromAccountBalance = initialFromAccountBalance.subtract(amountToTransfer);
|
||||
BigDecimal finalToAccountBalance = initialToAccountBalance.add(amountToTransfer);
|
||||
|
||||
final CreateAccountResponse fromAccount = restTemplate.postForEntity(accountsCommandSideBaseUrl("/accounts"), new CreateAccountRequest(initialFromAccountBalance), CreateAccountResponse.class).getBody();
|
||||
final CreateAccountResponse fromAccount = restTemplate.postForEntity(accountsCommandSideBaseUrl("/accounts"), new CreateAccountRequest("00000000-00000000", "My #1 Account", initialFromAccountBalance), CreateAccountResponse.class).getBody();
|
||||
final String fromAccountId = fromAccount.getAccountId();
|
||||
|
||||
CreateAccountResponse toAccount = restTemplate.postForEntity(accountsCommandSideBaseUrl("/accounts"), new CreateAccountRequest(initialToAccountBalance), CreateAccountResponse.class).getBody();
|
||||
CreateAccountResponse toAccount = restTemplate.postForEntity(accountsCommandSideBaseUrl("/accounts"), new CreateAccountRequest("00000000-00000000", "My #2 Account", initialToAccountBalance), CreateAccountResponse.class).getBody();
|
||||
String toAccountId = toAccount.getAccountId();
|
||||
|
||||
Assert.assertNotNull(fromAccountId);
|
||||
|
||||
@@ -6,6 +6,8 @@ dependencies {
|
||||
compile project(":accounts-query-side-web")
|
||||
compile project(":accounts-command-side-web")
|
||||
compile project(":transactions-command-side-web")
|
||||
compile project(":customers-command-side-web")
|
||||
compile project(":customers-query-side-web")
|
||||
|
||||
compile "org.springframework.boot:spring-boot-starter-web"
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator"
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web;
|
||||
|
||||
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.queryside.CustomersQuerySideWebConfiguration;
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.web.queryside.QuerySideWebConfiguration;
|
||||
import net.chrisrichardson.eventstore.jdbc.config.JdbcEventStoreConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -14,7 +16,7 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
|
||||
@Configuration
|
||||
@Import({CommandSideWebAccountsConfiguration.class, CommandSideWebTransactionsConfiguration.class, JdbcEventStoreConfiguration.class, QuerySideWebConfiguration.class})
|
||||
@Import({CommandSideWebAccountsConfiguration.class, CommandSideWebTransactionsConfiguration.class, JdbcEventStoreConfiguration.class, QuerySideWebConfiguration.class, CustomersQuerySideWebConfiguration.class, CustomersCommandSideWebConfiguration.class})
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan
|
||||
public class BankingWebConfiguration {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.chrisrichardson.eventstore.javaexamples.banking.web;
|
||||
|
||||
import net.chrisrichardson.eventstore.javaexamples.banking.common.customers.*;
|
||||
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;
|
||||
@@ -50,10 +51,10 @@ public class BankingWebIntegrationTest {
|
||||
BigDecimal finalFromAccountBalance = initialFromAccountBalance.subtract(amountToTransfer);
|
||||
BigDecimal finalToAccountBalance = initialToAccountBalance.add(amountToTransfer);
|
||||
|
||||
final CreateAccountResponse fromAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest(initialFromAccountBalance), CreateAccountResponse.class).getBody();
|
||||
final CreateAccountResponse fromAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest("00000000-00000000", "My Account", initialFromAccountBalance), CreateAccountResponse.class).getBody();
|
||||
final String fromAccountId = fromAccount.getAccountId();
|
||||
|
||||
CreateAccountResponse toAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest(initialToAccountBalance), CreateAccountResponse.class).getBody();
|
||||
CreateAccountResponse toAccount = restTemplate.postForEntity(baseUrl("/accounts"), new CreateAccountRequest("00000000-00000000", "My Account", initialToAccountBalance), CreateAccountResponse.class).getBody();
|
||||
String toAccountId = toAccount.getAccountId();
|
||||
|
||||
Assert.assertNotNull(fromAccountId);
|
||||
@@ -71,6 +72,19 @@ public class BankingWebIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCustomers() {
|
||||
CustomerInfo customerInfo = generateCustomerInfo();
|
||||
|
||||
final CustomerResponse customerResponse = restTemplate.postForEntity(baseUrl("/customers"), customerInfo, CustomerResponse.class).getBody();
|
||||
final String customerId = customerResponse.getId();
|
||||
|
||||
Assert.assertNotNull(customerId);
|
||||
Assert.assertEquals(customerInfo, customerResponse.getCustomerInfo());
|
||||
|
||||
assertCustomerResponse(customerId, customerInfo);
|
||||
}
|
||||
|
||||
private BigDecimal toCents(BigDecimal dollarAmount) {
|
||||
return dollarAmount.multiply(new BigDecimal(100));
|
||||
}
|
||||
@@ -93,4 +107,38 @@ public class BankingWebIntegrationTest {
|
||||
});
|
||||
}
|
||||
|
||||
private void assertCustomerResponse(final String customerId, final CustomerInfo customerInfo) {
|
||||
eventually(
|
||||
new Producer<CustomerResponse>() {
|
||||
@Override
|
||||
public Observable<CustomerResponse> produce() {
|
||||
return Observable.just(restTemplate.getForEntity(baseUrl("/customers/" + customerId), CustomerResponse.class).getBody());
|
||||
}
|
||||
},
|
||||
new Verifier<CustomerResponse>() {
|
||||
@Override
|
||||
public void verify(CustomerResponse customerResponse) {
|
||||
Assert.assertEquals(customerId, customerResponse.getId());
|
||||
Assert.assertEquals(customerInfo, customerResponse.getCustomerInfo());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private CustomerInfo generateCustomerInfo() {
|
||||
return new CustomerInfo(
|
||||
new Name("John", "Doe"),
|
||||
"current@email.com",
|
||||
"000-00-0000",
|
||||
"1-111-111-1111",
|
||||
new Address("street 1",
|
||||
"street 2",
|
||||
"City",
|
||||
"State",
|
||||
"1111111")
|
||||
);
|
||||
}
|
||||
|
||||
private ToAccountInfo generateToAccountInfo() {
|
||||
return new ToAccountInfo("11111111-11111111", "New Account", "John Doe");
|
||||
}
|
||||
}
|
||||
|
||||
377
java-spring/schemas/java-mt-demo-extended-api.json
Normal file
377
java-spring/schemas/java-mt-demo-extended-api.json
Normal file
@@ -0,0 +1,377 @@
|
||||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "Api Documentation",
|
||||
"version": "1.0",
|
||||
"title": "Api Documentation",
|
||||
"termsOfService": "urn:tos",
|
||||
"contact": {
|
||||
"name": "Contact Email"
|
||||
},
|
||||
"license": {
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0"
|
||||
}
|
||||
},
|
||||
"host": "localhost:8080",
|
||||
"basePath": "/",
|
||||
"tags": [
|
||||
{
|
||||
"name": "customer-service-command-side-controller",
|
||||
"description": "Customer Service Commandside Controller"
|
||||
},
|
||||
{
|
||||
"name": "customer-service-query-side-controller",
|
||||
"description": "Customer Service Queryside Controller"
|
||||
},
|
||||
{
|
||||
"name": "account-query-side-controller",
|
||||
"description": "Account Service Queryside Controller"
|
||||
},
|
||||
{
|
||||
"name": "auth-controller",
|
||||
"description": "Authentication Controller"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/login": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"auth-controller"
|
||||
],
|
||||
"summary": "doAuth",
|
||||
"operationId": "doAuthUsingPOST",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"description": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AuthRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AuthResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/accounts": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"account-query-side-controller"
|
||||
],
|
||||
"summary": "getAllAccountsByCustomer",
|
||||
"operationId": "getAllAccountsByCustomerUsingGET",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "customerId",
|
||||
"in": "query",
|
||||
"description": "customer id",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/CustomersQueryResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/customers": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"customer-service-query-side-controller"
|
||||
],
|
||||
"summary": "getAllCustomersByEmail",
|
||||
"operationId": "getAllCustomersByEmailUsingGET",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "email",
|
||||
"in": "query",
|
||||
"description": "customer's email",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/CustomersQueryResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"customer-service-command-side-controller"
|
||||
],
|
||||
"summary": "saveCustomer",
|
||||
"operationId": "saveCustomerUsingPOST",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "body",
|
||||
"name": "customer",
|
||||
"description": "customer",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/CreateCustomerRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/CustomerResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Validation error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/customers/{id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"customer-service-query-side-controller"
|
||||
],
|
||||
"summary": "getBoard",
|
||||
"operationId": "getBoardUsingGET",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"description": "id",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/CustomerResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/customers/{id}/toaccounts": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"customer-service-command-side-controller"
|
||||
],
|
||||
"summary": "addToAccount",
|
||||
"operationId": "addToAccountUsingPOST",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"*/*"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"description": "id",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"in": "body",
|
||||
"name": "request",
|
||||
"description": "request",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/ToAccountsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
},
|
||||
"400": {
|
||||
"description": "Validation error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"AuthRequest": {
|
||||
"required": [ "email" ],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AuthResponse": {
|
||||
"properties": {
|
||||
"token": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CustomerInfo": {
|
||||
"required": [ "email" ],
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"ssn": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CustomersQueryResponse": {
|
||||
"properties": {
|
||||
"customers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/CustomerResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CustomerResponse": {
|
||||
"required": [ "id", "customerInfo" ],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"customerInfo": {
|
||||
"$ref": "#/definitions/CustomerInfo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AccountsQueryResponse": {
|
||||
"properties": {
|
||||
"customers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/GetAccountResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"GetAccountResponse": {
|
||||
"properties": {
|
||||
"accountId": {
|
||||
"type": "string"
|
||||
},
|
||||
"balance": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateCustomerRequest": {
|
||||
"required": [ "email", "ssn", "phoneNumber" ],
|
||||
"properties": {
|
||||
"firstName": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastName": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"ssn": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "string"
|
||||
},
|
||||
"address": {
|
||||
"$ref": "#/definitions/Address"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ToAccountsRequest":{
|
||||
"required": [ "id", "owner" ],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"owner": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Address": {
|
||||
"required": [ "street1", "city", "state", "zipCode" ],
|
||||
"properties": {
|
||||
"street1": {
|
||||
"type": "string"
|
||||
},
|
||||
"street2": {
|
||||
"type": "string"
|
||||
},
|
||||
"city": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
},
|
||||
"zipCode": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,5 +24,13 @@ include 'transactions-command-side-service'
|
||||
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-controller'
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ public class TestUtil {
|
||||
|
||||
public static <T> void eventually(final Producer<T> producer, final Verifier<T> verifier) {
|
||||
final int n = 50;
|
||||
Object possibleException = Observable.timer(0, 100, TimeUnit.MILLISECONDS).flatMap(new Func1<Long, Observable<Outcome<T>>>() {
|
||||
Object possibleException = Observable.timer(0, 200, TimeUnit.MILLISECONDS).flatMap(new Func1<Long, Observable<Outcome<T>>>() {
|
||||
|
||||
@Override
|
||||
public Observable<Outcome<T>> call(Long aLong) {
|
||||
|
||||
Reference in New Issue
Block a user