diff --git a/docs/src/modules/java/pages/actions-as-controller.adoc b/docs/src/modules/java/pages/actions-as-controller.adoc index 10a549bcdb..d748f1c9b8 100644 --- a/docs/src/modules/java/pages/actions-as-controller.adoc +++ b/docs/src/modules/java/pages/actions-as-controller.adoc @@ -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 @@ -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`. to `CompletionStage>` using `handle`. diff --git a/docs/src/modules/java/pages/timers.adoc b/docs/src/modules/java/pages/timers.adoc index 18171d71d5..2209e8a620 100644 --- a/docs/src/modules/java/pages/timers.adoc +++ b/docs/src/modules/java/pages/timers.adoc @@ -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. diff --git a/samples/java-spring-eventsourced-counter/src/main/java/com/example/actions/CounterCommandFromTopicAction.java b/samples/java-spring-eventsourced-counter/src/main/java/com/example/actions/CounterCommandFromTopicAction.java index 2a0cc4f4e3..c2e61903e1 100644 --- a/samples/java-spring-eventsourced-counter/src/main/java/com/example/actions/CounterCommandFromTopicAction.java +++ b/samples/java-spring-eventsourced-counter/src/main/java/com/example/actions/CounterCommandFromTopicAction.java @@ -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 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); } - } diff --git a/samples/java-spring-fibonacci-action/src/it/java/com/example/fibonacci/FibonacciActionIntegrationTest.java b/samples/java-spring-fibonacci-action/src/it/java/com/example/fibonacci/FibonacciActionIntegrationTest.java index fb2d47bcb2..7cadcf17d6 100644 --- a/samples/java-spring-fibonacci-action/src/it/java/com/example/fibonacci/FibonacciActionIntegrationTest.java +++ b/samples/java-spring-fibonacci-action/src/it/java/com/example/fibonacci/FibonacciActionIntegrationTest.java @@ -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; @@ -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; @@ -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 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()); } @@ -69,4 +77,12 @@ public void wrongNumberReturnsError() { Assertions.assertTrue(bodyErrorMessage.contains("Input number is not a Fibonacci number")); } } + + private T execute(DeferredCall deferredCall) { + try { + return deferredCall.execute().toCompletableFuture().get(timeout.toMillis(), TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + throw new RuntimeException(e); + } + } } diff --git a/samples/java-spring-fibonacci-action/src/main/java/com/example/fibonacci/FibonacciAction.java b/samples/java-spring-fibonacci-action/src/main/java/com/example/fibonacci/FibonacciAction.java index ceb25e55ce..22aa60e8b8 100644 --- a/samples/java-spring-fibonacci-action/src/main/java/com/example/fibonacci/FibonacciAction.java +++ b/samples/java-spring-fibonacci-action/src/main/java/com/example/fibonacci/FibonacciAction.java @@ -31,7 +31,7 @@ private long nextFib(long num) { // <2> } @GetMapping("/{number}/next") - public Effect nextNumber(@PathVariable Long number) { // <3> + public Effect getNumber(@PathVariable Long number) { // <3> return nextNumber(new Number(number)); } diff --git a/samples/java-spring-fibonacci-action/src/main/java/com/example/fibonacci/LimitedFibonacciAction.java b/samples/java-spring-fibonacci-action/src/main/java/com/example/fibonacci/LimitedFibonacciAction.java index d0aa492b8f..61051f9709 100644 --- a/samples/java-spring-fibonacci-action/src/main/java/com/example/fibonacci/LimitedFibonacciAction.java +++ b/samples/java-spring-fibonacci-action/src/main/java/com/example/fibonacci/LimitedFibonacciAction.java @@ -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.*; @@ -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") @@ -29,7 +29,7 @@ public Effect 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); } @@ -41,7 +41,7 @@ public Effect 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); } diff --git a/samples/java-spring-fibonacci-action/src/test/java/com/example/fibonacci/FibonacciActionTest.java b/samples/java-spring-fibonacci-action/src/test/java/com/example/fibonacci/FibonacciActionTest.java index ade44e8588..b9b80c73f3 100644 --- a/samples/java-spring-fibonacci-action/src/test/java/com/example/fibonacci/FibonacciActionTest.java +++ b/samples/java-spring-fibonacci-action/src/test/java/com/example/fibonacci/FibonacciActionTest.java @@ -16,7 +16,7 @@ public class FibonacciActionTest { @Test public void testNextFib() { ActionTestkit testkit = ActionTestkit.of(FibonacciAction::new); // <1> - ActionResult result = testkit.call(a -> a.nextNumber(3L)); // <2> + ActionResult result = testkit.call(a -> a.getNumber(3L)); // <2> assertTrue(result.isReply()); assertEquals(5L, result.getReply().value()); } @@ -24,7 +24,7 @@ public void testNextFib() { @Test public void testNextFibError() { ActionTestkit testkit = ActionTestkit.of(FibonacciAction::new); // <1> - ActionResult result = testkit.call(a -> a.nextNumber(4L)); // <2> + ActionResult result = testkit.call(a -> a.getNumber(4L)); // <2> assertTrue(result.isError()); assertTrue(result.getError().startsWith("Input number is not a Fibonacci number")); } diff --git a/samples/java-spring-reliable-timers/src/it/resources/logback-test.xml b/samples/java-spring-reliable-timers/src/it/resources/logback-test.xml index 2abce0facb..d25577ff1c 100644 --- a/samples/java-spring-reliable-timers/src/it/resources/logback-test.xml +++ b/samples/java-spring-reliable-timers/src/it/resources/logback-test.xml @@ -11,7 +11,7 @@ - + diff --git a/samples/java-spring-reliable-timers/src/main/java/com/example/actions/OrderAction.java b/samples/java-spring-reliable-timers/src/main/java/com/example/actions/OrderAction.java index c529fd59da..a5b6bd333a 100644 --- a/samples/java-spring-reliable-timers/src/main/java/com/example/actions/OrderAction.java +++ b/samples/java-spring-reliable-timers/src/main/java/com/example/actions/OrderAction.java @@ -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.*; @@ -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[] @@ -46,9 +47,9 @@ public Effect placeOrder(@RequestBody OrderRequest orderRequest) { CompletionStage 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[] @@ -59,7 +60,7 @@ public Effect 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()) @@ -74,7 +75,7 @@ public Effect placeOrder(@RequestBody OrderRequest orderRequest) { @PostMapping("/expire/{orderId}") public Effect 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 reply = cancelRequest @@ -103,8 +104,8 @@ public Effect confirm(@PathVariable String orderId) { logger.info("Confirming order '{}'", orderId); CompletionStage 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"); @@ -116,7 +117,7 @@ public Effect cancel(@PathVariable String orderId) { logger.info("Cancelling order '{}'", orderId); CompletionStage reply = - kalixClient.post("/order/"+orderId+"/cancel", "", String.class) + componentClient.forValueEntity(orderId).call(OrderEntity::cancel) .execute() .thenCompose(req -> timers().cancel(timerName(orderId))) .thenApply(done -> "Ok"); diff --git a/samples/java-spring-reliable-timers/src/main/java/com/example/domain/OrderEntity.java b/samples/java-spring-reliable-timers/src/main/java/com/example/domain/OrderEntity.java index 8ee89da6ee..84c71f026a 100644 --- a/samples/java-spring-reliable-timers/src/main/java/com/example/domain/OrderEntity.java +++ b/samples/java-spring-reliable-timers/src/main/java/com/example/domain/OrderEntity.java @@ -29,40 +29,42 @@ public Order emptyState() { } @PutMapping("/place") - public Effect placeOrder(@PathVariable String id, - @RequestBody OrderRequest orderRequest) { // <1> - logger.info("Placing orderId={} request={}", id, orderRequest); + public Effect 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 confirm(@PathVariable String id) { - logger.info("Confirming orderId={}", id); + public Effect 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 cancel(@PathVariable String id) { - logger.info("Cancelling orderId={} currentState={}", id, currentState()); + public Effect 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( diff --git a/samples/java-spring-transfer-workflow-compensation/src/main/java/com/example/transfer/TransferWorkflow.java b/samples/java-spring-transfer-workflow-compensation/src/main/java/com/example/transfer/TransferWorkflow.java index db093f252d..61644158ab 100644 --- a/samples/java-spring-transfer-workflow-compensation/src/main/java/com/example/transfer/TransferWorkflow.java +++ b/samples/java-spring-transfer-workflow-compensation/src/main/java/com/example/transfer/TransferWorkflow.java @@ -1,6 +1,7 @@ package com.example.transfer; import com.example.transfer.TransferState.Transfer; +import com.example.wallet.WalletEntity; import com.example.wallet.WalletEntity.DepositResult; import com.example.wallet.WalletEntity.DepositResult.DepositFailed; import com.example.wallet.WalletEntity.DepositResult.DepositSucceed; @@ -9,8 +10,8 @@ import com.example.wallet.WalletEntity.WithdrawResult.WithdrawSucceed; import kalix.javasdk.annotations.Id; import kalix.javasdk.annotations.TypeId; +import kalix.javasdk.client.ComponentClient; import kalix.javasdk.workflow.Workflow; -import kalix.spring.KalixClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; @@ -43,10 +44,10 @@ public record Deposit(String to, int amount) { private static final Logger logger = LoggerFactory.getLogger(TransferWorkflow.class); - final private KalixClient kalixClient; + final private ComponentClient componentClient; - public TransferWorkflow(KalixClient kalixClient) { - this.kalixClient = kalixClient; + public TransferWorkflow(ComponentClient componentClient) { + this.componentClient = componentClient; } @Override @@ -55,8 +56,9 @@ public WorkflowDef definition() { step("withdraw") .call(Withdraw.class, cmd -> { logger.info("Running: " + cmd); - String withdrawUri = "/wallet/" + cmd.from() + "/withdraw/" + cmd.amount(); - return kalixClient.patch(withdrawUri, WithdrawResult.class); + return componentClient.forValueEntity(cmd.from) + .call(WalletEntity::withdraw) + .params(cmd.amount); }) .andThen(WithdrawResult.class, withdrawResult -> { if (withdrawResult instanceof WithdrawSucceed) { @@ -81,8 +83,9 @@ public WorkflowDef definition() { // end::compensation[] logger.info("Running: " + cmd); // tag::compensation[] - String depositUri = "/wallet/" + cmd.to() + "/deposit/" + cmd.amount(); - return kalixClient.patch(depositUri, DepositResult.class); // <1> + return componentClient.forValueEntity(cmd.to) + .call(WalletEntity::deposit) + .params(cmd.amount); // <1> }) .andThen(DepositResult.class, depositResult -> { if (depositResult instanceof DepositSucceed) { @@ -108,8 +111,9 @@ public WorkflowDef definition() { logger.info("Running withdraw compensation"); // tag::compensation[] var transfer = currentState().transfer(); - String refundUri = "/wallet/" + transfer.from() + "/deposit/" + transfer.amount(); - return kalixClient.patch(refundUri, DepositResult.class); + return componentClient.forValueEntity(transfer.from()) + .call(WalletEntity::deposit) + .params(transfer.amount()); }) .andThen(DepositResult.class, depositResult -> { if (depositResult instanceof DepositSucceed) { diff --git a/samples/java-spring-transfer-workflow-compensation/src/main/java/com/example/wallet/WalletEntity.java b/samples/java-spring-transfer-workflow-compensation/src/main/java/com/example/wallet/WalletEntity.java index 7b5597a10a..35d377f2a9 100644 --- a/samples/java-spring-transfer-workflow-compensation/src/main/java/com/example/wallet/WalletEntity.java +++ b/samples/java-spring-transfer-workflow-compensation/src/main/java/com/example/wallet/WalletEntity.java @@ -71,9 +71,9 @@ public Effect withdraw(@PathVariable int amount) { } @PatchMapping("/deposit/{amount}") // <3> - public Effect deposit(@PathVariable String id, @PathVariable int amount) { + public Effect deposit(@PathVariable int amount) { if (currentState() == null) { - return effects().reply(new DepositFailed("Wallet [" + id + "] not exists")); + return effects().reply(new DepositFailed("Wallet [" + commandContext().entityId() + "] not exists")); } else { Wallet updatedWallet = currentState().deposit(amount); // end::wallet[] diff --git a/samples/java-spring-transfer-workflow/src/main/java/com/example/transfer/TransferWorkflow.java b/samples/java-spring-transfer-workflow/src/main/java/com/example/transfer/TransferWorkflow.java index 7aff874a8a..ca8b5025b1 100644 --- a/samples/java-spring-transfer-workflow/src/main/java/com/example/transfer/TransferWorkflow.java +++ b/samples/java-spring-transfer-workflow/src/main/java/com/example/transfer/TransferWorkflow.java @@ -1,8 +1,9 @@ package com.example.transfer; import com.example.transfer.TransferState.Transfer; +import com.example.wallet.WalletEntity; +import kalix.javasdk.client.ComponentClient; import kalix.javasdk.workflow.Workflow; -import kalix.spring.KalixClient; import kalix.javasdk.annotations.Id; import kalix.javasdk.annotations.TypeId; import org.slf4j.Logger; @@ -36,10 +37,10 @@ public record Deposit(String to, int amount) { private static final Logger logger = LoggerFactory.getLogger(TransferWorkflow.class); - final private KalixClient kalixClient; + final private ComponentClient componentClient; - public TransferWorkflow(KalixClient kalixClient) { - this.kalixClient = kalixClient; + public TransferWorkflow(ComponentClient componentClient) { + this.componentClient = componentClient; } // tag::definition[] @@ -48,8 +49,9 @@ public WorkflowDef definition() { Step withdraw = step("withdraw") // <1> .call(Withdraw.class, cmd -> { - String withdrawUri = "/wallet/" + cmd.from() + "/withdraw/" + cmd.amount(); - return kalixClient.patch(withdrawUri, String.class); + return componentClient.forValueEntity(cmd.from) + .call(WalletEntity::withdraw) + .params(cmd.amount); }) // <2> .andThen(String.class, __ -> { Deposit depositInput = new Deposit(currentState().transfer().to(), currentState().transfer().amount()); @@ -61,8 +63,9 @@ public WorkflowDef definition() { Step deposit = step("deposit") // <1> .call(Deposit.class, cmd -> { - String depositUri = "/wallet/" + cmd.to() + "/deposit/" + cmd.amount(); - return kalixClient.patch(depositUri, String.class); + return componentClient.forValueEntity(cmd.to) + .call(WalletEntity::deposit) + .params(cmd.amount); }) // <4> .andThen(String.class, __ -> { return effects() diff --git a/samples/java-spring-valueentity-counter/src/main/java/com/example/action/DoubleCounterAction.java b/samples/java-spring-valueentity-counter/src/main/java/com/example/action/DoubleCounterAction.java index 450b31a773..c20f6f59f9 100644 --- a/samples/java-spring-valueentity-counter/src/main/java/com/example/action/DoubleCounterAction.java +++ b/samples/java-spring-valueentity-counter/src/main/java/com/example/action/DoubleCounterAction.java @@ -4,25 +4,27 @@ import kalix.javasdk.SideEffect; import kalix.javasdk.action.Action; import com.example.Number; -import kalix.spring.KalixClient; +import kalix.javasdk.client.ComponentClient; import kalix.javasdk.annotations.Subscribe; @Subscribe.ValueEntity(CounterEntity.class) public class DoubleCounterAction extends Action { - final private KalixClient kalixClient; + final private ComponentClient componentClient; - public DoubleCounterAction(KalixClient kalixClient){ - this.kalixClient = kalixClient; - } + public DoubleCounterAction(ComponentClient componentClient) { + this.componentClient = componentClient; + } - // tag::controller-side-effect[] - public Action.Effect increaseWithSideEffect(Integer increase){ - var counterId = actionContext().eventSubject().get(); // <1> - var doubleIncrease = increase * 2; // <2> - var deferredCall = kalixClient.post("/counter/" + counterId + "/increase", new Number(doubleIncrease), Number.class); - return effects().reply(Confirmed.instance).addSideEffect(SideEffect.of(deferredCall)); // <3> - } - // end::controller-side-effect[] + // tag::controller-side-effect[] + public Action.Effect increaseWithSideEffect(Integer increase) { + var counterId = actionContext().eventSubject().get(); // <1> + var doubleIncrease = increase * 2; // <2> + var deferredCall = componentClient.forValueEntity(counterId) + .call(CounterEntity::increaseBy) + .params(new Number(doubleIncrease)); + return effects().reply(Confirmed.instance).addSideEffect(SideEffect.of(deferredCall)); // <3> + } + // end::controller-side-effect[] } diff --git a/samples/java-spring-valueentity-shopping-cart/src/main/java/com/example/api/ShoppingCartController.java b/samples/java-spring-valueentity-shopping-cart/src/main/java/com/example/api/ShoppingCartController.java index 39397b55ec..8ed5e9bb9e 100644 --- a/samples/java-spring-valueentity-shopping-cart/src/main/java/com/example/api/ShoppingCartController.java +++ b/samples/java-spring-valueentity-shopping-cart/src/main/java/com/example/api/ShoppingCartController.java @@ -3,7 +3,7 @@ import com.example.api.ShoppingCartDTO.LineItemDTO; // tag::forward[] import kalix.javasdk.action.Action; -import kalix.spring.KalixClient; +import kalix.javasdk.client.ComponentClient; // end::forward[] import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -18,10 +18,10 @@ @RequestMapping("/carts") public class ShoppingCartController extends Action { - private final KalixClient kalixClient; + private final ComponentClient componentClient; - public ShoppingCartController(KalixClient kalixClient) { - this.kalixClient = kalixClient; // <1> + public ShoppingCartController(ComponentClient componentClient) { + this.componentClient = componentClient; // <1> } // end::forward[] @@ -31,19 +31,20 @@ public ShoppingCartController(KalixClient kalixClient) { public Action.Effect initializeCart() { final String cartId = UUID.randomUUID().toString(); // <1> CompletionStage shoppingCartCreated = - kalixClient - .post("/cart/" + cartId + "/create", "", ShoppingCartDTO.class) // <2> - .execute(); // <3> + componentClient.forValueEntity(cartId) + .call(ShoppingCartEntity::create) // <2> + .execute(); // <3> + // transform response CompletionStage> effect = - shoppingCartCreated.handle((empty, error) -> { // <4> - if (error == null) { - return effects().reply(cartId); // <5> - } else { - return effects().error("Failed to create cart, please retry"); // <6> - } - }); + shoppingCartCreated.handle((empty, error) -> { // <4> + if (error == null) { + return effects().reply(cartId); // <5> + } else { + return effects().error("Failed to create cart, please retry"); // <6> + } + }); return effects().asyncEffect(effect); // <7> } @@ -56,8 +57,9 @@ public Action.Effect verifiedAddItem(@PathVariable String cartI if (addLineItem.name().equalsIgnoreCase("carrot")) { // <3> return effects().error("Carrots no longer for sale"); // <4> } else { - var deferredCall = - kalixClient.post("/cart/" + cartId + "/items/add", addLineItem, ShoppingCartDTO.class); // <5> + var deferredCall = componentClient.forValueEntity(cartId) + .call(ShoppingCartEntity::addItem) + .params(addLineItem); // <5> return effects().forward(deferredCall); // <6> } } @@ -69,16 +71,17 @@ public Action.Effect verifiedAddItem(@PathVariable String cartI public Action.Effect createPrePopulated() { final String cartId = UUID.randomUUID().toString(); CompletionStage shoppingCartCreated = - kalixClient.post("/cart/" + cartId + "/create", "", ShoppingCartDTO.class).execute(); + componentClient.forValueEntity(cartId).call(ShoppingCartEntity::create).execute(); CompletionStage cartPopulated = - shoppingCartCreated.thenCompose(empty -> { // <1> - var initialItem = new LineItemDTO("e", "eggplant", 1); + shoppingCartCreated.thenCompose(empty -> { // <1> + var initialItem = new LineItemDTO("e", "eggplant", 1); - return kalixClient - .post("/cart/" + cartId + "/items/add", initialItem, ShoppingCartDTO.class) // <2> - .execute(); // <3> - }); + return componentClient.forValueEntity(cartId) + .call(ShoppingCartEntity::addItem) + .params(initialItem) // <2> + .execute(); // <3> + }); CompletionStage reply = cartPopulated.thenApply(ShoppingCartDTO::cartId); // <4> @@ -93,7 +96,7 @@ public Action.Effect unsafeValidation(@PathVariable String cartId, @RequestBody LineItemDTO addLineItem) { // NOTE: This is an example of an anti-pattern, do not copy this CompletionStage cartReply = - kalixClient.get("/cart/" + cartId, ShoppingCartDTO.class).execute(); // <1> + componentClient.forValueEntity(cartId).call(ShoppingCartEntity::getCart).execute(); // <1> CompletionStage> effect = cartReply.thenApply(cart -> { int totalCount = cart.items().stream() @@ -103,9 +106,10 @@ public Action.Effect unsafeValidation(@PathVariable String cartId, if (totalCount < 10) { return effects().error("Max 10 items in a cart"); } else { - var addCall = kalixClient.post("/cart/" + cartId + "/items/add", addLineItem, String.class); + CompletionStage addItemReply = componentClient.forValueEntity(cartId).call(ShoppingCartEntity::addItem).params(addLineItem) + .execute().thenApply(ShoppingCartDTO::cartId); return effects() - .forward(addCall); // <2> + .asyncReply(addItemReply); // <2> } }); diff --git a/samples/java-spring-valueentity-shopping-cart/src/main/java/com/example/api/ShoppingCartEntity.java b/samples/java-spring-valueentity-shopping-cart/src/main/java/com/example/api/ShoppingCartEntity.java index 1b99fd0f42..a44eae77b2 100644 --- a/samples/java-spring-valueentity-shopping-cart/src/main/java/com/example/api/ShoppingCartEntity.java +++ b/samples/java-spring-valueentity-shopping-cart/src/main/java/com/example/api/ShoppingCartEntity.java @@ -52,7 +52,7 @@ public ShoppingCart emptyState() { // tag::summary[] @PostMapping("/create") // <2> - public ValueEntity.Effect create(@PathVariable String cartId) { + public ValueEntity.Effect create() { //... // end::summary[] if (currentState().creationTimestamp() > 0L) { @@ -60,8 +60,8 @@ public ValueEntity.Effect create(@PathVariable String cartId) { } else { var newState = currentState().withCreationTimestamp(Instant.now().toEpochMilli()); return effects() - .updateState(newState) - .thenReply(ShoppingCartDTO.of(newState)); + .updateState(newState) + .thenReply(ShoppingCartDTO.of(newState)); } } // end::create[]