Skip to content

Commit

Permalink
Cart entity and http resource; test failing
Browse files Browse the repository at this point in the history
  • Loading branch information
brian-wehrle-roche committed Dec 30, 2018
1 parent 0d392aa commit 6772b2c
Show file tree
Hide file tree
Showing 14 changed files with 320 additions and 174 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<module>vlingo-iddd-collaboration</module>
<module>vlingo-reactive-messaging-patterns</module>
<module>vlingo-eventjournal</module>
<module>vlingo-process-manager</module>
<module>vlingo-ecommerce</module>
</modules>

</project>
11 changes: 9 additions & 2 deletions vlingo-process-manager/pom.xml → vlingo-ecommerce/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>vlingo-examples-process-manager</artifactId>
<artifactId>vlingo-ecommerce</artifactId>

<dependencies>
<dependency>
<groupId>com.google.collections</groupId>
Expand All @@ -20,7 +21,7 @@
<dependency>
<groupId>io.vlingo</groupId>
<artifactId>vlingo-lattice</artifactId>
<version>0.7.4</version>
<version>0.7.6</version>
<scope>compile</scope>
</dependency>
<dependency>
Expand All @@ -35,6 +36,12 @@
<version>0.7.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
<scope>test</scope>
</dependency>
</dependencies>


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.vlingo.examples.ecommerce;

import io.vlingo.actors.Definition;
import io.vlingo.actors.World;
import io.vlingo.examples.ecommerce.infra.CartEventAdapter;
import io.vlingo.examples.ecommerce.infra.CartResource;
import io.vlingo.examples.ecommerce.infra.MockJournalListener;
import io.vlingo.examples.ecommerce.model.CartEntity;
import io.vlingo.examples.ecommerce.model.CartEvents;
import io.vlingo.http.resource.Configuration;
import io.vlingo.http.resource.Resources;
import io.vlingo.http.resource.Server;
import io.vlingo.lattice.model.sourcing.SourcedTypeRegistry;
import io.vlingo.symbio.store.journal.Journal;
import io.vlingo.symbio.store.journal.inmemory.InMemoryJournalActor;

public class Bootstrap {
private static Bootstrap instance;
private final Server server;

private Bootstrap() {
World world = World.startWithDefaults("cartservice");

MockJournalListener listener = new MockJournalListener();
Journal<String> journal = world.actorFor(Definition.has(InMemoryJournalActor.class, Definition.parameters(listener)), Journal.class);
journal.registerAdapter(CartEvents.CreatedEvent.class, new CartEventAdapter());

SourcedTypeRegistry registry = new SourcedTypeRegistry(world);
registry.register(new SourcedTypeRegistry.Info<>(journal, CartEntity.class, CartEntity.class.getSimpleName()));

final CartResource cartResource = new CartResource(world);

final Resources resources = Resources.are(cartResource.routes());

this.server = Server.startWith(world.stage(), resources, 8081, Configuration.Sizing.define(), Configuration.Timing.define());

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (instance != null) {
instance.server.stop();

System.out.println("\n");
System.out.println("=======================");
System.out.println("Stopping frontservice.");
System.out.println("=======================");
pause();
}
}));
}

public static final Bootstrap instance() {
if (instance == null) instance = new Bootstrap();
return instance;
}

public static void main(final String[] args) throws Exception {
System.out.println("=======================");
System.out.println("service: started.");
System.out.println("=======================");
Bootstrap.instance();
}

public void stop() throws InterruptedException {
instance.server.shutDown().wait(1000);
}

