Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ingress clients #43

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
534 changes: 534 additions & 0 deletions client/client.go

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions client/workflow.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package client

import (
"context"
"net/url"

"github.com/restatedev/sdk-go/internal/options"
)

// Workflow gets a Workflow request client by service name, workflow ID and method name
// It must be called with a context returned from [Connect]
func Workflow[O any](ctx context.Context, service string, workflowID string, method string, opts ...options.IngressClientOption) IngressClient[any, O] {
return Object[O](ctx, service, workflowID, method, opts...)
}

type WorkflowSubmission struct {
InvocationId string
}

func (w WorkflowSubmission) attachable() bool {
return true
}

func (w WorkflowSubmission) attachUrl(connURL *url.URL) *url.URL {
return connURL.JoinPath("restate", "invocation", w.InvocationId, "attach")
}

func (w WorkflowSubmission) outputUrl(connURL *url.URL) *url.URL {
return connURL.JoinPath("restate", "invocation", w.InvocationId, "output")
}

var _ Attacher = WorkflowSubmission{}

// WorkflowSubmit submits a workflow, defaulting to 'Run' as the main handler name, but this is configurable with [restate.WithWorkflowRun]
// It must be called with a context returned from [Connect]
func WorkflowSubmit[I any](ctx context.Context, service string, workflowID string, input I, opts ...options.WorkflowSubmitOption) (WorkflowSubmission, error) {
os := options.WorkflowSubmitOptions{}
for _, opt := range opts {
opt.BeforeWorkflowSubmit(&os)
}
if os.RunHandler == "" {
os.RunHandler = "Run"
}

send, err := Workflow[I](ctx, service, workflowID, os.RunHandler, os).Send(input, os)
if err != nil {
return WorkflowSubmission{}, err
}
return WorkflowSubmission{InvocationId: send.InvocationId}, nil
}

type WorkflowIdentifier struct {
Service string
WorkflowID string
}

var _ Attacher = WorkflowIdentifier{}

func (w WorkflowIdentifier) attachable() bool {
return true
}

func (w WorkflowIdentifier) attachUrl(connURL *url.URL) *url.URL {
return connURL.JoinPath("restate", "workflow", w.Service, w.WorkflowID, "attach")
}

func (w WorkflowIdentifier) outputUrl(connURL *url.URL) *url.URL {
return connURL.JoinPath("restate", "workflow", w.Service, w.WorkflowID, "output")
}

// WorkflowAttach attaches to a workflow, waiting for it to complete and returning the result.
// It is only possible to 'attach' to a workflow that has been previously submitted.
// This operation is safe to retry many times, and it will always return the same result.
// It must be called with a context returned from [Connect]
func WorkflowAttach[O any](ctx context.Context, service string, workflowID string, opts ...options.IngressClientOption) (O, error) {
return Attach[O](ctx, WorkflowIdentifier{service, workflowID}, opts...)
}

// WorkflowOutput tries to retrieve the output of a workflow if it has already completed. Otherwise, [ready] will be false.
// It must be called with a context returned from [Connect]
func WorkflowOutput[O any](ctx context.Context, service string, workflowID string, opts ...options.IngressClientOption) (o O, ready bool, err error) {
return GetOutput[O](ctx, WorkflowIdentifier{service, workflowID}, opts...)
}
38 changes: 38 additions & 0 deletions examples/codegen/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package main

import (
"context"
"fmt"

"github.com/restatedev/sdk-go/client"
helloworld "github.com/restatedev/sdk-go/examples/codegen/proto"
)

func main() {
ctx, err := client.Connect(context.Background(), "http://127.0.0.1:8080")
if err != nil {
panic(err)
}

greeter := helloworld.NewGreeterIngressClient(ctx)
greeting, err := greeter.SayHello().Request(&helloworld.HelloRequest{Name: "world"})
if err != nil {
panic(err)
}
fmt.Println(greeting.Message)

workflow := helloworld.NewWorkflowIngressClient(ctx, "my-workflow")
send, err := workflow.Run().Send(&helloworld.RunRequest{})
if err != nil {
panic(err)
}
status, err := workflow.Status().Request(&helloworld.StatusRequest{})
if err != nil {
panic(err)
}
fmt.Println("workflow running with invocation id", send.InvocationId, "and status", status.Status)

if _, err := workflow.Finish().Request(&helloworld.FinishRequest{}); err != nil {
panic(err)
}
}
136 changes: 136 additions & 0 deletions examples/codegen/proto/helloworld_restate.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions examples/ticketreservation/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"context"
"fmt"

restate "github.com/restatedev/sdk-go"
"github.com/restatedev/sdk-go/client"
)

func main() {
ctx, err := client.Connect(context.Background(), "http://127.0.0.1:8080")
if err != nil {
panic(err)
}

if ok, err := AddTicketSend(ctx, "user-1", "ticket-1"); err != nil {
panic(err)
} else if !ok {
fmt.Println("Ticket-1 was not available")
} else {
fmt.Println("Added ticket-1 to user-1 basket")
}

if ok, err := Checkout(ctx, "user-1", "ticket-1"); err != nil {
panic(err)
} else if !ok {
fmt.Println("Nothing to check out")
} else {
fmt.Println("Checked out")
}
}

func AddTicket(ctx context.Context, userId, ticketId string) (bool, error) {
return client.
Object[bool](ctx, "UserSession", userId, "AddTicket").
Request(ticketId)
}

func AddTicketSend(ctx context.Context, userId, ticketId string) (bool, error) {
send, err := client.
Object[bool](ctx, "UserSession", userId, "AddTicket").
Send(ticketId, restate.WithIdempotencyKey(fmt.Sprintf("%s/%s", userId, ticketId)))
if err != nil {
return false, err
}

fmt.Println("Submitted AddTicket with ID", send.InvocationId)

o, ready, err := client.GetOutput[bool](ctx, send)
if err != nil {
return false, err
}
if ready {
return o, nil
}

return client.Attach[bool](ctx, send)
}

func Checkout(ctx context.Context, userId, ticketId string) (bool, error) {
return client.
Object[bool](ctx, "UserSession", userId, "Checkout").
Request(restate.Void{})
}
2 changes: 1 addition & 1 deletion facilitators.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func ResolveAwakeable[T any](ctx Context, id string, value T, options ...options
ctx.inner().ResolveAwakeable(id, value, options...)
}

// ResolveAwakeable allows an awakeable (not necessarily from this service) to be
// RejectAwakeable allows an awakeable (not necessarily from this service) to be
// rejected with a particular error.
func RejectAwakeable(ctx Context, id string, reason error) {
ctx.inner().RejectAwakeable(id, reason)
Expand Down
Loading
Loading