Skip to content

Commit

Permalink
feat: component client samples update
Browse files Browse the repository at this point in the history
  • Loading branch information
aludwiko committed Jun 27, 2023
1 parent 28fa345 commit 87e7e7d
Show file tree
Hide file tree
Showing 16 changed files with 150 additions and 114 deletions.
8 changes: 4 additions & 4 deletions docs/src/modules/java/pages/actions-as-controller.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ request and only conditionally forward the request to the entity if the verifica
----
include::example$java-spring-valueentity-shopping-cart/src/main/java/com/example/api/ShoppingCartController.java[tag=forward]
----
<1> `KalixClient` is injected on the constructor. It will be used to build calls to the underlining Entity.
<1> `ComponentClient` is injected on the constructor. It will be used to build calls to the underlining Entity.
<2> Expose the command handler as a `POST` endpoint at specified path.
<3> Check if the added item is carrots.
<4> If it is "carrots" immediately return an error, disallowing adding the item.
<5> For allowed requests, use `kalixClient` to get a deferred call to the entity.
<5> For allowed requests, use `componentClient` to get a deferred call to the entity.
<6> The `deferredCall` is then used with `effects().forward()` to forward the request to the entity.

NOTE: You might be wondering what the `kalixClient` is about. For now, think of it as a lightweight HTTP client allowing you to reach out to other Kalix services. All details can be found at xref:call-another-service.adoc[] chapter.
NOTE: You might be wondering what the `componentClient` is about. For now, think of it as a lightweight, type safe, HTTP client allowing you to reach out to other Kalix services. All details can be found at xref:call-another-service.adoc[] chapter.

== Transform Request and Response to Another Component

Expand All @@ -59,7 +59,7 @@ This example implements an `initializeCart` command for the controller Action wh
include::example$java-spring-valueentity-shopping-cart/src/main/java/com/example/api/ShoppingCartController.java[tag=initialize]
----
<1> Generate a new UUID.
<2> Use the `kalixClient` to create a call to endpoint `create` on the shopping cart - note the use of the full path, empty body and the expected reply type `ShoppingCartDTO`.
<2> Use the `componentClient` to create a call to endpoint `create` on the shopping cart.
<3> `execute()` on the deferred call immediately triggers a call and returns a `CompletionStage` for the response.
<4> Once the call succeeds or fails the `CompletionStage` is completed or failed, we can transform the result from `CompletionStage<Empty>`.
to `CompletionStage<Effect<String>>` using `handle`.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/modules/java/pages/timers.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ include::example$java-spring-reliable-timers/src/main/java/com/example/actions/O
<3> Order id is used to generate a unique name for the timer.
<4> Set the delay you want for the timer to trigger.
<5> Scheduled call to `OrderAction.expire` method. We will cover it in a while.
<6> Pass on the request for the Order entity specifying the `orderId` using the `KalixClient`.
<6> Pass on the request for the Order entity using the `ComponentClient`.
<7> Finally, you build an `asyncReply` by composing the `timerRegistration` CompletionStage with a call to execute the request and place the order.

In a nutshell, you first requested Kalix to register a timer. When it completes, you know that the timer is persisted and will run at the specified time. You then proceed by placing the order.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
package com.example.actions;