private void pause() {
try {
Thread.sleep(1000L);
} catch (Exception e) {
// ignore
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.vlingo.examples.ecommerce.infra;

import io.vlingo.common.serialization.JsonSerialization;
import io.vlingo.examples.ecommerce.model.CartEvents;
import io.vlingo.symbio.Entry;
import io.vlingo.symbio.EntryAdapter;
import io.vlingo.symbio.Metadata;

public class CartEventAdapter implements EntryAdapter<CartEvents.CreatedEvent,Entry.TextEntry> {

@Override
public CartEvents.CreatedEvent fromEntry(final Entry.TextEntry entry) {
return JsonSerialization.deserialized(entry.entryData, CartEvents.CreatedEvent.class);
}

@Override
public Entry.TextEntry toEntry(final CartEvents.CreatedEvent source) {
final String serialization = JsonSerialization.serialized(source);
return new Entry.TextEntry(CartEvents.CreatedEvent.class, 1, serialization, Metadata.nullMetadata());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package io.vlingo.examples.ecommerce.infra;

import io.vlingo.actors.*;
import io.vlingo.common.Completes;
import io.vlingo.examples.ecommerce.model.Cart;
import io.vlingo.examples.ecommerce.model.UserId;
import io.vlingo.examples.ecommerce.model.ProductId;
import io.vlingo.examples.ecommerce.model.CartEntity;
import io.vlingo.http.Response;
import io.vlingo.http.resource.Resource;

import static io.vlingo.common.serialization.JsonSerialization.serialized;
import static io.vlingo.http.Response.Status.*;
import static io.vlingo.http.ResponseHeader.*;
import static io.vlingo.http.resource.ResourceBuilder.*;

public class CartResource {
public static final String ROOT_URL = "/cart";
private final AddressFactory addressFactory;
private final Stage stage;

public CartResource(final World world) {
this.addressFactory = world.addressFactory();
this.stage = world.stage();
}

public Completes<Response> create(final UserId userId) {
// Check if already exists and then return 404?
final Address cartAddress = addressFactory.uniquePrefixedWith("sc-");

stage.actorFor(Definition.has(CartEntity.class,
Definition.parameters(cartAddress.idString(), userId)),
Cart.class,
cartAddress);

return Completes.withSuccess(
Response.of(Created,
headers(of(Location, location(cartAddress.idString()))),
""));
}

public Completes<Response> queryCart(String cartId) {
return stage.actorOf(addressFactory.from(cartId), CartEntity.class)
.andThenTo(cart -> Completes.withSuccess(Response.of(Ok, "")))
.otherwise(noUser -> Response.of(NotFound, location(cartId)));
}

private String doChangeItem(CartEntity entity, String idOfProduct, CartItemChange change) {
ProductId productId = ProductId.fromId(idOfProduct);
if (change.isAdd())
entity.addItem(productId);
else
entity.removeItem(productId);

return serialized(entity.queryCart());
}

public Completes<Response> changeCart(String cartId, String productId, CartItemChange change) {
return stage.actorOf(addressFactory.from(cartId), CartEntity.class)
.andThenTo(cart -> Completes.withSuccess(Response.of(Ok, doChangeItem(cart, productId, change))))
.otherwise(noUser -> Response.of(NotFound, location(cartId)));
}


private String location(final String shoppingCartId) {
return ROOT_URL + "/" + shoppingCartId;
}


public static class CartItemChange {

public CartItemChange(String operation) {
this.operation = operation;
}

public final String operation;

public boolean isAdd() {
return operation.equals("add");
}
}

public Resource routes() {

return resource("Cart resource fluent api",
post("/cart")
.body(UserId.class)
.handle(this::create),
patch("/cart/{cartId}/{productId}")
.param(String.class)
.param(String.class)
.body(CartItemChange.class)
.handle(this::changeCart),
get("/cart/{cartId}")
.param(String.class)
.handle(this::queryCart));
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.vlingo.examples.ecommerce.infra;

import io.vlingo.symbio.Entry;
import io.vlingo.symbio.State;
import io.vlingo.symbio.store.journal.JournalListener;

import java.util.ArrayList;
import java.util.List;

public final class MockJournalListener implements JournalListener<String> {
public List<Entry<String>> entries = new ArrayList<>();

@Override
public void appended(Entry<String> entry) {
this.entries.add(entry);
}

@Override
public void appendedWith(Entry<String> entry, State<String> snapshot) {
this.entries.add(entry);
}

@Override
public void appendedAll(List<Entry<String>> entries) {
this.entries.addAll(entries);
}

@Override
public void appendedAllWith(List<Entry<String>> entries, State<String> snapshot) {
this.entries.addAll(entries);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package io.vlingo.examples.processmanager.choreography;
package io.vlingo.examples.ecommerce.model;

import java.util.List;



public interface ShoppingCart {
public interface Cart {

void addItem(ProductId productId);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.vlingo.examples.processmanager.choreography;
package io.vlingo.examples.ecommerce.model;

import io.vlingo.lattice.model.sourcing.EventSourced;

Expand All @@ -8,40 +8,39 @@
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

import static io.vlingo.examples.processmanager.choreography.ShoppingCartEvents.*;

public class ShoppingCartEntity extends EventSourced implements ShoppingCart {
public class CartEntity extends EventSourced implements Cart {

private State state;

public ShoppingCartEntity(final String shoppingCartId, final UserId userId) {
apply(CreatedEvent.forUser(shoppingCartId, userId));
public CartEntity(final String shoppingCartId, final UserId userId) {
super("cartStream");
apply(CartEvents.CreatedEvent.forUser(shoppingCartId, userId));
}


static {
BiConsumer<ShoppingCartEntity, CreatedEvent> applyCartCreated = ShoppingCartEntity::applyCartCreated;
EventSourced.registerConsumer(ShoppingCartEntity.class, CreatedEvent.class, applyCartCreated);
BiConsumer<CartEntity, CartEvents.CreatedEvent> applyCartCreated = CartEntity::applyCartCreated;
EventSourced.registerConsumer(CartEntity.class, CartEvents.CreatedEvent.class, applyCartCreated);

BiConsumer<ShoppingCartEntity, ProductAddedEvent> applyItemAdded = ShoppingCartEntity::applyItemAdded;
EventSourced.registerConsumer(ShoppingCartEntity.class, ProductAddedEvent.class, applyItemAdded);
BiConsumer<CartEntity, CartEvents.ProductAddedEvent> applyItemAdded = CartEntity::applyItemAdded;
EventSourced.registerConsumer(CartEntity.class, CartEvents.ProductAddedEvent.class, applyItemAdded);

BiConsumer<ShoppingCartEntity, ProductRemovedEvent> applyItemRemoved = ShoppingCartEntity::applyItemRemoved;
EventSourced.registerConsumer(ShoppingCartEntity.class, ProductAddedEvent.class, applyItemRemoved);
BiConsumer<CartEntity, CartEvents.ProductRemovedEvent> applyItemRemoved = CartEntity::applyItemRemoved;
EventSourced.registerConsumer(CartEntity.class, CartEvents.ProductAddedEvent.class, applyItemRemoved);

BiConsumer<ShoppingCartEntity, AllItemsRemovedEvent> applyRemoveAll = ShoppingCartEntity::applyAllItemsRemoved;
EventSourced.registerConsumer(ShoppingCartEntity.class, AllItemsRemovedEvent.class, applyRemoveAll);
BiConsumer<CartEntity, CartEvents.AllItemsRemovedEvent> applyRemoveAll = CartEntity::applyAllItemsRemoved;
EventSourced.registerConsumer(CartEntity.class, CartEvents.AllItemsRemovedEvent.class, applyRemoveAll);
}

@Override
public void addItem(ProductId productId) {
apply(ProductAddedEvent.with(state.userId, productId));
apply(CartEvents.ProductAddedEvent.with(state.userId, productId));
}

@Override
public void removeItem(ProductId productId) {
if (state.basketProductsById.containsKey(productId)) {
apply(ProductRemovedEvent.with(state.userId, productId));
apply(CartEvents.ProductRemovedEvent.with(state.userId, productId));
}
}

Expand All @@ -54,7 +53,7 @@ public List<CartItem> queryCart() {

@Override
public void removeAllItems() {
apply(AllItemsRemovedEvent.with(state.userId));
apply(CartEvents.AllItemsRemovedEvent.with(state.userId));
}

private static class State {
Expand Down Expand Up @@ -98,19 +97,19 @@ State productRemoved(ProductId removedProductId) {
}


private void applyCartCreated(final CreatedEvent e) {
private void applyCartCreated(final CartEvents.CreatedEvent e) {
state = State.created(e.shoppingCartId, e.userId);
}

private void applyItemAdded(final ProductAddedEvent e) {
private void applyItemAdded(final CartEvents.ProductAddedEvent e) {
state = state.productAdded(e.productId);
}

private void applyItemRemoved(final ProductRemovedEvent e) {
private void applyItemRemoved(final CartEvents.ProductRemovedEvent e) {
state = state.productRemoved(e.productId);
}

private void applyAllItemsRemoved(final AllItemsRemovedEvent e) {
private void applyAllItemsRemoved(final CartEvents.AllItemsRemovedEvent e) {
state = state.removeAllItems();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.vlingo.examples.processmanager.choreography;
package io.vlingo.examples.ecommerce.model;

import io.vlingo.lattice.model.DomainEvent;

public class ShoppingCartEvents {
public class CartEvents {

public static class CreatedEvent extends DomainEvent {

Expand Down

0 comments on commit 6772b2c

Please sign in to comment.