Skip to content

Commit

Permalink
Add Kotlin to workflow docs Java SDK (#415)
Browse files Browse the repository at this point in the history
* Add Kotlin to workflow docs Java SDK

* Small fixes

* Cleanup

* Document Kotlin clients

* Address feedback
  • Loading branch information
gvdongen authored Jun 14, 2024
1 parent a407ca3 commit 75b609b
Show file tree
Hide file tree
Showing 13 changed files with 470 additions and 85 deletions.
10 changes: 4 additions & 6 deletions code_snippets/java/src/main/java/develop/clients/Ingress.java
Original file line number Diff line number Diff line change
@@ -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() {
// <start_rpc_java>
Client rs = Client.connect("http://localhost:8080");
String greet = GreeterServiceClient.fromClient(rs)
Expand All @@ -23,7 +21,7 @@ public void myJavaHandler(Context ctx) {
// <end_rpc_java>
}

public void myOneWayCallHandler(Context ctx) {
public void myOneWayCallHandler() {

// <start_one_way_call_java>
Client rs = Client.connect("http://localhost:8080");
Expand All @@ -39,7 +37,7 @@ public void myOneWayCallHandler(Context ctx) {
// <end_one_way_call_java>
}

public void myDelayedOneWayCallHandler(Context ctx) {
public void myDelayedOneWayCallHandler() {
// <start_delayed_call_java>
Client rs = Client.connect("http://localhost:8080");
GreeterServiceClient.fromClient(rs)
Expand All @@ -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"));
// <end_service_idempotent>
}

Expand Down
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,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<Int>("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()
}
Original file line number Diff line number Diff line change
@@ -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!"
}
}

92 changes: 92 additions & 0 deletions code_snippets/kotlin/src/main/kotlin/develop/clients/Ingress.kt
Original file line number Diff line number Diff line change
@@ -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() {
// <start_rpc_kotlin>
val rs = Client.connect("http://localhost:8080")
val greet: String = GreeterServiceClient.fromClient(rs)
.greet("Hi")

val count: Int = GreetCounterObjectClient.fromClient(rs, "Mary")
.greet("Hi")
// <end_rpc_kotlin>
}

suspend fun myOneWayCallHandler() {
// <start_one_way_call_kotlin>
val rs = Client.connect("http://localhost:8080")
GreeterServiceClient.fromClient(rs)
// mark
.send()
.greet("Hi")

GreetCounterObjectClient.fromClient(rs, "Mary")
// mark
.send()
.greet("Hi")
// <end_one_way_call_kotlin>
}

suspend fun myDelayedOneWayCallHandler() {
// <start_delayed_call_kotlin>
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")
// <end_delayed_call_kotlin>
}

suspend fun idempotentInvoke() {
// <start_service_idempotent>
val rs = Client.connect("http://localhost:8080")
GreetCounterObjectClient.fromClient(rs, "Mary")
.send()
// withClass highlight-line
.greet("Hi", CallRequestOptions.DEFAULT.withIdempotency("abcde"))
// <end_service_idempotent>
}

suspend fun attach() {
// <start_service_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<String>())
.attach()

// Option 2: Peek to see if the result is ready
// mark(1:3)
val output: Output<String> = rs
.invocationHandle(handle.invocationId, KtSerdes.json<String>())
.output
if (output.isReady) {
val result = output.value
}
// <end_service_attach>
}
}
41 changes: 0 additions & 41 deletions code_snippets/kotlin/src/main/kotlin/develop/signals/MyWorkflow.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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

// <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)

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,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>
}
}
Loading

0 comments on commit 75b609b

Please sign in to comment.