diff --git a/_build-and-test-all.sh b/_build-and-test-all.sh index 41bfd51d..66fd5eef 100755 --- a/_build-and-test-all.sh +++ b/_build-and-test-all.sh @@ -19,7 +19,7 @@ docker-compose -f docker-compose-${DATABASE?}.yml up -d --build cdcservice docker-compose -f docker-compose-${DATABASE?}.yml up -d --build -./wait-for-services.sh $DOCKER_HOST_IP "8081 8082" +./wait-for-services.sh $DOCKER_HOST_IP "8081 8082 8083" ./gradlew :end-to-end-tests:cleanTest :end-to-end-tests:test diff --git a/buildSrc/src/main/groovy/VerifyMongoDBConfigurationPlugin.groovy b/buildSrc/src/main/groovy/VerifyMongoDBConfigurationPlugin.groovy new file mode 100644 index 00000000..1486a0d0 --- /dev/null +++ b/buildSrc/src/main/groovy/VerifyMongoDBConfigurationPlugin.groovy @@ -0,0 +1,15 @@ +import org.gradle.api.* + + +class VerifyMongoDBConfigurationPlugin implements Plugin { + void apply(Project project) { + project.test { + beforeSuite { x -> + if (x.parent == null) { + if (System.getenv("SPRING_DATA_MONGODB_URI") == null) + throw new RuntimeException("Please make sure that the environment variable SPRING_DATA_MONGODB_URI is set, e.g. export SPRING_DATA_MONGODB_URI=mongodb://192.168.59.103/mydb") + } + } + } + } +} \ No newline at end of file diff --git a/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreatedEvent.java b/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreatedEvent.java new file mode 100644 index 00000000..0da08a6a --- /dev/null +++ b/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreatedEvent.java @@ -0,0 +1,42 @@ +package io.eventuate.examples.tram.ordersandcustomers.commondomain; + +import io.eventuate.tram.events.common.DomainEvent; + +public class CustomerCreatedEvent implements DomainEvent { + private Long customerId; + private String name; + private Money creditLimit; + + public CustomerCreatedEvent() { + } + + public CustomerCreatedEvent(Long customerId, String name, Money creditLimit) { + this.customerId = customerId; + this.name = name; + this.creditLimit = creditLimit; + } + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(Long customerId) { + this.customerId = customerId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Money getCreditLimit() { + return creditLimit; + } + + public void setCreditLimit(Money creditLimit) { + this.creditLimit = creditLimit; + } +} diff --git a/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreditReservationFailedEvent.java b/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreditReservationFailedEvent.java index 58fb0057..53f2438d 100644 --- a/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreditReservationFailedEvent.java +++ b/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreditReservationFailedEvent.java @@ -5,18 +5,24 @@ public class CustomerCreditReservationFailedEvent implements DomainEvent { private Long orderId; + private OrderDetails orderDetails; public CustomerCreditReservationFailedEvent() { } - public CustomerCreditReservationFailedEvent(Long orderId) { + public CustomerCreditReservationFailedEvent(Long orderId, OrderDetails orderDetails) { this.orderId = orderId; + this.orderDetails = orderDetails; } public Long getOrderId() { return orderId; } + public OrderDetails getOrderDetails() { + return orderDetails; + } + public void setOrderId(Long orderId) { this.orderId = orderId; } diff --git a/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreditReservedEvent.java b/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreditReservedEvent.java index 50fe7c19..8ebc016a 100644 --- a/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreditReservedEvent.java +++ b/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/CustomerCreditReservedEvent.java @@ -5,18 +5,24 @@ public class CustomerCreditReservedEvent implements DomainEvent { private Long orderId; + private OrderDetails orderDetails; public CustomerCreditReservedEvent() { } - public CustomerCreditReservedEvent(Long orderId) { + public CustomerCreditReservedEvent(Long orderId, OrderDetails orderDetails) { this.orderId = orderId; + this.orderDetails = orderDetails; } public Long getOrderId() { return orderId; } + public OrderDetails getOrderDetails() { + return orderDetails; + } + public void setOrderId(Long orderId) { this.orderId = orderId; } diff --git a/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/OrderState.java b/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/OrderState.java new file mode 100644 index 00000000..627d2a74 --- /dev/null +++ b/common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/commondomain/OrderState.java @@ -0,0 +1,3 @@ +package io.eventuate.examples.tram.ordersandcustomers.commondomain; + +public enum OrderState { PENDING, APPROVED, REJECTED } diff --git a/customer-backend/build.gradle b/customer-backend/build.gradle index 37523476..784ec41b 100644 --- a/customer-backend/build.gradle +++ b/customer-backend/build.gradle @@ -2,6 +2,7 @@ dependencies { compile project(":common") compile "io.eventuate.tram.core:eventuate-tram-jdbc-kafka:$eventuateTramVersion" + compile "io.eventuate.tram.core:eventuate-tram-events:$eventuateTramVersion" compile 'mysql:mysql-connector-java:5.1.36' compile ('org.postgresql:postgresql:9.4-1200-jdbc41') { diff --git a/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/domain/Customer.java b/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/domain/Customer.java index 3b461b87..60bd91f2 100644 --- a/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/domain/Customer.java +++ b/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/domain/Customer.java @@ -39,6 +39,14 @@ public Long getId() { return id; } + public String getName() { + return name; + } + + public Money getCreditLimit() { + return creditLimit; + } + public void reserveCredit(Long orderId, Money orderTotal) { if (availableCredit().isGreaterThanOrEqual(orderTotal)) { creditReservations.put(orderId, orderTotal); diff --git a/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/service/CustomerService.java b/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/service/CustomerService.java index ed2fc02f..10f549aa 100644 --- a/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/service/CustomerService.java +++ b/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/service/CustomerService.java @@ -1,17 +1,31 @@ package io.eventuate.examples.tram.ordersandcustomers.customers.service; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.CustomerCreatedEvent; import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; import io.eventuate.examples.tram.ordersandcustomers.customers.domain.Customer; import io.eventuate.examples.tram.ordersandcustomers.customers.domain.CustomerRepository; +import io.eventuate.tram.events.publisher.DomainEventPublisher; import org.springframework.beans.factory.annotation.Autowired; +import java.util.Collections; + public class CustomerService { @Autowired private CustomerRepository customerRepository; + @Autowired + private DomainEventPublisher domainEventPublisher; + public Customer createCustomer(String name, Money creditLimit) { Customer customer = new Customer(name, creditLimit); - return customerRepository.save(customer); + customer = customerRepository.save(customer); + + domainEventPublisher.publish(Customer.class, + customer.getId(), + Collections.singletonList(new CustomerCreatedEvent(customer.getId(), + customer.getName(), customer.getCreditLimit()))); + + return customer; } } diff --git a/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/service/OrderEventConsumer.java b/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/service/OrderEventConsumer.java index ba63e384..5e076ccb 100644 --- a/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/service/OrderEventConsumer.java +++ b/customer-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/service/OrderEventConsumer.java @@ -42,7 +42,8 @@ private void orderCreatedEventHandler(DomainEventEnvelope dom orderCreatedEvent.getOrderDetails().getOrderTotal()); CustomerCreditReservedEvent customerCreditReservedEvent = - new CustomerCreditReservedEvent(orderCreatedEvent.getOrderId()); + new CustomerCreditReservedEvent(orderCreatedEvent.getOrderId(), + orderCreatedEvent.getOrderDetails()); domainEventPublisher.publish(Customer.class, customer.getId(), @@ -51,7 +52,9 @@ private void orderCreatedEventHandler(DomainEventEnvelope dom } catch (CustomerCreditLimitExceededException e) { CustomerCreditReservationFailedEvent customerCreditReservationFailedEvent = - new CustomerCreditReservationFailedEvent(orderCreatedEvent.getOrderId()); + new CustomerCreditReservationFailedEvent(orderCreatedEvent.getOrderId(), + orderCreatedEvent.getOrderDetails()); + domainEventPublisher.publish(Customer.class, customer.getId(), diff --git a/customer-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/CustomersServiceMain.java b/customer-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/CustomerServiceMain.java similarity index 68% rename from customer-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/CustomersServiceMain.java rename to customer-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/CustomerServiceMain.java index 6555545c..d688a030 100644 --- a/customer-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/CustomersServiceMain.java +++ b/customer-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/customers/CustomerServiceMain.java @@ -3,14 +3,12 @@ import io.eventuate.examples.tram.ordersandcustomers.customers.web.CustomerWebConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @SpringBootApplication @Import({CustomerConfiguration.class, CustomerWebConfiguration.class}) -public class CustomersServiceMain { +public class CustomerServiceMain { public static void main(String[] args) { - SpringApplication.run(CustomersServiceMain.class, args); + SpringApplication.run(CustomerServiceMain.class, args); } } diff --git a/docker-compose-mysql.yml b/docker-compose-mysql.yml index 18900dbb..a2d791f5 100755 --- a/docker-compose-mysql.yml +++ b/docker-compose-mysql.yml @@ -36,6 +36,26 @@ customerservice: EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 +orderhistoryservice: + build: ./order-history-view-service/ + ports: + - "8083:8080" + links: + - mongodb + - mysql + - kafka + - zookeeper + environment: + SPRING_DATA_MONGODB_URI: mongodb://mongodb/clients_and_orders + SPRING_DATASOURCE_URL: jdbc:mysql://mysql/eventuate + SPRING_DATASOURCE_USERNAME: mysqluser + SPRING_DATASOURCE_PASSWORD: mysqlpw + SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver + EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 + EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 + EVENTUATELOCAL_CDC_DB_USER_NAME: root + EVENTUATELOCAL_CDC_DB_PASSWORD: rootpassword + zookeeper: image: eventuateio/eventuateio-local-zookeeper:0.17.0.RELEASE ports: @@ -65,6 +85,14 @@ mysql: - MYSQL_USER=mysqluser - MYSQL_PASSWORD=mysqlpw + +mongodb: + image: mongo:3.0.4 + hostname: mongodb + command: mongod --smallfiles + ports: + - "27017:27017" + cdcservice: image: eventuateio/eventuate-tram-cdc-mysql-service:0.6.0.RELEASE ports: diff --git a/docker-compose-postgres.yml b/docker-compose-postgres.yml index 49b2c6dd..29ae0e68 100755 --- a/docker-compose-postgres.yml +++ b/docker-compose-postgres.yml @@ -54,6 +54,33 @@ customerservice: EVENTUATELOCAL_CDC_SOURCE_TABLE_NAME: message SPRING_PROFILES_ACTIVE: EventuatePolling +orderhistoryservice: + build: ./order-history-view-service/ + ports: + - "8083:8080" + links: + - mongodb + - postgres + - kafka + - zookeeper + environment: + SPRING_DATA_MONGODB_URI: mongodb://mongodb/clients_and_orders + SPRING_DATASOURCE_URL: jdbc:postgresql://postgres/eventuate + SPRING_DATASOURCE_USERNAME: eventuate + SPRING_DATASOURCE_PASSWORD: eventuate + SPRING_DATASOURCE_TEST_ON_BORROW: "true" + SPRING_DATASOURCE_VALIDATION_QUERY: SELECT 1 + SPRING_DATASOURCE_DRIVER_CLASS_NAME: org.postgresql.Driver + EVENTUATELOCAL_CDC_POLLING_INTERVAL_IN_MILLISECONDS: 500 + EVENTUATELOCAL_CDC_MAX_EVENTS_PER_POLLING: 1000 + EVENTUATELOCAL_CDC_MAX_ATTEMPTS_FOR_POLLING: 100 + EVENTUATELOCAL_CDC_POLLING_RETRY_INTERVAL_IN_MILLISECONDS: 500 + EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 + EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 + EVENTUATELOCAL_CDC_BINLOG_CLIENT_ID: 1234567890 + EVENTUATELOCAL_CDC_SOURCE_TABLE_NAME: message + SPRING_PROFILES_ACTIVE: EventuatePolling + zookeeper: image: eventuateio/eventuateio-local-zookeeper:0.17.0.RELEASE ports: @@ -82,6 +109,14 @@ postgres: POSTGRES_USER: eventuate POSTGRES_PASSWORD: eventuate + +mongodb: + image: mongo:3.0.4 + hostname: mongodb + command: mongod --smallfiles + ports: + - "27017:27017" + cdcservice: image: eventuateio/eventuate-tram-cdc-mysql-service:0.6.0.RELEASE ports: diff --git a/end-to-end-tests/build.gradle b/end-to-end-tests/build.gradle index be93b365..eeb7e54c 100644 --- a/end-to-end-tests/build.gradle +++ b/end-to-end-tests/build.gradle @@ -1,4 +1,5 @@ dependencies { + compile project(":order-history-common") compile project(":customer-web-api") compile project(":order-web-api") diff --git a/end-to-end-tests/src/test/java/io/eventuate/examples/tram/ordersandcustomers/endtoendtests/CustomersAndOrdersE2ETest.java b/end-to-end-tests/src/test/java/io/eventuate/examples/tram/ordersandcustomers/endtoendtests/CustomersAndOrdersE2ETest.java index 02227080..d9dcecc0 100644 --- a/end-to-end-tests/src/test/java/io/eventuate/examples/tram/ordersandcustomers/endtoendtests/CustomersAndOrdersE2ETest.java +++ b/end-to-end-tests/src/test/java/io/eventuate/examples/tram/ordersandcustomers/endtoendtests/CustomersAndOrdersE2ETest.java @@ -3,10 +3,12 @@ import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; import io.eventuate.examples.tram.ordersandcustomers.customers.webapi.CreateCustomerRequest; import io.eventuate.examples.tram.ordersandcustomers.customers.webapi.CreateCustomerResponse; -import io.eventuate.examples.tram.ordersandcustomers.orders.domain.OrderState; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; import io.eventuate.examples.tram.ordersandcustomers.orders.webapi.CreateOrderRequest; import io.eventuate.examples.tram.ordersandcustomers.orders.webapi.CreateOrderResponse; import io.eventuate.examples.tram.ordersandcustomers.orders.webapi.GetOrderResponse; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.CustomerView; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.OrderView; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,6 +37,10 @@ private String baseUrlCustomers(String path) { return "http://"+hostName+":8082/" + path; } + private String baseUrlOrderHistory(String path) { + return "http://"+hostName+":8083/" + path; + } + @Autowired RestTemplate restTemplate; @@ -60,6 +66,84 @@ public void shouldReject() throws Exception { assertOrderState(createOrderResponse.getOrderId(), OrderState.REJECTED); } + @Test + public void shouldKeepHistory() throws Exception { + CreateCustomerResponse createCustomerResponseResponse = restTemplate.postForObject(baseUrlCustomers("customers"), + new CreateCustomerRequest("John", new Money("1000.00")), CreateCustomerResponse.class); + + CreateOrderResponse approvedCreateOrderResponse = restTemplate.postForObject(baseUrlOrders("orders"), + new CreateOrderRequest(createCustomerResponseResponse.getCustomerId(), new Money("100")), CreateOrderResponse.class); + + Long approvedOrderId = approvedCreateOrderResponse.getOrderId(); + + CreateOrderResponse rejectedCreateOrdeResponse = restTemplate.postForObject(baseUrlOrders("orders"), + new CreateOrderRequest(createCustomerResponseResponse.getCustomerId(), new Money("1000")), CreateOrderResponse.class); + + Long rejectedOrderId = rejectedCreateOrdeResponse.getOrderId(); + + String approvedOrderHistoryUrl = baseUrlOrderHistory("orders") + "/" + approvedCreateOrderResponse.getOrderId(); + String rejectedOrderHistoryUrl = baseUrlOrderHistory("orders") + "/" + rejectedCreateOrdeResponse.getOrderId(); + String customerHistoryUrl = baseUrlOrderHistory("customers") + "/" + createCustomerResponseResponse.getCustomerId(); + + ResponseEntity customerViewResponseEntity = null; + ResponseEntity approvedOrderViewResponseEntity = null; + ResponseEntity rejectedOrderViewResponseEntity = null; + + OrderState approvedCustomerOrderState = null; + OrderState rejectedCustomerOrderState = null; + + for (int i = 0; i < 30; i++) { + try { + customerViewResponseEntity = restTemplate.getForEntity(customerHistoryUrl, + CustomerView.class); + + approvedOrderViewResponseEntity = restTemplate.getForEntity(approvedOrderHistoryUrl, + OrderView.class); + + rejectedOrderViewResponseEntity = restTemplate.getForEntity(rejectedOrderHistoryUrl, + OrderView.class); + + if (approvedOrderViewResponseEntity.getBody().getState() != OrderState.APPROVED || + rejectedOrderViewResponseEntity.getBody().getState() != OrderState.REJECTED || + customerViewResponseEntity.getBody().getOrders().size() != 2) { + + Thread.sleep(400); + + continue; + } + + approvedCustomerOrderState = customerViewResponseEntity.getBody().getOrders().get(approvedOrderId).getState(); + rejectedCustomerOrderState = customerViewResponseEntity.getBody().getOrders().get(rejectedOrderId).getState(); + + if (approvedCustomerOrderState != OrderState.APPROVED || rejectedCustomerOrderState != OrderState.REJECTED) { + Thread.sleep(400); + continue; + } + + break; + } catch (Exception e) { + Thread.sleep(400); + } + } + + Assert.assertNotNull(customerViewResponseEntity); + Assert.assertNotNull(approvedOrderViewResponseEntity); + Assert.assertNotNull(rejectedOrderViewResponseEntity); + + + Assert.assertTrue(customerViewResponseEntity.getStatusCode().is2xxSuccessful()); + Assert.assertTrue(approvedOrderViewResponseEntity.getStatusCode().is2xxSuccessful()); + Assert.assertTrue(rejectedOrderViewResponseEntity.getStatusCode().is2xxSuccessful()); + + Assert.assertEquals(2, customerViewResponseEntity.getBody().getOrders().size()); + Assert.assertEquals(OrderState.APPROVED, approvedCustomerOrderState); + Assert.assertEquals(OrderState.REJECTED, rejectedCustomerOrderState); + + + Assert.assertEquals(OrderState.APPROVED, approvedOrderViewResponseEntity.getBody().getState()); + Assert.assertEquals(OrderState.REJECTED, rejectedOrderViewResponseEntity.getBody().getState()); + } + private void assertOrderState(Long id, OrderState expectedState) throws InterruptedException { GetOrderResponse order = null; for (int i = 0; i < 30; i++) { diff --git a/gradle.properties b/gradle.properties index 47049f10..4b94063c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,5 @@ eventuateMavenRepoUrl=https://dl.bintray.com/eventuateio-oss/eventuate-maven-rel springBootVersion=1.4.5.RELEASE eventuateClientVersion=0.20.1.RELEASE eventuateTramVersion=0.6.0.RELEASE +dockerComposePluginVersion=0.4.5 version=0.1.0-SNAPSHOT \ No newline at end of file diff --git a/order-backend/build.gradle b/order-backend/build.gradle index 98af59ac..ed45372d 100644 --- a/order-backend/build.gradle +++ b/order-backend/build.gradle @@ -2,6 +2,7 @@ dependencies { compile project(":common") compile "io.eventuate.tram.core:eventuate-tram-jdbc-kafka:$eventuateTramVersion" + compile "io.eventuate.tram.core:eventuate-tram-events:$eventuateTramVersion" compile 'mysql:mysql-connector-java:5.1.36' compile ('org.postgresql:postgresql:9.4-1200-jdbc41') { diff --git a/order-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/domain/Order.java b/order-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/domain/Order.java index f3cc9bc6..74a5b1be 100644 --- a/order-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/domain/Order.java +++ b/order-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/domain/Order.java @@ -2,6 +2,7 @@ import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderDetails; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; import javax.persistence.*; diff --git a/order-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/domain/OrderState.java b/order-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/domain/OrderState.java deleted file mode 100644 index 9f0be0c0..00000000 --- a/order-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/domain/OrderState.java +++ /dev/null @@ -1,3 +0,0 @@ -package io.eventuate.examples.tram.ordersandcustomers.orders.domain; - -public enum OrderState { PENDING, APPROVED, REJECTED } diff --git a/order-history-backend/build.gradle b/order-history-backend/build.gradle new file mode 100644 index 00000000..546782b3 --- /dev/null +++ b/order-history-backend/build.gradle @@ -0,0 +1,33 @@ +buildscript { + repositories { + mavenCentral() + jcenter() + } + dependencies { + classpath "com.avast.gradle:docker-compose-gradle-plugin:$dockerComposePluginVersion" + } +} + +apply plugin: VerifyMongoDBConfigurationPlugin + +apply plugin: 'docker-compose' + +dockerCompose.isRequiredBy(test) + +dockerCompose { + useComposeFiles = ['docker-compose-integration-tests.yml'] +} + +dependencies { + compile project(":order-history-common") + + compile "io.eventuate.tram.core:eventuate-tram-jdbc-kafka:$eventuateTramVersion" + + compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" + compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" + compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion" + compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion" + + testCompile "junit:junit:4.12" + testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" +} diff --git a/order-history-backend/docker-compose-integration-tests.yml b/order-history-backend/docker-compose-integration-tests.yml new file mode 100644 index 00000000..bcac1114 --- /dev/null +++ b/order-history-backend/docker-compose-integration-tests.yml @@ -0,0 +1,6 @@ +mongodb: + image: mongo:3.0.4 + hostname: mongodb + command: mongod --smallfiles + ports: + - "27017:27017" diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerHistoryEventConsumer.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerHistoryEventConsumer.java new file mode 100644 index 00000000..24db32d3 --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerHistoryEventConsumer.java @@ -0,0 +1,44 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.CustomerCreatedEvent; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.CustomerCreditReservationFailedEvent; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.CustomerCreditReservedEvent; +import io.eventuate.tram.events.subscriber.DomainEventEnvelope; +import io.eventuate.tram.events.subscriber.DomainEventHandlers; +import io.eventuate.tram.events.subscriber.DomainEventHandlersBuilder; +import org.springframework.beans.factory.annotation.Autowired; + + +public class CustomerHistoryEventConsumer { + + @Autowired + private OrderHistoryViewService orderHistoryViewService; + + + public DomainEventHandlers domainEventHandlers() { + return DomainEventHandlersBuilder + .forAggregateType("io.eventuate.examples.tram.ordersandcustomers.customers.domain.Customer") + .onEvent(CustomerCreatedEvent.class, this::customerCreatedEventHandler) + .onEvent(CustomerCreditReservedEvent.class, this::customerCreditReservedEventHandler) + .onEvent(CustomerCreditReservationFailedEvent.class, this::customerCreditReservationFailedEventHandler) + .build(); + } + + private void customerCreatedEventHandler(DomainEventEnvelope domainEventEnvelope) { + CustomerCreatedEvent customerCreatedEvent = domainEventEnvelope.getEvent(); + orderHistoryViewService.createCustomer(customerCreatedEvent.getCustomerId(), + customerCreatedEvent.getName(), customerCreatedEvent.getCreditLimit()); + } + + private void customerCreditReservedEventHandler(DomainEventEnvelope domainEventEnvelope) { + CustomerCreditReservedEvent customerCreditReservedEvent = domainEventEnvelope.getEvent(); + orderHistoryViewService.approveOrder(customerCreditReservedEvent.getOrderDetails().getCustomerId(), + customerCreditReservedEvent.getOrderId()); + } + + private void customerCreditReservationFailedEventHandler(DomainEventEnvelope domainEventEnvelope) { + CustomerCreditReservationFailedEvent customerCreditReservationFailedEvent = domainEventEnvelope.getEvent(); + orderHistoryViewService.rejectOrder(customerCreditReservationFailedEvent.getOrderDetails().getCustomerId(), + customerCreditReservationFailedEvent.getOrderId()); + } +} diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerViewRepository.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerViewRepository.java new file mode 100644 index 00000000..496b43cd --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerViewRepository.java @@ -0,0 +1,8 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.CustomerView; +import org.springframework.data.mongodb.repository.MongoRepository; + +public interface CustomerViewRepository + extends MongoRepository, CustomerViewRepositoryCustom { +} diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerViewRepositoryCustom.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerViewRepositoryCustom.java new file mode 100644 index 00000000..6bf2275f --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerViewRepositoryCustom.java @@ -0,0 +1,13 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; + +public interface CustomerViewRepositoryCustom { + + void addCustomer(Long customerId, String customerName, Money creditLimit); + + void addOrder(Long customerId, Long orderId, Money orderTotal); + + void updateOrderState(Long customerId, Long orderId, OrderState state); +} diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerViewRepositoryImpl.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerViewRepositoryImpl.java new file mode 100644 index 00000000..4bb059f3 --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/CustomerViewRepositoryImpl.java @@ -0,0 +1,40 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.CustomerView; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.OrderInfo; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; + +import static org.springframework.data.mongodb.core.query.Criteria.where; + +public class CustomerViewRepositoryImpl implements CustomerViewRepositoryCustom { + + private MongoTemplate mongoTemplate; + + @Autowired + public CustomerViewRepositoryImpl(MongoTemplate mongoTemplate) { + this.mongoTemplate = mongoTemplate; + } + + @Override + public void addCustomer(Long customerId, String customerName, Money creditLimit) { + mongoTemplate.upsert(new Query(where("id").is(customerId)), + new Update().set("name", customerName).set("creditLimit", creditLimit), CustomerView.class); + } + + @Override + public void addOrder(Long customerId, Long orderId, Money orderTotal) { + mongoTemplate.upsert(new Query(where("id").is(customerId)), + new Update().set("orders." + orderId, new OrderInfo(orderId, orderTotal)), CustomerView.class); + } + + @Override + public void updateOrderState(Long customerId, Long orderId, OrderState state) { + mongoTemplate.upsert(new Query(where("id").is(customerId)), + new Update().set("orders." + orderId + ".state", state), CustomerView.class); + } +} diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryEventConsumer.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryEventConsumer.java new file mode 100644 index 00000000..f04a6912 --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryEventConsumer.java @@ -0,0 +1,26 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderCreatedEvent; +import io.eventuate.tram.events.subscriber.DomainEventEnvelope; +import io.eventuate.tram.events.subscriber.DomainEventHandlers; +import io.eventuate.tram.events.subscriber.DomainEventHandlersBuilder; +import org.springframework.beans.factory.annotation.Autowired; + +public class OrderHistoryEventConsumer { + + @Autowired + private OrderHistoryViewService orderHistoryViewService; + + public DomainEventHandlers domainEventHandlers() { + return DomainEventHandlersBuilder + .forAggregateType("io.eventuate.examples.tram.ordersandcustomers.orders.domain.Order") + .onEvent(OrderCreatedEvent.class, this::orderCreatedEventHandler) + .build(); + } + + private void orderCreatedEventHandler(DomainEventEnvelope domainEventEnvelope) { + OrderCreatedEvent orderCreatedEvent = domainEventEnvelope.getEvent(); + orderHistoryViewService.addOrder(orderCreatedEvent.getOrderDetails().getCustomerId(), + orderCreatedEvent.getOrderId(), orderCreatedEvent.getOrderDetails().getOrderTotal()); + } +} diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryViewBackendConfiguration.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryViewBackendConfiguration.java new file mode 100644 index 00000000..0740903c --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryViewBackendConfiguration.java @@ -0,0 +1,44 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +import io.eventuate.tram.consumer.kafka.TramConsumerKafkaConfiguration; +import io.eventuate.tram.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.events.subscriber.DomainEventDispatcher; +import io.eventuate.tram.messaging.consumer.MessageConsumer; +import io.eventuate.tram.messaging.producer.jdbc.TramMessageProducerJdbcConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import({OrderHistoryViewMongoConfiguration.class, + TramConsumerKafkaConfiguration.class, + TramEventsPublisherConfiguration.class, + TramMessageProducerJdbcConfiguration.class}) +public class OrderHistoryViewBackendConfiguration { + + @Bean + public OrderHistoryEventConsumer orderEventConsumer() { + return new OrderHistoryEventConsumer(); + } + + @Bean("orderHistoryDomainEventDispatcher") + public DomainEventDispatcher orderHistoryDomainEventDispatcher(OrderHistoryEventConsumer orderHistoryEventConsumer, + MessageConsumer messageConsumer) { + + return new DomainEventDispatcher("orderHistoryServiceEvents", + orderHistoryEventConsumer.domainEventHandlers(), messageConsumer); + } + + @Bean + public CustomerHistoryEventConsumer customerHistoryEventConsumer() { + return new CustomerHistoryEventConsumer(); + } + + @Bean("customerHistoryDomainEventDispatcher") + public DomainEventDispatcher customerHistoryDomainEventDispatcher(CustomerHistoryEventConsumer customerHistoryEventConsumer, + MessageConsumer messageConsumer) { + + return new DomainEventDispatcher("customerHistoryServiceEvents", + customerHistoryEventConsumer.domainEventHandlers(), messageConsumer); + } +} diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryViewMongoConfiguration.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryViewMongoConfiguration.java new file mode 100644 index 00000000..7ec12c9c --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryViewMongoConfiguration.java @@ -0,0 +1,16 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +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; + +@Configuration +@EnableMongoRepositories +public class OrderHistoryViewMongoConfiguration { + + @Bean + public OrderHistoryViewService orderHistoryViewService(CustomerViewRepository customerViewRepository, OrderViewRepository orderViewRepository, MongoTemplate mongoTemplate) { + return new OrderHistoryViewService(customerViewRepository, orderViewRepository, mongoTemplate); + } +} diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryViewService.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryViewService.java new file mode 100644 index 00000000..0231d746 --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderHistoryViewService.java @@ -0,0 +1,37 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; + +public class OrderHistoryViewService { + + private CustomerViewRepository customerViewRepository; + private OrderViewRepository orderViewRepository; + + @Autowired + public OrderHistoryViewService(CustomerViewRepository customerViewRepository, OrderViewRepository orderViewRepository, MongoTemplate mongoTemplate) { + this.customerViewRepository = customerViewRepository; + this.orderViewRepository = orderViewRepository; + } + + public void createCustomer(Long customerId, String customerName, Money creditLimit) { + customerViewRepository.addCustomer(customerId, customerName, creditLimit); + } + + public void addOrder(Long customerId, Long orderId, Money orderTotal) { + customerViewRepository.addOrder(customerId, orderId, orderTotal); + orderViewRepository.addOrder(orderId, orderTotal); + } + + public void approveOrder(Long customerId, Long orderId) { + customerViewRepository.updateOrderState(customerId, orderId, OrderState.APPROVED); + orderViewRepository.updateOrderState(orderId, OrderState.APPROVED); + } + + public void rejectOrder(Long customerId, Long orderId) { + customerViewRepository.updateOrderState(customerId, orderId, OrderState.REJECTED); + orderViewRepository.updateOrderState(orderId, OrderState.REJECTED); + } +} diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderViewRepository.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderViewRepository.java new file mode 100644 index 00000000..1dfcf3c9 --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderViewRepository.java @@ -0,0 +1,7 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.OrderView; +import org.springframework.data.mongodb.repository.MongoRepository; + +public interface OrderViewRepository extends MongoRepository, OrderViewRepositoryCustom { +} diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderViewRepositoryCustom.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderViewRepositoryCustom.java new file mode 100644 index 00000000..e3076ef7 --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderViewRepositoryCustom.java @@ -0,0 +1,9 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; + +public interface OrderViewRepositoryCustom { + void addOrder(Long orderId, Money orderTotal); + void updateOrderState(Long orderId, OrderState state); +} diff --git a/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderViewRepositoryImpl.java b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderViewRepositoryImpl.java new file mode 100644 index 00000000..12e94ee9 --- /dev/null +++ b/order-history-backend/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/backend/OrderViewRepositoryImpl.java @@ -0,0 +1,33 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.OrderView; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; + +import static org.springframework.data.mongodb.core.query.Criteria.where; + +public class OrderViewRepositoryImpl implements OrderViewRepositoryCustom { + + private MongoTemplate mongoTemplate; + + @Autowired + public OrderViewRepositoryImpl(MongoTemplate mongoTemplate) { + this.mongoTemplate = mongoTemplate; + } + + @Override + public void addOrder(Long orderId, Money orderTotal) { + mongoTemplate.upsert(new Query(where("id").is(orderId)), + new Update().set("orderTotal", orderTotal), OrderView.class); + } + + @Override + public void updateOrderState(Long orderId, OrderState state) { + mongoTemplate.updateFirst(new Query(where("id").is(orderId)), + new Update().set("state", state), OrderView.class); + } +} diff --git a/order-history-backend/src/main/resources/logback.xml b/order-history-backend/src/main/resources/logback.xml new file mode 100644 index 00000000..381ada99 --- /dev/null +++ b/order-history-backend/src/main/resources/logback.xml @@ -0,0 +1,25 @@ + + + + + + + + %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/order-history-backend/src/test/java/net/chrisrichardson/eventstore/examples/customersandorders/views/orderhistory/CustomerViewRepositoryIntegrationTest.java b/order-history-backend/src/test/java/net/chrisrichardson/eventstore/examples/customersandorders/views/orderhistory/CustomerViewRepositoryIntegrationTest.java new file mode 100644 index 00000000..d1e07ed9 --- /dev/null +++ b/order-history-backend/src/test/java/net/chrisrichardson/eventstore/examples/customersandorders/views/orderhistory/CustomerViewRepositoryIntegrationTest.java @@ -0,0 +1,39 @@ +package net.chrisrichardson.eventstore.examples.customersandorders.views.orderhistory; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.CustomerView; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend.CustomerViewRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.UUID; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = OrderHistoryViewServiceTestConfiguration.class, + webEnvironment = SpringBootTest.WebEnvironment.NONE) +public class CustomerViewRepositoryIntegrationTest { + + @Autowired + private CustomerViewRepository customerViewRepository; + + @Test + public void shouldCreateAndFindCustomer() { + + Long customerId = System.nanoTime(); + Money creditLimit = new Money(2000); + String customerName = "Fred"; + + customerViewRepository.addCustomer(customerId, customerName, creditLimit); + CustomerView customerView = customerViewRepository.findOne(customerId); + + assertEquals(customerId, customerView.getId()); + assertEquals(customerName, customerView.getName()); + assertEquals(0, customerView.getOrders().size()); + assertEquals(creditLimit, customerView.getCreditLimit()); + } +} diff --git a/order-history-backend/src/test/java/net/chrisrichardson/eventstore/examples/customersandorders/views/orderhistory/OrderHistoryViewServiceTest.java b/order-history-backend/src/test/java/net/chrisrichardson/eventstore/examples/customersandorders/views/orderhistory/OrderHistoryViewServiceTest.java new file mode 100644 index 00000000..6a5c2700 --- /dev/null +++ b/order-history-backend/src/test/java/net/chrisrichardson/eventstore/examples/customersandorders/views/orderhistory/OrderHistoryViewServiceTest.java @@ -0,0 +1,75 @@ +package net.chrisrichardson.eventstore.examples.customersandorders.views.orderhistory; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.CustomerView; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.OrderView; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend.CustomerViewRepository; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend.OrderHistoryViewService; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend.OrderViewRepository; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = OrderHistoryViewServiceTestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) +public class OrderHistoryViewServiceTest { + + @Autowired + private OrderHistoryViewService orderHistoryViewService; + + @Autowired + private CustomerViewRepository customerViewRepository; + + @Autowired + private OrderViewRepository orderViewRepository; + + @Test + public void shouldCreateCustomerAndOrdersEtc() { + Long customerId = System.nanoTime(); + Money creditLimit = new Money(2000); + String customerName = "Fred"; + + Long orderId1 = System.nanoTime(); + Money orderTotal1 = new Money(1234); + Long orderId2 = System.nanoTime(); + Money orderTotal2 = new Money(3000); + + orderHistoryViewService.createCustomer(customerId, customerName, creditLimit); + orderHistoryViewService.addOrder(customerId, orderId1, orderTotal1); + orderHistoryViewService.approveOrder(customerId, orderId1); + + orderHistoryViewService.addOrder(customerId, orderId2, orderTotal2); + orderHistoryViewService.rejectOrder(customerId, orderId2); + + CustomerView customerView = customerViewRepository.findOne(customerId); + + + assertEquals(2, customerView.getOrders().size()); + assertNotNull(customerView.getOrders().get(orderId1)); + assertNotNull(customerView.getOrders().get(orderId2)); + assertEquals(orderTotal1, customerView.getOrders().get(orderId1).getOrderTotal()); + assertEquals(OrderState.APPROVED, customerView.getOrders().get(orderId1).getState()); + + assertNotNull(customerView.getOrders().get(orderId2)); + assertEquals(orderTotal2, customerView.getOrders().get(orderId2).getOrderTotal()); + assertEquals(OrderState.REJECTED, customerView.getOrders().get(orderId2).getState()); + + OrderView orderView1 = orderViewRepository.findOne(orderId1); + assertEquals(orderTotal1, orderView1.getOrderTotal()); + assertEquals(OrderState.APPROVED, orderView1.getState()); + + OrderView orderView2 = orderViewRepository.findOne(orderId2); + assertEquals(orderTotal2, orderView2.getOrderTotal()); + assertEquals(OrderState.REJECTED, orderView2.getState()); + } + + +} \ No newline at end of file diff --git a/order-history-backend/src/test/java/net/chrisrichardson/eventstore/examples/customersandorders/views/orderhistory/OrderHistoryViewServiceTestConfiguration.java b/order-history-backend/src/test/java/net/chrisrichardson/eventstore/examples/customersandorders/views/orderhistory/OrderHistoryViewServiceTestConfiguration.java new file mode 100644 index 00000000..4161f53e --- /dev/null +++ b/order-history-backend/src/test/java/net/chrisrichardson/eventstore/examples/customersandorders/views/orderhistory/OrderHistoryViewServiceTestConfiguration.java @@ -0,0 +1,12 @@ +package net.chrisrichardson.eventstore.examples.customersandorders.views.orderhistory; + +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend.OrderHistoryViewBackendConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@EnableAutoConfiguration +@Import(OrderHistoryViewBackendConfiguration.class) +public class OrderHistoryViewServiceTestConfiguration { +} diff --git a/order-history-common/build.gradle b/order-history-common/build.gradle new file mode 100644 index 00000000..c1c67009 --- /dev/null +++ b/order-history-common/build.gradle @@ -0,0 +1,4 @@ +dependencies { + compile project(":common") + compile "org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion" +} diff --git a/order-history-common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/common/CustomerView.java b/order-history-common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/common/CustomerView.java new file mode 100644 index 00000000..bdda5f0c --- /dev/null +++ b/order-history-common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/common/CustomerView.java @@ -0,0 +1,48 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.common; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.util.HashMap; +import java.util.Map; + +@Document +public class CustomerView { + + @Id + private Long id; + + + private Map orders = new HashMap<>(); + private String name; + private Money creditLimit; + + public void setId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public Map getOrders() { + return orders; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setCreditLimit(Money creditLimit) { + this.creditLimit = creditLimit; + } + + public Money getCreditLimit() { + return creditLimit; + } +} diff --git a/order-history-common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/common/OrderInfo.java b/order-history-common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/common/OrderInfo.java new file mode 100644 index 00000000..4acbfbf1 --- /dev/null +++ b/order-history-common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/common/OrderInfo.java @@ -0,0 +1,37 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.common; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; + +public class OrderInfo { + + private OrderState state; + private Long orderId; + private Money orderTotal; + + + public OrderInfo() { + } + + public OrderInfo(Long orderId, Money orderTotal) { + this.orderId = orderId; + this.orderTotal = orderTotal; + this.state = OrderState.PENDING; + } + + public void approve() { + state = OrderState.APPROVED; + } + + public void reject() { + state = OrderState.REJECTED; + } + + public Money getOrderTotal() { + return orderTotal; + } + + public OrderState getState() { + return state; + } +} diff --git a/order-history-common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/common/OrderView.java b/order-history-common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/common/OrderView.java new file mode 100644 index 00000000..e66d3090 --- /dev/null +++ b/order-history-common/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistory/common/OrderView.java @@ -0,0 +1,34 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistory.common; + +import io.eventuate.examples.tram.ordersandcustomers.commondomain.Money; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document +public class OrderView { + + @Id + private Long id; + + private OrderState state; + private Money orderTotal; + + + public OrderView() { + } + + public OrderView(Long id, Money orderTotal) { + this.id = id; + this.orderTotal = orderTotal; + this.state = OrderState.PENDING; + } + + public Money getOrderTotal() { + return orderTotal; + } + + public OrderState getState() { + return state; + } +} diff --git a/order-history-view-service/Dockerfile b/order-history-view-service/Dockerfile new file mode 100644 index 00000000..672437a9 --- /dev/null +++ b/order-history-view-service/Dockerfile @@ -0,0 +1,3 @@ +FROM java:openjdk-8u91-jdk +CMD java ${JAVA_OPTS} -jar order-history-view-service-*.jar +COPY build/libs/order-history-view-service-*.jar . \ No newline at end of file diff --git a/order-history-view-service/build.gradle b/order-history-view-service/build.gradle new file mode 100644 index 00000000..9ce7e7ea --- /dev/null +++ b/order-history-view-service/build.gradle @@ -0,0 +1,19 @@ +buildscript { + repositories { + mavenCentral() + jcenter() + } + dependencies { + classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}" + } +} + +apply plugin: 'spring-boot' + +dependencies { + compile project(":order-history-backend") + compile project(":common-swagger") + + compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" + compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" +} diff --git a/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/OrderHistoryServiceMain.java b/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/OrderHistoryServiceMain.java new file mode 100644 index 00000000..478c9fb9 --- /dev/null +++ b/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/OrderHistoryServiceMain.java @@ -0,0 +1,16 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistoryviewservice; + +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend.OrderHistoryViewBackendConfiguration; +import io.eventuate.examples.tram.ordersandcustomers.orderhistoryviewservice.web.OrderHistoryViewWebConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Import; + +@SpringBootApplication +@Import({OrderHistoryViewWebConfiguration.class, OrderHistoryViewBackendConfiguration.class}) +public class OrderHistoryServiceMain { + + public static void main(String[] args) { + SpringApplication.run(OrderHistoryServiceMain.class, args); + } +} diff --git a/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/web/OrderHistoryViewWebConfiguration.java b/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/web/OrderHistoryViewWebConfiguration.java new file mode 100644 index 00000000..dfd85244 --- /dev/null +++ b/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/web/OrderHistoryViewWebConfiguration.java @@ -0,0 +1,23 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistoryviewservice.web; + +import io.eventuate.examples.tram.ordersandcustomers.commonswagger.CommonSwaggerConfiguration; +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 +@ComponentScan +@Import(CommonSwaggerConfiguration.class) +public class OrderHistoryViewWebConfiguration { + + @Bean + public HttpMessageConverters customConverters() { + HttpMessageConverter additional = new MappingJackson2HttpMessageConverter(); + return new HttpMessageConverters(additional); + } + +} diff --git a/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/web/customers/CustomerOrderHistoryController.java b/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/web/customers/CustomerOrderHistoryController.java new file mode 100644 index 00000000..6e7c282a --- /dev/null +++ b/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/web/customers/CustomerOrderHistoryController.java @@ -0,0 +1,34 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistoryviewservice.web.customers; + +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.CustomerView; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend.CustomerViewRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CustomerOrderHistoryController { + + private final CustomerViewRepository customerViewRepository; + + @Autowired + public CustomerOrderHistoryController(CustomerViewRepository customerViewRepository) { + this.customerViewRepository = customerViewRepository; + } + + @RequestMapping(value="/customers/{customerId}", method= RequestMethod.GET) + public ResponseEntity getCustomer(@PathVariable Long customerId) { + CustomerView customer = customerViewRepository.findOne(customerId); + System.out.println("Found customer=" + customer + " for " + customerId); + if (customer == null) + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + else + return new ResponseEntity<>(customer, HttpStatus.OK); + } + + +} diff --git a/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/web/orders/OrderViewController.java b/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/web/orders/OrderViewController.java new file mode 100644 index 00000000..84594e83 --- /dev/null +++ b/order-history-view-service/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orderhistoryviewservice/web/orders/OrderViewController.java @@ -0,0 +1,36 @@ +package io.eventuate.examples.tram.ordersandcustomers.orderhistoryviewservice.web.orders; + +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.backend.OrderViewRepository; +import io.eventuate.examples.tram.ordersandcustomers.orderhistory.common.OrderView; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class OrderViewController { + + private OrderViewRepository orderViewRepository; + + @Autowired + public OrderViewController(OrderViewRepository orderViewRepository) { + this.orderViewRepository = orderViewRepository; + } + + + @RequestMapping(value="/orders/{orderId}", method= RequestMethod.GET) + public ResponseEntity getOrder(@PathVariable Long orderId) { + + OrderView ov = orderViewRepository.findOne(orderId); + if (ov == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } else { + return new ResponseEntity<>(ov, HttpStatus.OK); + } + } + + +} diff --git a/order-history-view-service/src/main/resources/logback.xml b/order-history-view-service/src/main/resources/logback.xml new file mode 100644 index 00000000..381ada99 --- /dev/null +++ b/order-history-view-service/src/main/resources/logback.xml @@ -0,0 +1,25 @@ + + + + + + + + %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/order-web-api/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/webapi/GetOrderResponse.java b/order-web-api/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/webapi/GetOrderResponse.java index cd847593..99ba0922 100644 --- a/order-web-api/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/webapi/GetOrderResponse.java +++ b/order-web-api/src/main/java/io/eventuate/examples/tram/ordersandcustomers/orders/webapi/GetOrderResponse.java @@ -1,7 +1,7 @@ package io.eventuate.examples.tram.ordersandcustomers.orders.webapi; -import io.eventuate.examples.tram.ordersandcustomers.orders.domain.OrderState; +import io.eventuate.examples.tram.ordersandcustomers.commondomain.OrderState; public class GetOrderResponse { private Long orderId; diff --git a/set-env.sh b/set-env.sh index 794e5687..0c5bfcf9 100644 --- a/set-env.sh +++ b/set-env.sh @@ -12,4 +12,5 @@ echo DOCKER_HOST_IP is $DOCKER_HOST_IP export EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS=$DOCKER_HOST_IP:9092 -export EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING=$DOCKER_HOST_IP:2181 \ No newline at end of file +export EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING=$DOCKER_HOST_IP:2181 +export SPRING_DATA_MONGODB_URI=mongodb://${DOCKER_HOST_IP?}/customers_orders diff --git a/settings.gradle b/settings.gradle index 6e6222f2..106c4d40 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,4 +6,8 @@ include 'customer-service' include 'order-backend' include 'order-web-api' include 'order-service' +include 'order-history-common' +include 'order-history-view-service' +include 'order-history-backend' include 'end-to-end-tests' +