From 75b609bcfdedffab83b2a81c56f957a8b264d617 Mon Sep 17 00:00:00 2001 From: Giselle van Dongen Date: Fri, 14 Jun 2024 13:26:02 +0200 Subject: [PATCH] Add Kotlin to workflow docs Java SDK (#415) * Add Kotlin to workflow docs Java SDK * Small fixes * Cleanup * Document Kotlin clients * Address feedback --- .../main/java/develop/clients/Ingress.java | 10 +- .../workflows/UserManagementService.java | 1 - code_snippets/kotlin/build.gradle.kts | 1 + .../develop/clients/GreetCounterObject.kt | 33 +++++ .../kotlin/develop/clients/GreeterService.kt | 14 ++ .../main/kotlin/develop/clients/Ingress.kt | 92 +++++++++++++ .../main/kotlin/develop/signals/MyWorkflow.kt | 41 ------ .../develop/workflows/SignupWorkflow.kt | 57 ++++++++ .../workflows/UserManagementService.kt | 27 ++++ .../develop/workflows/WorkflowSubmitter.kt | 43 ++++++ docs/develop/java/clients.mdx | 103 ++++++++++---- docs/develop/java/workflows.mdx | 129 ++++++++++++++++-- src/css/custom.css | 4 + 13 files changed, 470 insertions(+), 85 deletions(-) create mode 100644 code_snippets/kotlin/src/main/kotlin/develop/clients/GreetCounterObject.kt create mode 100644 code_snippets/kotlin/src/main/kotlin/develop/clients/GreeterService.kt create mode 100644 code_snippets/kotlin/src/main/kotlin/develop/clients/Ingress.kt delete mode 100644 code_snippets/kotlin/src/main/kotlin/develop/signals/MyWorkflow.kt create mode 100644 code_snippets/kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt create mode 100644 code_snippets/kotlin/src/main/kotlin/develop/workflows/UserManagementService.kt create mode 100644 code_snippets/kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt diff --git a/code_snippets/java/src/main/java/develop/clients/Ingress.java b/code_snippets/java/src/main/java/develop/clients/Ingress.java index 9c8cfee2..77706efe 100644 --- a/code_snippets/java/src/main/java/develop/clients/Ingress.java +++ b/code_snippets/java/src/main/java/develop/clients/Ingress.java @@ -1,18 +1,16 @@ package operate.invocations; -import dev.restate.sdk.Context; import dev.restate.sdk.JsonSerdes; import dev.restate.sdk.client.CallRequestOptions; import dev.restate.sdk.client.Client; import dev.restate.sdk.client.SendResponse; import dev.restate.sdk.common.Output; -import develop.MyWorkflowClient; import java.time.Duration; public class Ingress { - public void myJavaHandler(Context ctx) { + public void myJavaHandler() { // Client rs = Client.connect("http://localhost:8080"); String greet = GreeterServiceClient.fromClient(rs) @@ -23,7 +21,7 @@ public void myJavaHandler(Context ctx) { // } - public void myOneWayCallHandler(Context ctx) { + public void myOneWayCallHandler() { // Client rs = Client.connect("http://localhost:8080"); @@ -39,7 +37,7 @@ public void myOneWayCallHandler(Context ctx) { // } - public void myDelayedOneWayCallHandler(Context ctx) { + public void myDelayedOneWayCallHandler() { // Client rs = Client.connect("http://localhost:8080"); GreeterServiceClient.fromClient(rs) @@ -61,7 +59,7 @@ public void idempotentInvoke(){ GreetCounterObjectClient.fromClient(rs, "Mary") .send() // withClass highlight-line - .greet( "Hi", CallRequestOptions.DEFAULT.withIdempotency("abcde")); + .greet("Hi", CallRequestOptions.DEFAULT.withIdempotency("abcde")); // } diff --git a/code_snippets/java/src/main/java/develop/workflows/UserManagementService.java b/code_snippets/java/src/main/java/develop/workflows/UserManagementService.java index 5e6e9598..068cf446 100644 --- a/code_snippets/java/src/main/java/develop/workflows/UserManagementService.java +++ b/code_snippets/java/src/main/java/develop/workflows/UserManagementService.java @@ -4,7 +4,6 @@ import dev.restate.sdk.annotation.Handler; import dev.restate.sdk.annotation.VirtualObject; - @VirtualObject public class UserManagementService { // diff --git a/code_snippets/kotlin/build.gradle.kts b/code_snippets/kotlin/build.gradle.kts index 48614a40..d4992102 100644 --- a/code_snippets/kotlin/build.gradle.kts +++ b/code_snippets/kotlin/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { // Restate SDK implementation("dev.restate:sdk-api-kotlin:$restateVersion") + implementation("dev.restate:sdk-common:$restateVersion") implementation("dev.restate:sdk-http-vertx:$restateVersion") implementation("dev.restate:sdk-lambda:$restateVersion") diff --git a/code_snippets/kotlin/src/main/kotlin/develop/clients/GreetCounterObject.kt b/code_snippets/kotlin/src/main/kotlin/develop/clients/GreetCounterObject.kt new file mode 100644 index 00000000..8c98277f --- /dev/null +++ b/code_snippets/kotlin/src/main/kotlin/develop/clients/GreetCounterObject.kt @@ -0,0 +1,33 @@ +package develop.clients + +import dev.restate.sdk.annotation.Handler +import dev.restate.sdk.annotation.VirtualObject +import dev.restate.sdk.http.vertx.RestateHttpEndpointBuilder +import dev.restate.sdk.kotlin.KtStateKey +import dev.restate.sdk.kotlin.ObjectContext +import develop.workflows.SignupWorkflow + +@VirtualObject +class GreetCounterObject { + + companion object { + private val COUNT = KtStateKey.json("count") + } + + @Handler + suspend fun greet(ctx: ObjectContext, greeting: String): Int { + val count = ctx.get(COUNT) ?: 0 + + val newCount = count + 1 + ctx.set(COUNT, newCount) + return newCount + } +} + +fun main() { + RestateHttpEndpointBuilder + .builder() + .bind(GreetCounterObject()) + .bind(GreeterService()) + .buildAndListen() +} \ No newline at end of file diff --git a/code_snippets/kotlin/src/main/kotlin/develop/clients/GreeterService.kt b/code_snippets/kotlin/src/main/kotlin/develop/clients/GreeterService.kt new file mode 100644 index 00000000..978712b5 --- /dev/null +++ b/code_snippets/kotlin/src/main/kotlin/develop/clients/GreeterService.kt @@ -0,0 +1,14 @@ +package develop.clients + +import dev.restate.sdk.annotation.Handler +import dev.restate.sdk.annotation.Service +import dev.restate.sdk.kotlin.Context + +@Service +class GreeterService { + @Handler + suspend fun greet(ctx: Context, greeting: String): String { + return "Hello, $greeting!" + } +} + diff --git a/code_snippets/kotlin/src/main/kotlin/develop/clients/Ingress.kt b/code_snippets/kotlin/src/main/kotlin/develop/clients/Ingress.kt new file mode 100644 index 00000000..18facc10 --- /dev/null +++ b/code_snippets/kotlin/src/main/kotlin/develop/clients/Ingress.kt @@ -0,0 +1,92 @@ +package operate.invocations + +import dev.restate.sdk.client.CallRequestOptions +import dev.restate.sdk.client.Client +import dev.restate.sdk.client.SendResponse +import dev.restate.sdk.common.Output +import dev.restate.sdk.kotlin.Context +import dev.restate.sdk.kotlin.KtSerdes +import develop.clients.GreetCounterObjectClient +import develop.clients.GreeterServiceClient +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds + +class Ingress { + suspend fun myJavaHandler() { + // + val rs = Client.connect("http://localhost:8080") + val greet: String = GreeterServiceClient.fromClient(rs) + .greet("Hi") + + val count: Int = GreetCounterObjectClient.fromClient(rs, "Mary") + .greet("Hi") + // + } + + suspend fun myOneWayCallHandler() { + // + val rs = Client.connect("http://localhost:8080") + GreeterServiceClient.fromClient(rs) + // mark + .send() + .greet("Hi") + + GreetCounterObjectClient.fromClient(rs, "Mary") + // mark + .send() + .greet("Hi") + // + } + + suspend fun myDelayedOneWayCallHandler() { + // + val rs = Client.connect("http://localhost:8080") + GreeterServiceClient.fromClient(rs) + // withClass highlight-line + .send(1.seconds) + .greet("Hi") + + GreetCounterObjectClient.fromClient(rs, "Mary") + // withClass highlight-line + .send(1000.milliseconds) + .greet("Hi") + // + } + + suspend fun idempotentInvoke() { + // + val rs = Client.connect("http://localhost:8080") + GreetCounterObjectClient.fromClient(rs, "Mary") + .send() + // withClass highlight-line + .greet("Hi", CallRequestOptions.DEFAULT.withIdempotency("abcde")) + // + } + + suspend fun attach() { + // + val rs = Client.connect("http://localhost:8080") + val handle: SendResponse = GreeterServiceClient.fromClient(rs) + .send() + // mark + .greet("Hi", CallRequestOptions.DEFAULT.withIdempotency("abcde")) + + // ... do something else ... + + // Option 1: Attach later to retrieve the result + // mark(1:3) + val greeting: String = rs + .invocationHandle(handle.invocationId, KtSerdes.json()) + .attach() + + // Option 2: Peek to see if the result is ready + // mark(1:3) + val output: Output = rs + .invocationHandle(handle.invocationId, KtSerdes.json()) + .output + if (output.isReady) { + val result = output.value + } + // + } +} \ No newline at end of file diff --git a/code_snippets/kotlin/src/main/kotlin/develop/signals/MyWorkflow.kt b/code_snippets/kotlin/src/main/kotlin/develop/signals/MyWorkflow.kt deleted file mode 100644 index aefa260d..00000000 --- a/code_snippets/kotlin/src/main/kotlin/develop/signals/MyWorkflow.kt +++ /dev/null @@ -1,41 +0,0 @@ -package develop.signals - -import dev.restate.sdk.annotation.Handler -import dev.restate.sdk.annotation.Workflow -import dev.restate.sdk.kotlin.KtDurablePromiseKey -import dev.restate.sdk.kotlin.SharedWorkflowContext -import dev.restate.sdk.kotlin.WorkflowContext - -// -@Workflow -class MyWorkflow { - - companion object { - private val MY_BOOLEAN_SIGNAL = KtDurablePromiseKey.json("my-boolean-signal") - } - - @Workflow - suspend fun run(ctx: WorkflowContext, input: String): String { - - // do some steps... - - // withClass highlight-line - // Creation of the Durable Promise - // withClass highlight-line - val signal: Boolean = ctx.promise(MY_BOOLEAN_SIGNAL).awaitable().await() - - // do some steps... - - return "success" - } - - @Handler - suspend fun resolveMySignal(ctx: SharedWorkflowContext, signal: Boolean) { - // withClass highlight-line - // Resolution of the Durable Promise - // withClass highlight-line - ctx.promiseHandle(MY_BOOLEAN_SIGNAL).resolve(signal) - } - -} -// diff --git a/code_snippets/kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt b/code_snippets/kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt new file mode 100644 index 00000000..439189b6 --- /dev/null +++ b/code_snippets/kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt @@ -0,0 +1,57 @@ +package develop.workflows + +import dev.restate.sdk.annotation.Shared +import dev.restate.sdk.annotation.Workflow +import dev.restate.sdk.http.vertx.RestateHttpEndpointBuilder +import dev.restate.sdk.kotlin.* +import kotlinx.serialization.Serializable + +// +@Workflow +class SignupWorkflow { + + companion object { + private val EMAIL_CLICKED = KtDurablePromiseKey.json("email_clicked") + private val STATUS = KtStateKey.json("status") + } + + @Workflow + suspend fun run(ctx: WorkflowContext, email: Email): Boolean { + val secret = ctx.random().nextUUID().toString() + ctx.set(STATUS, "Generated secret") + + ctx.runBlock ("send email") { + sendEmailWithLink(email, secret) + } + + val clickSecret = ctx.promise(EMAIL_CLICKED) + .awaitable() + .await() + ctx.set(STATUS, "Clicked email") + + return clickSecret == secret + } + + @Shared + suspend fun click(ctx: SharedWorkflowContext, secret: String) { + ctx.promiseHandle(EMAIL_CLICKED).resolve(secret) + } + + @Shared + suspend fun getStatus(ctx: SharedWorkflowContext): String? { + return ctx.get(STATUS) + } +} + +fun main() { + RestateHttpEndpointBuilder + .builder() + .bind(SignupWorkflow()) + .buildAndListen() +} +// + +@Serializable +data class Email(val email: String) + +fun sendEmailWithLink(email: Email, secret: String){} \ No newline at end of file diff --git a/code_snippets/kotlin/src/main/kotlin/develop/workflows/UserManagementService.kt b/code_snippets/kotlin/src/main/kotlin/develop/workflows/UserManagementService.kt new file mode 100644 index 00000000..c0e6780d --- /dev/null +++ b/code_snippets/kotlin/src/main/kotlin/develop/workflows/UserManagementService.kt @@ -0,0 +1,27 @@ +package develop.workflows + +import dev.restate.sdk.annotation.Handler; +import dev.restate.sdk.annotation.VirtualObject; +import dev.restate.sdk.kotlin.ObjectContext + +@VirtualObject +class UserManagementService { + // + @Handler + suspend fun setup(ctx: ObjectContext, email: Email) { + // focus(1:3) + val result = SignupWorkflowClient.fromContext(ctx, "someone") + .run(email) + .await() + + } + + @Handler + suspend fun queryStatus(ctx: ObjectContext) { + // focus(1:3) + val status = SignupWorkflowClient.fromContext(ctx, "someone") + .getStatus() + .await() + } + // +} diff --git a/code_snippets/kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt b/code_snippets/kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt new file mode 100644 index 00000000..7f4ecd2f --- /dev/null +++ b/code_snippets/kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt @@ -0,0 +1,43 @@ +package develop.workflows + +import dev.restate.sdk.client.Client +import dev.restate.sdk.client.SendResponse + +class WorkflowSubmitter { + + suspend fun submitWorkflow(email: Email){ + // + val restate = Client.connect("http://localhost:8080") + val handle: SendResponse = SignupWorkflowClient + .fromClient(restate, "someone") + .submit(email) + // + + // + val status = SignupWorkflowClient + .fromClient(restate, "someone") + .getStatus() + // + + // + + // + + // + // Option 1: attach and wait for result + val result = SignupWorkflowClient + .fromClient(restate, "someone") + .workflowHandle() + .attach() + + // Option 2: peek to check if ready + val peekOutput = SignupWorkflowClient + .fromClient(restate, "someone") + .workflowHandle() + .output + if (peekOutput.isReady) { + val result2 = peekOutput.value + } + // + } +} \ No newline at end of file diff --git a/docs/develop/java/clients.mdx b/docs/develop/java/clients.mdx index d17c825d..9a71b74b 100644 --- a/docs/develop/java/clients.mdx +++ b/docs/develop/java/clients.mdx @@ -3,9 +3,10 @@ sidebar_position: 13 description: "Use the clients to invoke handlers programmatically." --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; import Admonition from '@theme/Admonition'; import {Step} from "../../../src/components/Stepper"; -import clsx from "clsx"; # Clients The Restate SDK client library lets you invoke Restate handlers from anywhere in your application. @@ -27,33 +28,66 @@ Invoke a handler programmatically with the SDK clients as follows: Register the service you want to invoke.}/> - + + + - **Request-response invocations** allow you to wait on a response from the handler. + **Request-response invocations** allow you to wait on a response from the handler. - ```java - CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- - ``` + ```java + CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- + ``` - - + + - **One-way invocations** allow you to send a message without waiting for a response. + **One-way invocations** allow you to send a message without waiting for a response. - ```java - CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- - ``` + ```java + CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- + ``` - - + + - **Delayed invocations** allow you to schedule an invocation for a later point in time. + **Delayed invocations** allow you to schedule an invocation for a later point in time. - ```java - CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- - ``` + ```java + CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- + ``` + + + + + + + **Request-response invocations** allow you to wait on a response from the handler. + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#- + ``` + + + + + **One-way invocations** allow you to send a message without waiting for a response. - + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#- + ``` + + + + + **Delayed invocations** allow you to schedule an invocation for a later point in time. + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#- + ``` + + + + @@ -65,9 +99,18 @@ Invoke a handler programmatically with the SDK clients as follows: To make a service call idempotent, you can use the idempotency key feature. Add the idempotency key [to the header](https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/) via: -```java -CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- -``` + + + ```java + CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- + ``` + + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#- + ``` + + After the invocation completes, Restate persists the response for a retention period of one day (24 hours). If you re-invoke the service with the same idempotency key within 24 hours, Restate sends back the same response and doesn't re-execute the request to the service. @@ -105,10 +148,18 @@ You can: You can use the invocation ID to attach or peek at a service/object invocation that used an idempotency key: -```java -CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- -``` - + + + ```java + CODE_LOAD::java/src/main/java/develop/clients/Ingress.java#- + ``` + + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/clients/Ingress.kt#- + ``` + + [Have a look at the workflow documentation to learn how to attach to workflow executions.](/develop/java/workflows#submitting-workflows-with-sdk-clients) diff --git a/docs/develop/java/workflows.mdx b/docs/develop/java/workflows.mdx index 2a76f49e..58237c0d 100644 --- a/docs/develop/java/workflows.mdx +++ b/docs/develop/java/workflows.mdx @@ -3,6 +3,8 @@ sidebar_position: 7 description: "Find out how you can implement workflows with the Restate SDK." --- +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; import Admonition from '@theme/Admonition'; # Workflows @@ -24,7 +26,9 @@ A workflow can be seen as a special type of [Virtual Object](/concepts/services# ## Implementing workflows Have a look at the code example to get a better understanding of how workflows are implemented: - + + + ### The `run` handler @@ -32,7 +36,7 @@ Have a look at the code example to get a better understanding of how workflows a This handler has access to the same SDK features as Service and Virtual Object handlers. For example, use [`ctx.run`](/develop/java/journaling-results#journaled-actions) to log intermediate results in Restate and avoid re-execution on replay. - ```java SignupWorkflow.java mark=9:25 + ```java SignupWorkflow.java mark=8:23 CODE_LOAD::java/src/main/java/develop/workflows/SignupWorkflow.java ``` @@ -45,7 +49,7 @@ Have a look at the code example to get a better understanding of how workflows a Every workflow execution can be seen as a new object, so the state is isolated to a single workflow execution. The state can only be mutated by the `run` handler of the workflow. The other handlers can only read the state. - ```java SignupWorkflow.java mark=31:34 + ```java SignupWorkflow.java mark=30:33 ``` --- @@ -59,7 +63,7 @@ Have a look at the code example to get a better understanding of how workflows a 1. Create a promise in your `run` handler that is durable and distributed 2. Resolve or reject the promise in any other handler in the workflow. This can be done at most one time. - ```java SignupWorkflow.java mark=18,26:29 + ```java SignupWorkflow.java mark=17:19,25:28 ``` --- @@ -69,18 +73,72 @@ Have a look at the code example to get a better understanding of how workflows a You serve workflows in the same way as Services and Virtual Objects: by binding them to an [HTTP endpoint](/develop/java/serving#creating-an-http-endpoint) or [AWS Lambda handler](/develop/java/serving#creating-a-lambda-handler). Make sure you [register the endpoint or Lambda handler](/operate/registration) in Restate before invoking it. - ```java SignupWorkflow.java mark=36:40 - + ```java SignupWorkflow.java mark=35:39 ``` + + + + + ### The `run` handler + + Every workflow needs a `run` handler. + This handler has access to the same SDK features as Service and Virtual Object handlers. + For example, use [`ctx.runBlock`](/develop/java/journaling-results#journaled-actions) to log intermediate results in Restate and avoid re-execution on replay. + + ```kotlin SignupWorkflow.kt mark=9:24 + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/SignupWorkflow.kt + ``` + + --- + + ### Querying workflows + + Similar to Virtual Objects, you can retrieve the [K/V state](/develop/java/state#retrieving-state) of workflows via the other handlers defined in the workflow definition, + For example, here we expose the status of the workflow to external clients. + Every workflow execution can be seen as a new object, so the state is isolated to a single workflow execution. + The state can only be mutated by the `run` handler of the workflow. The other handlers can only read the state. + + ```kotlin SignupWorkflow.kt mark=31:34 + ``` + + --- + + ### Signaling workflows + + You can use Durable Promises to interact with your running workflows: to let the workflow block until an event occurs, or to send a signal / information into or out of a running workflow. + These promises are durable and distributed, meaning they survive crashes and can be resolved or rejected by any handler in the workflow. + + Do the following: + 1. Create a promise in your `run` handler that is durable and distributed + 2. Resolve or reject the promise in any other handler in the workflow. This can be done at most one time. + + ```kotlin SignupWorkflow.kt mark=18:20,26:29 + ``` + + --- + + ### Serving and registering workflows + + You serve workflows in the same way as Services and Virtual Objects: by binding them to an [HTTP endpoint](/develop/java/serving#creating-an-http-endpoint) or [AWS Lambda handler](/develop/java/serving#creating-a-lambda-handler). + Make sure you [register the endpoint or Lambda handler](/operate/registration) in Restate before invoking it. + + ```kotlin SignupWorkflow.kt mark=38:41 + ``` + + + + + [Check out some examples of workflows-as-code with Restate on the use case page](/use-cases/workflows). ## Submitting workflows with SDK clients - + + [**Submit**](/develop/java/clients): This returns a handle to the workflow once it has been registered in Restate. @@ -104,16 +162,49 @@ Have a look at the code example to get a better understanding of how workflows a [**Attach/peek**](/develop/java/clients#retrieve-result-of-invocations-and-workflows): - This lets you retrieve the result of a workflow or check if it's finished. - Use `.getOutput()` to peek whether the result is ready. + This lets you attach to a workflow and wait for it to finish, or to peek whether the result is ready. ```java CODE_LOAD::java/src/main/java/develop/workflows/WorkflowSubmitter.java#- ``` + + + + [**Submit**](/develop/java/clients): + This returns a handle to the workflow once it has been registered in Restate. + You can only submit once per workflow ID (here `"someone"`). + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt#- + ``` + + + + [**Query/signal**](/develop/java/clients): + Call the other handlers of the workflow in the same way as for Virtual Object handlers. + Use `send()` for one-way calls. + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt#- + ``` + + + + + [**Attach/peek**](/develop/java/clients#retrieve-result-of-invocations-and-workflows): + This lets you attach to a workflow and wait for it to finish, or to peek whether the result is ready. + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/WorkflowSubmitter.kt#- + ``` + + + ## Submitting workflows from a Restate service - + + [**Submit/query/signal**](/develop/java/service-communication): @@ -122,10 +213,26 @@ Have a look at the code example to get a better understanding of how workflows a Use `.send()` for to call the handler without waiting for the result. You can only call the `run` handler (submit) once per workflow ID (here `"someone"`). - ```java + ```kotlin CODE_LOAD::java/src/main/java/develop/workflows/UserManagementService.java ``` + + + + + [**Submit/query/signal**](/develop/java/service-communication): + Use the generated client to call any workflow handler in the same way as for Services and Virtual Objects. + This returns the result of the workflow/handler once it has finished. + Use `.send()` for to call the handler without waiting for the result. + You can only call the `run` handler (submit) once per workflow ID (here `"someone"`). + + ```kotlin + CODE_LOAD::kotlin/src/main/kotlin/develop/workflows/UserManagementService.kt + ``` + + + ## Submitting workflows over HTTP diff --git a/src/css/custom.css b/src/css/custom.css index 43020984..7737d5de 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -41,6 +41,10 @@ background: var(--docusaurus-highlighted-code-line-bg); } +.ch-code-multiline-mark .ch-code-button { + display: none !important; +} + .ch-code-multiline-mark-border { background: var(--ifm-color-secondary-darkest); }