Skip to content

Commit

Permalink
Add Kotlin to workflow docs Java SDK
Browse files Browse the repository at this point in the history
  • Loading branch information
gvdongen committed Jun 13, 2024
1 parent c78c3ac commit d7bfed2
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import dev.restate.sdk.annotation.Handler;
import dev.restate.sdk.annotation.VirtualObject;


@VirtualObject
public class UserManagementService {
// <start_here>
Expand Down
1 change: 1 addition & 0 deletions code_snippets/kotlin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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 develop.workflows.Utils.Companion.sendEmailWithLink
import kotlinx.serialization.Serializable

// <start_here>
@Workflow
class SignupWorkflow {

companion object {
private val EMAIL_CLICKED = KtDurablePromiseKey.json<String>("email_clicked")
private val STATUS = KtStateKey.json<String>("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()
}
// <end_here>

@Serializable
data class Email(val email: String)

class Utils {
companion object {
fun sendEmailWithLink(email: Email, secret: String){

}
}
}
Original file line number Diff line number Diff line change
@@ -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 {
// <start_here>
@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()
}
// <end_here>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package develop.workflows

Original file line number Diff line number Diff line change
@@ -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){
// <start_submit>
val restate = Client.connect("http://localhost:8080")
val handle: SendResponse = SignupWorkflowClient
.fromClient(restate, "someone")
.submit(email)
// <end_submit>

// <start_query>
val status = SignupWorkflowClient
.fromClient(restate, "someone")
.getStatus()
// <end_query>

// <start_interact>

// <end_query>

// <start_interact>
// 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
}
// <end_interact>
}
}
129 changes: 118 additions & 11 deletions docs/develop/java/workflows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -24,15 +26,17 @@ 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:

<CH.Scrollycoding className={"spotlight-medium"}>
<Tabs groupId="sdk" queryString>
<TabItem value="java" label="Java">
<CH.Scrollycoding className={"spotlight-medium"}>

### 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.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
```

Expand All @@ -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
```

---
Expand All @@ -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
```

---
Expand All @@ -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
```

</CH.Scrollycoding>
</TabItem>
<TabItem value="kotlin" label="Kotlin">
<CH.Scrollycoding className={"spotlight-medium"}>

### 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.run`](/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
```

</CH.Scrollycoding>

</TabItem>
</Tabs>

<Admonition type={"tip"} title={"Workflows-as-code with Restate"}>
[Check out some examples of workflows-as-code with Restate on the use case page](/use-cases/workflows).
</Admonition>

## Submitting workflows with SDK clients

<Tabs groupId="sdk" queryString>
<TabItem value="java" label="Java">
<CH.Scrollycoding className={"single-item"}>
[**Submit**](/develop/java/clients):
This returns a handle to the workflow once it has been registered in Restate.
Expand All @@ -104,16 +162,49 @@ Have a look at the code example to get a better understanding of how workflows a
<CH.Scrollycoding className={"single-item"}>

[**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#<start_interact>-<end_interact>
```
</CH.Scrollycoding>
</TabItem>
<TabItem value="kotlin" label="Kotlin">
<CH.Scrollycoding className={"single-item"}>
[**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#<start_submit>-<end_submit>
```
</CH.Scrollycoding>

<CH.Scrollycoding className={"single-item"}>
[**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#<start_query>-<end_query>
```
</CH.Scrollycoding>

<CH.Scrollycoding className={"single-item"}>

[**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#<start_interact>-<end_interact>
```
</CH.Scrollycoding>
</TabItem>
</Tabs>

## Submitting workflows from a Restate service
<Tabs groupId="sdk" queryString>
<TabItem value="java" label="Java">
<CH.Scrollycoding className={"single-item"}>

[**Submit/query/signal**](/develop/java/service-communication):
Expand All @@ -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
```
</CH.Scrollycoding>
</TabItem>
<TabItem value="kotlin" label="Kotlin">
<CH.Scrollycoding className={"single-item"}>

[**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
```
</CH.Scrollycoding>
</TabItem>
</Tabs>

## Submitting workflows over HTTP

Expand Down

0 comments on commit d7bfed2

Please sign in to comment.