import com.example.Counter;
import kalix.javasdk.action.Action;
import kalix.javasdk.annotations.Subscribe;
import kalix.spring.KalixClient;
import kalix.javasdk.client.ComponentClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Subscribe.Topic(value = "counter-commands", ignoreUnknown = true)
public class CounterCommandFromTopicAction extends Action {

public record IncreaseCounter(String counterId, int value) { }
public record IncreaseCounter(String counterId, int value) {
}

public record MultiplyCounter(String counterId, int value) {
}

private KalixClient kalixClient;
private ComponentClient componentClient;

public CounterCommandFromTopicAction(KalixClient kalixClient) {
this.kalixClient = kalixClient;
public CounterCommandFromTopicAction(ComponentClient componentClient) {
this.componentClient = componentClient;
}

private Logger logger = LoggerFactory.getLogger(CounterCommandFromTopicAction.class);

public Effect<String> onValueIncreased(IncreaseCounter increase) {
logger.info("Received increase command: " + increase.toString());
var deferredCall = kalixClient.post("/counter/"+ increase.counterId + "/increase/" + increase.value, String.class);
var deferredCall = componentClient.forEventSourcedEntity(increase.counterId).call(Counter::increase).params(increase.value);
return effects().forward(deferredCall);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.example.fibonacci;

import com.example.Main;
import com.google.protobuf.any.Any;
import kalix.javasdk.DeferredCall;
import kalix.javasdk.client.ComponentClient;
import kalix.spring.testkit.KalixIntegrationTestKitSupport;

import org.junit.jupiter.api.Assertions;
Expand All @@ -15,6 +18,9 @@

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import static java.time.temporal.ChronoUnit.SECONDS;

Expand All @@ -25,16 +31,18 @@ public class FibonacciActionIntegrationTest extends KalixIntegrationTestKitSuppo
@Autowired
private WebClient webClient;

@Autowired
private ComponentClient componentClient;
private Duration timeout = Duration.of(5, SECONDS);

@Test
public void calculateNextNumber() {

Mono<Number> response =
webClient.get()
.uri("/fibonacci/5/next")
.retrieve().bodyToMono(Number.class);
Number response = execute(componentClient.forAction()
.call(FibonacciAction::getNumber)
.params(5L));

long next = response.block(Duration.of(5, SECONDS)).value();
Assertions.assertEquals(8, next);
Assertions.assertEquals(8, response.value());

}

Expand Down Expand Up @@ -69,4 +77,12 @@ public void wrongNumberReturnsError() {
Assertions.assertTrue(bodyErrorMessage.contains("Input number is not a Fibonacci number"));
}
}

private <T> T execute(DeferredCall<Any, T> deferredCall) {
try {
return deferredCall.execute().toCompletableFuture().get(timeout.toMillis(), TimeUnit.MILLISECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private long nextFib(long num) { // <2>
}

@GetMapping("/{number}/next")
public Effect<Number> nextNumber(@PathVariable Long number) { // <3>
public Effect<Number> getNumber(@PathVariable Long number) { // <3>
return nextNumber(new Number(number));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import io.grpc.Status;
import kalix.javasdk.action.Action;
import kalix.javasdk.action.ActionCreationContext;
import kalix.spring.KalixClient;
import kalix.javasdk.client.ComponentClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
Expand All @@ -13,14 +13,14 @@ public class LimitedFibonacciAction extends Action {

private static final Logger logger = LoggerFactory.getLogger(LimitedFibonacciAction.class);

private KalixClient kalixClient;
private ComponentClient componentClient;

private ActionCreationContext ctx;


public LimitedFibonacciAction(ActionCreationContext ctx, KalixClient kalixClient) {
public LimitedFibonacciAction(ActionCreationContext ctx, ComponentClient componentClient) {
this.ctx = ctx;
this.kalixClient = kalixClient;
this.componentClient = componentClient;
}

@GetMapping("/{number}/next")
Expand All @@ -29,7 +29,7 @@ public Effect<Number> nextNumber(@PathVariable Long number) {
return effects().error("Only numbers between 0 and 10k are allowed", Status.Code.INVALID_ARGUMENT);
} else {
logger.info("Executing GET call to real /fibonacci = " + number);
var serviceCall = kalixClient.get("/fibonacci/"+number+"/next", Number.class);
var serviceCall = componentClient.forAction().call(FibonacciAction::getNumber).params(number);

return effects().forward(serviceCall);
}
Expand All @@ -41,7 +41,7 @@ public Effect<Number> nextNumber(@RequestBody Number number) {
return effects().error("Only numbers between 0 and 10k are allowed", Status.Code.INVALID_ARGUMENT);
} else {
logger.info("Executing POST call to real /fibonacci = " + number.value());
var serviceCall = kalixClient.post("/fibonacci/next", number, Number.class);
var serviceCall = componentClient.forAction().call(FibonacciAction::nextNumber).params(number);

return effects().forward(serviceCall);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ public class FibonacciActionTest {
@Test
public void testNextFib() {
ActionTestkit<FibonacciAction> testkit = ActionTestkit.of(FibonacciAction::new); // <1>
ActionResult<Number> result = testkit.call(a -> a.nextNumber(3L)); // <2>
ActionResult<Number> result = testkit.call(a -> a.getNumber(3L)); // <2>
assertTrue(result.isReply());
assertEquals(5L, result.getReply().value());
}

@Test
public void testNextFibError() {
ActionTestkit<FibonacciAction> testkit = ActionTestkit.of(FibonacciAction::new); // <1>
ActionResult<Number> result = testkit.call(a -> a.nextNumber(4L)); // <2>
ActionResult<Number> result = testkit.call(a -> a.getNumber(4L)); // <2>
assertTrue(result.isError());
assertTrue(result.getError().startsWith("Input number is not a Fibonacci number"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<logger name="io.grpc.netty" level="INFO"/>
<logger name="org.testcontainers" level="INFO"/>

<root level="DEBUG">
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.example.actions;

import akka.Done;
import com.example.domain.OrderEntity;
import com.example.domain.OrderRequest;
import com.example.domain.Order;
import kalix.javasdk.DeferredCallResponseException;
import kalix.javasdk.StatusCode.ErrorCode;
import kalix.javasdk.action.Action;
import kalix.javasdk.action.ActionCreationContext;
import kalix.spring.KalixClient;
import kalix.javasdk.client.ComponentClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
Expand All @@ -24,14 +25,14 @@ public class OrderAction extends Action {

private Logger logger = LoggerFactory.getLogger(getClass());

private KalixClient kalixClient;
private ComponentClient componentClient;

private ActionCreationContext ctx;


public OrderAction(ActionCreationContext creationContext, KalixClient kalixClient) {
public OrderAction(ActionCreationContext creationContext, ComponentClient componentClient) {
this.ctx = creationContext;
this.kalixClient = kalixClient;
this.componentClient = componentClient;
}

// tag::place-order[]
Expand All @@ -46,9 +47,9 @@ public Effect<Order> placeOrder(@RequestBody OrderRequest orderRequest) {

CompletionStage<Done> timerRegistration = // <2>
timers().startSingleTimer(
timerName(orderId), // <3>
Duration.ofSeconds(10), // <4>
kalixClient.post("/orders/expire/"+orderId, "", String.class) // <5>
timerName(orderId), // <3>
Duration.ofSeconds(10), // <4>
componentClient.forAction().call(OrderAction::expire).params(orderId) // <5>
);

// end::place-order[]
Expand All @@ -59,7 +60,7 @@ public Effect<Order> placeOrder(@RequestBody OrderRequest orderRequest) {
orderId);
// tag::place-order[]

var request = kalixClient.put("/order/"+orderId+"/place", orderRequest, Order.class); // <6>
var request = componentClient.forValueEntity(orderId).call(OrderEntity::placeOrder).params(orderRequest); // <6>
return effects().asyncReply( // <7>
timerRegistration
.thenCompose(done -> request.execute())
Expand All @@ -74,7 +75,7 @@ public Effect<Order> placeOrder(@RequestBody OrderRequest orderRequest) {
@PostMapping("/expire/{orderId}")
public Effect<String> expire(@PathVariable String orderId) {
logger.info("Expiring order '{}'", orderId);
var cancelRequest = kalixClient.post("/order/"+orderId+"/cancel", "", String.class);
var cancelRequest = componentClient.forValueEntity(orderId).call(OrderEntity::cancel);

CompletionStage<String> reply =
cancelRequest
Expand Down Expand Up @@ -103,8 +104,8 @@ public Effect<String> confirm(@PathVariable String orderId) {
logger.info("Confirming order '{}'", orderId);

CompletionStage<String> reply =
kalixClient.post("/order/"+orderId+"/confirm", "", String.class) // <1>
.execute()
componentClient.forValueEntity(orderId).call(OrderEntity::confirm) // <1>
.execute()
.thenCompose(req -> timers().cancel(timerName(orderId))) // <2>
.thenApply(done -> "Ok");

Expand All @@ -116,7 +117,7 @@ public Effect<String> cancel(@PathVariable String orderId) {
logger.info("Cancelling order '{}'", orderId);

CompletionStage<String> reply =
kalixClient.post("/order/"+orderId+"/cancel", "", String.class)
componentClient.forValueEntity(orderId).call(OrderEntity::cancel)
.execute()
.thenCompose(req -> timers().cancel(timerName(orderId)))
.thenApply(done -> "Ok");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,40 +29,42 @@ public Order emptyState() {
}

@PutMapping("/place")
public Effect<Order> placeOrder(@PathVariable String id,
@RequestBody OrderRequest orderRequest) { // <1>
logger.info("Placing orderId={} request={}", id, orderRequest);
public Effect<Order> placeOrder(@RequestBody OrderRequest orderRequest) { // <1>
var orderId = commandContext().entityId();
logger.info("Placing orderId={} request={}", orderId, orderRequest);
var newOrder = new Order(
id,
orderId,
false,
true, // <2>
orderRequest.item(),
orderRequest.quantity());
return effects()
.updateState(newOrder)
.thenReply(newOrder);
.updateState(newOrder)
.thenReply(newOrder);
}

@PostMapping("/confirm")
public Effect<String> confirm(@PathVariable String id) {
logger.info("Confirming orderId={}", id);
public Effect<String> confirm() {
var orderId = commandContext().entityId();
logger.info("Confirming orderId={}", orderId);
if (currentState().placed()) { // <3>
return effects()
.updateState(currentState().confirm())
.thenReply("Ok");
} else {
return effects().error(
"No order found for '" + id + "'",
"No order found for '" + orderId + "'",
ErrorCode.NOT_FOUND); // <4>
}
}

@PostMapping("/cancel")
public Effect<String> cancel(@PathVariable String id) {
logger.info("Cancelling orderId={} currentState={}", id, currentState());
public Effect<String> cancel() {
var orderId = commandContext().entityId();
logger.info("Cancelling orderId={} currentState={}", orderId, currentState());
if (!currentState().placed()) {
return effects().error(
"No order found for " + id,
"No order found for " + orderId,
ErrorCode.NOT_FOUND); // <5>
} else if (currentState().confirmed()) {
return effects().error(
Expand Down
Loading

0 comments on commit 87e7e7d

Please sign in to comment.