### Workflow Run Usage Example Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/clients.mdx Demonstrates how to start a workflow, send signals, query its state, perform an update, and wait for its completion using the `CreateFooRun` interface. ```go package main import ( "context" "log" "time" examplev1 "path/to/gen/example/v1" "go.temporal.io/sdk/client" ) func main() { c, _ := client.Dial(client.Options{}) client := examplev1.NewExampleClient(c) ctx := context.Background() // Start workflow run, err := client.CreateFooAsync(ctx, &examplev1.CreateFooRequest{Name: "test"}) if err != nil { log.Fatal(err) } log.Printf("workflow started: %s", run.ID()) // Send signals err = run.SetFooProgress(ctx, &examplev1.SetFooProgressRequest{Progress: 25.0}) if err != nil { log.Printf("signal failed: %v", err) } // Query workflow state progress, err := run.GetFooProgress(ctx) if err != nil { log.Printf("query failed: %v", err) } else { log.Printf("current progress: %.1f%%", progress.Progress) } // Update workflow update, err := run.UpdateFooProgress(ctx, &examplev1.SetFooProgressRequest{Progress: 100.0}) if err != nil { log.Printf("update failed: %v", err) } else { log.Printf("updated progress: %.1f%%", update.Progress) } // Wait for completion result, err := run.Get(ctx) if err != nil { log.Fatalf("workflow failed: %v", err) } log.Printf("workflow completed: %s", result.String()) } ``` -------------------------------- ### Mutex Example Go Implementation Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/mutex.mdx The main Go file for the mutex example. It includes the worker setup and the sample workflow execution logic. Ensure the Temporal server is running before executing. ```go package main import ( "context" "fmt" "log" "os" "time" "go.temporal.io/sdk/client" "go.temporal.io/sdk/workflow" "google.golang.org/protobuf/proto" mutexpb "github.com/cludden/protoc-gen-go-temporal/examples/mutex/gen/example/mutex/v1" "github.com/cludden/protoc-gen-go-temporal/protoc-gen-go-temporal/pkg/mutex" ) func main() { // ... (rest of the main function omitted for brevity) if len(os.Args) < 2 { log.Fatal("Usage: main.go ") } switch os.Args[1] { case "worker": startWorker() case "client": startClient() default: log.Fatalf("Unknown command: %s", os.Args[1]) } } func startWorker() { // ... (worker setup omitted for brevity) c, err := client.NewClient(client.Options{Namespace: "default"}) if err != nil { log.Fatalln("Unable to create client", err) } defer c.Close() workerOptions := workflow.WorkerOptions{} worker := workflow.NewWorker(c, "mutex-example", workerOptions) worker.RegisterWorkflow(SampleWorkflowWithMutex) if err := worker.Run(); err != nil { log.Fatalln("Unable to run worker", err) } } func startClient() { // ... (client setup omitted for brevity) c, err := client.NewClient(client.Options{Namespace: "default"}) if err != nil { log.Fatalln("Unable to create client", err) } defer c.Close() resourceID := "foo" if len(os.Args) > 3 && os.Args[2] == "--resource-id" { resourceID = os.Args[3] } workflowID := "mutex-workflow-" + resourceID workflowOptions := client.StartWorkflowOptions{ ID: workflowID, } mutexRequest := &mutexpb.MutexRequest{ResourceId: resourceID} input, err := proto.Marshal(mutexRequest) if err != nil { log.Fatalln("Unable to marshal request", err) } wfExecution, err := c.ExecuteWorkflow(context.Background(), workflowOptions, SampleWorkflowWithMutex, input) if err != nil { log.Fatalln("Unable to execute workflow", err) } log.Printf("Started workflow %s, WorkflowID: %s", wfExecution.GetRunID(), wfExecution.GetID()) var result mutexpb.MutexResponse if err := wfExecution.Get(context.Background(), &result); err != nil { log.Fatalln("Unable to get workflow result", err) } log.Printf("Workflow result: %s\n", result.GetMessage()) } func SampleWorkflowWithMutex(ctx workflow.Context, request []byte) (mutexpb.MutexResponse, error) { var mutexRequest mutexpb.MutexRequest if err := proto.Unmarshal(request, &mutexRequest); err != nil { return mutexpb.MutexResponse{}, fmt.Errorf("unable to unmarshal request: %w", err) } // Set up mutex options mutexOptions := mutex.Options{ ResourceID: mutexRequest.GetResourceId(), TTL: 1 * time.Minute, } // Create a mutex client mutexClient := mutex.NewClient(mutexOptions) // Acquire the mutex childCtx, cancel := workflow.WithCancel(ctx) defer cancel() // Use a short timeout for acquiring the mutex to prevent indefinite blocking acquireCtx, acquireCancel := workflow.WithTimeout(childCtx, 10*time.Second) defer acquireCancel() log.Printf("Attempting to acquire mutex for resource: %s\n", mutexRequest.GetResourceId()) mutexHandle, err := mutexClient.Acquire(acquireCtx) if err != nil { return mutexpb.MutexResponse{}, fmt.Errorf("failed to acquire mutex: %w", err) } defer func() { log.Printf("Releasing mutex for resource: %s\n", mutexRequest.GetResourceId()) mutexHandle.Release(childCtx) }() log.Printf("Mutex acquired for resource: %s\n", mutexRequest.GetResourceId()) // Simulate work that requires exclusive access workflow.Sleep(ctx, 5*time.Second) log.Printf("Work completed for resource: %s\n", mutexRequest.GetResourceId()) return mutexpb.MutexResponse{ Message: fmt.Sprintf("Successfully processed resource %s", mutexRequest.GetResourceId()), }, nil } ``` -------------------------------- ### Child Workflow Execution Example Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/child-workflows.mdx Demonstrates how to start a child workflow asynchronously, wait for it to start, send signals, query its status, update its configuration, and retrieve its final result. ```go func (w *ParentWorkflow) Execute(ctx workflow.Context) (*examplev1.ParentResponse, error) { // Start child workflow asynchronously childRun, err := examplev1.CreateFooChildAsync(ctx, &examplev1.CreateFooRequest{ Name: "managed-child", }) if err != nil { return nil, err } // Wait for child to start and get execution info execution, err := childRun.WaitStart(ctx) if err != nil { return nil, fmt.Errorf("child failed to start: %w", err) } workflow.GetLogger(ctx).Info("child started", "workflow_id", execution.ID, "run_id", execution.RunID) // Send signal to child err = childRun.SetFooProgress(ctx, &examplev1.SetFooProgressRequest{ Progress: 0.25, }) if err != nil { workflow.GetLogger(ctx).Error("failed to signal child", "error", err) } // Query child state status, err := childRun.GetFooStatus(ctx) if err != nil { workflow.GetLogger(ctx).Error("failed to query child", "error", err) } else { workflow.GetLogger(ctx).Info("child status", "status", status) } // Update child configuration updateResult, err := childRun.UpdateFooConfig(ctx, &examplev1.UpdateFooConfigRequest{ MaxRetries: 5, }) if err != nil { workflow.GetLogger(ctx).Error("failed to update child", "error", err) } // Wait for final completion result, err := childRun.Get(ctx) if err != nil { return nil, fmt.Errorf("child execution failed: %w", err) } return &examplev1.ParentResponse{ ChildResult: result, Execution: execution, }, nil } ``` -------------------------------- ### Install protoc-gen-go with go install Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/install.mdx Installs the protoc-gen-go plugin using 'go install'. ```sh go install google.golang.org/protobuf/cmd/protoc-gen-go@latest ``` -------------------------------- ### Clone Example Repository Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/searchattributes.mdx Clone the repository containing the protoc-gen-go-temporal examples to your local machine. ```shell git clone https://github.com/cludden/protoc-gen-go-temporal && cd protoc-gen-go-temporal ``` -------------------------------- ### Install protoc-gen-go-temporal with go install Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/install.mdx Installs protoc-gen-go-temporal using 'go install'. Note that this method omits detailed version metadata in generated file headers. ```sh go install github.com/cludden/protoc-gen-go-temporal/cmd/protoc-gen-go_temporal@ ``` -------------------------------- ### Initialize Project and Install Dependencies Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/getting-started.mdx Create a new project directory, initialize Go modules, install the protoc-gen-go-temporal plugin, and tidy dependencies. This sets up the basic project structure. ```shell mkdir -p example/proto cd example go mod init example go get -u "github.com/cludden/protoc-gen-go-temporal@$(curl --silent https://api.github.com/repos/cludden/protoc-gen-go-temporal/releases/latest|jq -r .tag_name)" go mod tidy buf config init ``` -------------------------------- ### Workflow Run Usage Example Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/clients.mdx Example Go code demonstrating how to start, signal, query, update, and get the result of a workflow using a run handle. ```APIDOC ## Workflow Run Usage This example shows how to use the workflow run handle to interact with a running workflow. ### Steps 1. **Start Workflow**: Initiate a workflow asynchronously. 2. **Send Signal**: Send a signal to update the workflow's state. 3. **Query Workflow**: Query the workflow for its current progress. 4. **Update Workflow**: Initiate an update to the workflow's progress. 5. **Wait for Completion**: Retrieve the final result of the workflow. ### Example Code ```go package main import ( "context" "log" "time" examplev1 "path/to/gen/example/v1" "go.temporal.io/sdk/client" ) func main() { c, _ := client.Dial(client.Options{}) client := examplev1.NewExampleClient(c) ctx := context.Background() // Start workflow run, err := client.CreateFooAsync(ctx, &examplev1.CreateFooRequest{Name: "test"}) if err != nil { log.Fatal(err) } log.Printf("workflow started: %s", run.ID()) // Send signals err = run.SetFooProgress(ctx, &examplev1.SetFooProgressRequest{Progress: 25.0}) if err != nil { log.Printf("signal failed: %v", err) } // Query workflow state progress, err := run.GetFooProgress(ctx) if err != nil { log.Printf("query failed: %v", err) } else { log.Printf("current progress: %.1f%%", progress.Progress) } // Update workflow update, err := run.UpdateFooProgress(ctx, &examplev1.SetFooProgressRequest{Progress: 100.0}) if err != nil { log.Printf("update failed: %v", err) } else { log.Printf("updated progress: %.1f%%", update.Progress) } // Wait for completion result, err := run.Get(ctx) if err != nil { log.Fatalf("workflow failed: %v", err) } log.Printf("workflow completed: %s", result.String()) } ``` ``` -------------------------------- ### Install protoc-gen-go-temporal with curl Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/install.mdx Installs protoc-gen-go-temporal by piping the install script to bash. ```sh curl -L https://raw.githubusercontent.com/cludden/protoc-gen-go-temporal/main/scripts/install.sh | bash ``` -------------------------------- ### Create Namespace and Run Worker Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/xns.mdx Creates a new 'example' namespace in Temporal and starts the worker process for the cross-namespace example. This command should be run in a separate terminal after the Temporal server is running. ```sh temporal operator namespace create example go run ./examples/xns/... worker ``` -------------------------------- ### Run Search Attributes Example Worker Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/examples/searchattributes/README.md Execute the main Go program to run the worker for the search attributes example. This should be run in a separate shell after starting the Temporal service. ```go go run examples/searchattributes/main.go worker ``` -------------------------------- ### Install buf with Homebrew Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/install.mdx Installs the buf CLI tool using Homebrew. ```sh brew install bufbuild/buf/buf ``` -------------------------------- ### Shopping Cart Main Application Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/shoppingcart.mdx Go main application for the shopping cart example, setting up workers and handling command-line arguments for starting workflows, sending signals, and querying state. ```go package main import ( "context" "encoding/json" "fmt" "log" "os" "time" "github.com/urfave/cli/v2" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" v1 "github.com/cludden/protoc-gen-go-temporal/examples/shoppingcart/proto/shoppingcart/v1" "github.com/cludden/protoc-gen-go-temporal/examples/shoppingcart/workflows" ) func main() { app := &cli.App{ Name: "shoppingcart", Usage: "Temporal Shopping Cart Example", Commands: []*cli.Command{ { Name: "start", Usage: "Start workers", Action: startWorkers }, { Name: "shopping-cart-with-update-cart", Usage: "Start a shopping cart workflow with an initial update", Action: startWorkflowWithUpdateCart }, { Name: "update-cart", Usage: "Send an updateCart signal to a workflow", Action: updateCart }, { Name: "describe", Usage: "Describe a workflow", Action: describeWorkflow }, { Name: "checkout", Usage: "Checkout a workflow", Action: checkoutWorkflow }, }, } if err := app.Run(os.Args); err != nil { log.Fatal(err) } } func startWorkers(c *cli.Context) error { // TODO: Add Temporal client configuration var temporalGRPCAddress string var temporalClientOptions []client.Option // The following line can be used to connect to a Temporal instance running // in a different namespace or on a different host. // temporalGRPCAddress = "your.temporal.host:7233" // If you are running Temporal locally using the default port, you can omit // the temporalGRPCAddress parameter. // Create Temporal client. client, err := client.Dial(context.Background(), client.Options{ HostPort: temporalGRPCAddress, Namespace: "default", // Or your desired namespace // Add any other client options here, like TLS configuration. }) if err != nil { return fmt.Errorf("unable to create Temporal client: %w", err) } defer client.Close() // Create a new worker. workerOptions := worker.Options{ // TODO: Configure worker options as needed. } worker := worker.New(client, "shopping-cart-task-queue", workerOptions) // Register your workflow and activity functions. worker.RegisterFunc(workflows.ShoppingCartWorkflow) // Start the worker. if err := worker.Run(context.Background()); err != nil { return fmt.Errorf("unable to start worker: %w", err) } return nil } func startWorkflowWithUpdateCart(c *cli.Context) error { var temporalGRPCAddress string var temporalClientOptions []client.Option client, err := client.Dial(context.Background(), client.Options{ HostPort: temporalGRPCAddress, Namespace: "default", }) if err != nil { return fmt.Errorf("unable to create Temporal client: %w", err) } defer client.Close() // Parse request JSON from command line arguments. var req v1.UpdateCartRequest if c.String("req") != "" { if err := json.Unmarshal([]byte(c.String("req")), &req); err != nil { return fmt.Errorf("unable to unmarshal request JSON: %w", err) } } var updateCartInput v1.UpdateCartRequest if c.String("update-cart-input") != "" { if err := json.Unmarshal([]byte(c.String("update-cart-input")), &updateCartInput); err != nil { return fmt.Errorf("unable to unmarshal update cart input JSON: %w", err) } } workflowOptions := client.StartWorkflowOptions{ ID: "shopping-cart-workflow-" + fmt.Sprint(time.Now().UnixNano()), TaskQueue: "shopping-cart-task-queue", } // Start the workflow with an initial update. workflowRun, err := client.ExecuteWorkflow(context.Background(), workflowOptions, workflows.ShoppingCartWorkflow, map[string]int32{}) if err != nil { return fmt.Errorf("unable to execute workflow: %w", err) } // Send the initial update. updateHandle, err := client.UpdateWorkflow(context.Background(), workflowRun.GetID(), workflowRun.GetRunID(), "updateCart", updateCartInput) if err != nil { return fmt.Errorf("unable to send updateCart signal: %w", err) } // Wait for the update to complete. var updateResp v1.UpdateCartResponse if err := updateHandle.Get(context.Background(), &updateResp); err != nil { return fmt.Errorf("unable to get updateCart response: %w", err) } fmt.Printf("Workflow started with ID: %s\n", workflowRun.GetID()) fmt.Printf("Initial update response: %s\n", updateResp.Message) // Optionally, wait for the workflow to complete. // err = workflowRun.Get(context.Background(), nil) // if err != nil { // return fmt.Errorf("unable to get workflow result: %w", err) // } return nil } func updateCart(c *cli.Context) error { var temporalGRPCAddress string var temporalClientOptions []client.Option client, err := client.Dial(context.Background(), client.Options{ HostPort: temporalGRPCAddress, Namespace: "default", }) if err != nil { return fmt.Errorf("unable to create Temporal client: %w", err) } defer client.Close() workflowID := c.String("w") if workflowID == "" { return errors.New("workflow ID is required") } var input v1.UpdateCartRequest if c.String("input") != "" { if err := json.Unmarshal([]byte(c.String("input")), &input); err != nil { return fmt.Errorf("unable to unmarshal input JSON: %w", err) } } updateHandle, err := client.UpdateWorkflow(context.Background(), workflowID, "", "updateCart", input) if err != nil { return fmt.Errorf("unable to send updateCart signal: %w", err) } var resp v1.UpdateCartResponse if err := updateHandle.Get(context.Background(), &resp); err != nil { return fmt.Errorf("unable to get updateCart response: %w", err) } fmt.Printf("Update response: %s\n", resp.Message) return nil } func describeWorkflow(c *cli.Context) error { var temporalGRPCAddress string var temporalClientOptions []client.Option client, err := client.Dial(context.Background(), client.Options{ HostPort: temporalGRPCAddress, Namespace: "default", }) if err != nil { return fmt.Errorf("unable to create Temporal client: %w", err) } defer client.Close() workflowID := c.String("w") if workflowID == "" { return errors.New("workflow ID is required") } queryResult, err := client.QueryWorkflow(context.Background(), workflowID, "", "getCart") if err != nil { return fmt.Errorf("unable to query workflow: %w", err) } var resp v1.GetCartResponse if err := queryResult.Get(context.Background(), &resp); err != nil { return fmt.Errorf("unable to get query result: %w", err) } fmt.Printf("Cart contents: %v\n", resp.Items) return nil } func checkoutWorkflow(c *cli.Context) error { var temporalGRPCAddress string var temporalClientOptions []client.Option client, err := client.Dial(context.Background(), client.Options{ HostPort: temporalGRPCAddress, Namespace: "default", }) if err != nil { return fmt.Errorf("unable to create Temporal client: %w", err) } defer client.Close() workflowID := c.String("w") if workflowID == "" { return errors.New("workflow ID is required") } // Send the checkout signal. if err := client.SignalWorkflow(context.Background(), workflowID, "", "checkout", nil); err != nil { return fmt.Errorf("unable to send checkout signal: %w", err) } fmt.Printf("Checkout signal sent to workflow %s\n", workflowID) // Optionally, wait for the workflow to complete. // workflowRun, err := client.GetWorkflowHandle(context.Background(), workflowID, "") // if err != nil { // return fmt.Errorf("unable to get workflow handle: %w", err) // } // err = workflowRun.Get(context.Background(), nil) // if err != nil { // return fmt.Errorf("unable to get workflow result: %w", err) // } // fmt.Printf("Workflow %s completed successfully.\n", workflowID) return nil } ``` -------------------------------- ### Install protoc-gen-go with Homebrew Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/install.mdx Installs the protoc-gen-go plugin using Homebrew. ```sh brew install protoc-gen-go ``` -------------------------------- ### Start Local Development Server Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/README.md Starts a local development server and opens the website in a browser. Changes are reflected live without server restarts. ```bash $ yarn start ``` -------------------------------- ### Run the example worker Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/helloworld.mdx Command to run the Hello World example worker. The worker listens for tasks on the specified task queue and executes workflows and activities. ```sh go run examples/helloworld/main.go worker ``` -------------------------------- ### Implement Go Temporal Activities Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/activities.mdx Implement activities as methods on a Go struct that satisfies the generated `Activities` interface. This example shows a basic 'Hello' activity and worker setup. ```go package main import ( "context" examplev1 "path/to/gen/example/v1" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" ) type Activities struct {} func (a *Activities) Hello(ctx context.Context, input *examplev1.HelloInput) (*examplev1.HelloOutput, error) { return &examplev1.HelloOutput{}, nil } func main() { c, _ := client.Dial(client.Options{}) w := worker.New(c, examplev1.ExampleTaskQueue, worker.Options{}) // Register all example.v1.Example activities with the worker examplev1.RegisterExampleActivities(w, &Activities{}) w.Run(worker.InterruptCh()) } ``` -------------------------------- ### Start Temporal Development Server Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/xns.mdx Starts a local Temporal development server with specific configurations enabled. Ensure this command is run before starting workers or executing workflows. ```sh temporal server start-dev \ --dynamic-config-value "frontend.enableUpdateWorkflowExecution=true" \ --dynamic-config-value "frontend.enableUpdateWorkflowExecutionAsyncAccepted=true" ``` -------------------------------- ### Worker Example Activities Interface Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/reference.md Example implementation of the Activities interface for a service. ```go type ExampleActivities interface { Foo(context.Context) error Bar(context.Context, *BarRequest) error Baz(context.Context) (*BazResponse, error) Qux(context.Context, *QuxRequest) (*QuxResponse, error) } ``` -------------------------------- ### Run the example worker Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/mutex.mdx Starts the Temporal worker that listens for and executes workflows. This command should be run in a separate terminal after the Temporal server is active. ```sh go run examples/mutex/main.go worker ``` -------------------------------- ### Install Temporal CLI with curl Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/install.mdx Installs the Temporal CLI by downloading and executing an installation script. ```sh curl -sSf https://temporal.download/cli.sh | sh ``` -------------------------------- ### Install protoc-gen-go-temporal with Homebrew Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/install.mdx Use this command to install the protoc-gen-go-temporal plugin via Homebrew. ```sh brew install cludden/formula/protoc-gen-go_temporal ``` -------------------------------- ### Install buf with curl Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/install.mdx Installs the buf CLI tool by downloading the binary and making it executable. Ensure to substitute VERSION with the current released version and BIN with your desired bin directory. ```sh BIN="/usr/local/bin" && \ VERSION="1.30.0" && \ curl -sSL \ "https://github.com/bufbuild/buf/releases/download/v${VERSION}/buf-$(uname -s)-$(uname -m)" \ -o "${BIN}/buf" && \ chmod +x "${BIN}/buf" ``` -------------------------------- ### Install Temporal CLI with Homebrew Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/install.mdx Installs the Temporal CLI using Homebrew. ```sh brew install temporal ``` -------------------------------- ### Start Temporal Development Server Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/getting-started.mdx Start a local Temporal development server using the provided command. This server is used for testing and development purposes. ```sh temporal server start-dev ``` -------------------------------- ### Test Update with Start Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/testing.mdx Tests starting a workflow with an initial update and completing it with a subsequent update, verifying both intermediate and final results. ```go func TestUpdateWithStart(t *testing.T) { var suite testsuite.WorkflowTestSuite env := suite.NewTestWorkflowEnvironment() client := examplev1.NewTestExampleClient(env, &ExampleWorkflows{}, &ExampleActivities{}) ctx := context.Background() // Complete workflow with another update env.RegisterDelayedCallback(func() { finalUpdate, err := run.UpdateFooProgress(ctx, &examplev1.SetFooProgressRequest{ Progress: 1.0, }) require.NoError(t, err) require.Equal(t, examplev1.Foo_FOO_STATUS_READY, finalUpdate.Status) }, time.Second*10) // Start workflow with initial update updateResult, run, err := client.CreateFooWithUpdateFooProgress(ctx, &examplev1.CreateFooRequest{Name: "update-start-test"}, &examplev1.SetFooProgressRequest{Progress: 0.5}, ) require.NoError(t, err) // Verify initial update result require.Equal(t, float32(0.5), updateResult.GetProgress()) require.Equal(t, examplev1.Foo_FOO_STATUS_CREATING, updateResult.GetStatus()) result, err := run.Get(ctx) require.NoError(t, err) require.Equal(t, examplev1.Foo_FOO_STATUS_READY, result.GetFoo().GetStatus()) } ``` -------------------------------- ### CLI Setup with Worker V3 Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/cli.mdx Configures a CLI application with client and worker setup using the V3 CLI framework. This version utilizes context for operations and worker registration. ```go package main import ( "context" "log" "os" examplev1 "path/to/gen/example/v1" cliv3 "github.com/urfave/cli/v3" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" ) type ( Workflows struct{} Activities struct{} ) func main() { app, err := examplev1.NewExampleCliV3( examplev1.NewExampleCliV3Options(). WithClient(func(ctx context.Context, cmd *cliv3.Command) (client.Client, error) { return client.DialContext(ctx, client.Options{}) }). WithWorker(func(ctx context.Context, cmd *cliv3.Command, c client.Client) (worker.Worker, error) { w := worker.New(c, examplev1.ExampleTaskQueue, worker.Options{}) // Register workflows and activities examplev1.RegisterExampleWorkflows(w, &Workflows{}) examplev1.RegisterExampleActivities(w, &Activities{}) return w, nil }), ) if err != nil { log.Fatalf("error creating CLI: %v", err) } if err := app.Run(context.Background(), os.Args); err != nil { log.Fatal(err) } } ``` -------------------------------- ### Protobuf Schema for Example Service Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/workflows.mdx Defines the 'Example' service with a 'Hello' RPC, including input and output messages, suitable for Temporal workflows. ```protobuf syntax="proto3"; package example.v1; service Example { option (temporal.v1.service) = { task_queue: "example-v1" } // Hello returns a friendly greeting rpc Hello(HelloInput) returns (HelloOutput) { option (temporal.v1.workflow) = {}; } } message HelloInput { // Name specifies the subject to greet string name = 1; } message HelloOutput { string result = 1; } ``` -------------------------------- ### Start the codec server Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/codecserver.mdx Run the codec server using the 'codec' command. This starts an HTTP server on port 8080. ```sh go run examples/codecserver/main.go codec ``` -------------------------------- ### Go Implementation for XNS Example Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/xns.mdx The main Go file for the cross-namespace example. This code runs the worker and handles workflow execution. Ensure Temporal is set up correctly before running. ```go package main import ( "context" "log" "os" "go.temporal.io/sdk/client" "go.temporal.io/sdk/worker" "github.com/cludden/protoc-gen-go-temporal/examples/xns/xns/v1" ) func main() { if len(os.Args) < 2 { log.Fatal("expected command: worker or xns") } sc, err := client.Dial(client.Options{}) if err != nil { log.Fatal("unable to create client", err) } sswitch os.Args[1] { case "worker": w := worker.New(sc, "xns", "example") w.RegisterWorkflow(v1.ProvisionFoo) if err := w.Run(context.Background()); err != nil { log.Fatal("unable to start worker", err) } case "xns": var req v1.ProvisionFooRequest if len(os.Args) > 2 { req.Name = os.Args[2] } workflowOptions := client.StartWorkflowOptions{ ID: "provision-foo-" + req.Name, } we, err := sc.ExecuteWorkflow(context.Background(), workflowOptions, v1.ProvisionFoo, &req) if err != nil { log.Fatal("unable to execute workflow", err) } var result v1.ProvisionFooResponse if err := we.Get(context.Background(), &result); err != nil { log.Fatal("unable to get workflow result", err) } log.Printf("Workflow result: %v\n", result.Message) default: log.Fatalf("unknown command %q", os.Args[1]) } } ``` -------------------------------- ### Start Temporal with codec server Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/codecserver.mdx Start a local Temporal development server and configure it to use the codec server endpoint. This enables features like workflow update. ```sh temporal server start-dev \ --dynamic-config-value "frontend.enableUpdateWorkflowExecution=true" \ --dynamic-config-value "frontend.enableUpdateWorkflowExecutionAsyncAccepted=true" \ --ui-codec-endpoint http://localhost:8080 ``` -------------------------------- ### Selector Usage Example Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/child-workflows.mdx Example demonstrating how to use the selector to manage child workflows, including waiting for them to start and complete. ```APIDOC ## Selector Usage Example ### Description This example shows how to use `workflow.NewSelector` to manage multiple child workflows. It demonstrates starting child workflows, waiting for them to start using `SelectStart`, and then waiting for their completion using `Select`. ### Code Example ```go func (w *ParentWorkflow) Execute(ctx workflow.Context) error { // Start multiple child workflows child1, _ := examplev1.CreateFooChildAsync(ctx, &examplev1.CreateFooRequest{Name: "child-1"}) child2, _ := examplev1.CreateFooChildAsync(ctx, &examplev1.CreateFooRequest{Name: "child-2"}) selector := workflow.NewSelector(ctx) var completedChildren int // Wait for both children to start child1.SelectStart(selector, func(run examplev1.CreateFooChildRun) { workflow.GetLogger(ctx).Info("child-1 started") }) child2.SelectStart(selector, func(run examplev1.CreateFooChildRun) { workflow.GetLogger(ctx).Info("child-2 started") }) // Wait for both to start selector.Select(ctx) selector.Select(ctx) // Now wait for completion child1.Select(selector, func(run examplev1.CreateFooChildRun) { result, err := run.Get(ctx) if err != nil { workflow.GetLogger(ctx).Error("child-1 failed", "error", err) } completedChildren++ }) child2.Select(selector, func(run examplev1.CreateFooChildRun) { result, err := run.Get(ctx) if err != nil { workflow.GetLogger(ctx).Error("child-2 failed", "error", err) } completedChildren++ }) // Wait for both to complete for completedChildren < 2 { selector.Select(ctx) } return nil } ``` ``` -------------------------------- ### Basic CLI Setup V2 Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/cli.mdx Initializes a CLI application with basic client configuration using the V2 CLI framework. Ensure necessary imports are present. ```go package main import ( "log" "os" examplev1 "path/to/gen/example/v1" "go.temporal.io/sdk/client" ) func main() { // Create CLI application with client factory app, err := examplev1.NewExampleCli( examplev1.NewExampleCliOptions(). WithClient(func(cmd *cli.Context) (client.Client, error) { return client.Dial(client.Options{}) }), ) if err != nil { log.Fatalf("error creating CLI: %v", err) } // Run CLI application if err := app.Run(os.Args); err != nil { log.Fatal(err) } } ``` -------------------------------- ### Basic CLI Setup V3 Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/cli.mdx Initializes a CLI application with basic client configuration using the V3 CLI framework. This version uses context for client dialing. ```go package main import ( "context" "log" "os" cliv3 "github.com/urfave/cli/v3" examplev1 "path/to/gen/example/v1" "go.temporal.io/sdk/client" ) func main() { // Create CLI application with client factory app, err := examplev1.NewExampleCliV3( examplev1.NewExampleCliV3Options(). WithClient(func(ctx context.Context, cmd *cliv3.Command) (client.Client, error) { return client.DialContext(ctx, client.Options{}) }), ) if err != nil { log.Fatalf("error creating CLI: %v", err) } // Run CLI application if err := app.Run(context.Background(), os.Args); err != nil { log.Fatal(err) } } ``` -------------------------------- ### Client Initialization with Options Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/clients.mdx Initializes a Temporal client with options and then creates a generated client instance with additional configurations, including structured logging. This allows for more customized client behavior. ```go package main import ( "log" "log/slog" examplev1 "path/to/gen/example/v1" "go.temporal.io/sdk/client" ) func main() { // Initialize Temporal client with options c, err := client.Dial(client.Options{}) if err != nil { log.Fatalf("error initializing temporal client: %v", err) } defer c.Close() // Initialize generated client with structured logging logger := slog.Default() client := examplev1.NewExampleClientWithOptions(c, client.Options{ Namespace: "example-namespace", }, examplev1.NewExampleClientOptions().WithLogger(logger)) // Use client... } ``` -------------------------------- ### Start Workflow with Initial Update Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/clients.mdx This Go code starts a workflow and atomically applies an initial update. It returns the update result and a workflow run handle. Ensure correct imports and client initialization. ```go package main import ( "context" "log" examplev1 "path/to/gen/example/v1" "go.temporal.io/sdk/client" ) func main() { c, _ := client.Dial(client.Options{}) client := examplev1.NewExampleClient(c) ctx := context.Background() // Start workflow with initial update updateResult, workflowRun, err := client.CreateFooWithUpdateFooProgress(ctx, &examplev1.CreateFooRequest{Name: "test"}, &examplev1.SetFooProgressRequest{Progress: 50.0}, ) if err != nil { log.Fatalf("update-with-start failed: %v", err) } log.Printf("workflow started with initial update: %s", updateResult.String()) log.Printf("workflow ID: %s", workflowRun.ID()) // Continue with workflow execution finalResult, err := workflowRun.Get(ctx) if err != nil { log.Fatalf("workflow execution failed: %v", err) } log.Printf("workflow completed: %s", finalResult.String()) } ``` -------------------------------- ### example.xns.v1.Xns.ProvisionFoo Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/proto/example/xns/v1/README.md ProvisionFoo starts the process of creating a new foo resource. It takes a ProvisionFooRequest and returns a ProvisionFooResponse. ```APIDOC ## ProvisionFoo ### Description Starts the process of creating a new foo resource. ### Method POST ### Endpoint /example.xns.v1.Xns/ProvisionFoo ### Parameters #### Request Body - **name** (string) - Required - unique foo name ### Response #### Success Response (200) - **foo** (example.xns.v1.Foo) - The created foo resource. ### Defaults - id: provision-foo/${! name.slug() } - id_reuse_policy: WORKFLOW_ID_REUSE_POLICY_UNSPECIFIED ``` -------------------------------- ### Go CLI Application Setup Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/queries.mdx Initializes and runs a standalone Temporal CLI application generated by protoc-gen-go-temporal. ```go package main import ( "log" "os" examplev1 "path/to/gen/example/v1" ) func main() { app, err := examplev1.NewExampleCLI() if err != nil { log.Fatalf("error initializing cli: %v", err) } if err := app.Run(os.Args); err != nil { log.Fatal(err) } } ``` -------------------------------- ### CLI Output Examples Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/cli.mdx Examples of successful workflow execution, signal confirmation, asynchronous workflow start identifiers, and worker startup messages. Output is typically JSON formatted for structured data. ```sh # Successful workflow execution (JSON formatted) $ example create-foo --name "test" { "foo": { "name": "test", "status": "FOO_STATUS_READY" } } # Successful signal (simple confirmation) $ example set-foo-progress --workflow-id "test" --progress 50 signal sent successfully # Async workflow start (workflow identifiers) $ example create-foo --name "test" --detach workflow started successfully workflow_id: create-foo/test run_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890 # Worker startup (status updates) $ example worker starting worker... worker started successfully press Ctrl+C to stop ``` -------------------------------- ### example.xns.v1.Example.CreateFoo Workflow Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/basic.md Creates a new foo operation. ```APIDOC ## POST /example.xns.v1.Example/CreateFoo ### Description CreateFoo creates a new foo operation. ### Method POST ### Endpoint /example.xns.v1.Example/CreateFoo ### Request Body - **name** (string) - Required - Unique foo name. ### Request Example ```json { "name": "my-new-foo" } ``` ### Response #### Success Response (200) - **foo** (object) - The created foo object. - **foo.id** (string) - The ID of the foo. - **foo.name** (string) - The name of the foo. - **foo.status** (string) - The status of the foo. #### Response Example ```json { "foo": { "id": "create-foo-my-new-foo", "name": "my-new-foo", "status": "PENDING" } } ``` ### Defaults - **execution_timeout**: `1 hour` - **id**: `create-foo/${! name.slug() }` - **id_reuse_policy**: `WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE` - **xns.heartbeat_interval**: `10 seconds` - **xns.heartbeat_timeout**: `20 seconds` - **xns.start_to_close_timeout**: `1 hour 30 seconds` ``` -------------------------------- ### Basic Client Initialization Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/guides/clients.mdx Initializes a Temporal client and then uses it to create a generated client instance. Ensure the Temporal client is properly closed using defer. ```go package main import ( "log" examplev1 "path/to/gen/example/v1" "go.temporal.io/sdk/client" ) func main() { // Initialize Temporal client c, err := client.Dial(client.Options{}) if err != nil { log.Fatalf("error initializing temporal client: %v", err) } defer c.Close() // Initialize generated client client := examplev1.NewExampleClient(c) // Use client... } ``` -------------------------------- ### CLI Usage for Temporal Workflows Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/about.mdx Examples of using the generated CLI application to interact with Temporal workflows. Demonstrates starting a workflow, sending signals, and querying status. ```bash go run main.go -h ``` ```bash go run main.go create-foo -d --name test ``` ```bash go run main.go set-foo-progress -w create-foo/test --progress 5.7 ``` ```json go run main.go get-foo-progress -w create-foo/test ``` ```json go run main.go update-foo-progress -w create-foo/test --progress 100 ``` ```json go run main.go get-foo-progress -w create-foo/test ``` -------------------------------- ### Hello World Main Application Source: https://github.com/cludden/protoc-gen-go-temporal/blob/main/docs/docs/examples/helloworld.mdx The main entry point for the Hello World Temporal example. This Go code handles command-line arguments for running the worker or executing workflows. ```go package main import ( "context" "fmt" "log" "os" "github.com/cludden/protoc-gen-go-temporal/examples/helloworld/internal" "github.com/cludden/protoc-gen-go-temporal/pkg/client" "github.com/cludden/protoc-gen-go-temporal/pkg/worker" "go.temporal.io/sdk/workflow" helloworldv1 "google.golang.org/protobuf/proto" ) func main() { if len(os.Args) < 2 { fmt.Println("Usage: main ") os.Exit(1) } svc, err := client.New("localhost:7233") if err != nil { log.Fatalf("unable to create Temporal client: %v", err) } sswitch os.Args[1] { case "worker": worker.Run(svc, "helloworld-task-queue", internal.NewGreeterWorkflow(), internal.NewGreeterActivity()) // Register workflow and activity case "hello": if len(os.Args) < 4 { fmt.Println("Usage: main hello --name ") os.Exit(1) } name := os.Args[3] var resp helloworldv1.SayHelloResponse err := svc.ExecuteWorkflow(context.Background(), "helloworld-task-queue", internal.GreeterWorkflow.SayHello, name, client.WorkflowOptions{ID: fmt.Sprintf("hello/%s", name), StartToCloseTimeout: 10000000000}) if err != nil { log.Fatalf("unable to execute workflow: %v", err) } log.Printf("Workflow executed successfully. Result: %v\n", resp.GetMessage()) case "goodbye": if len(os.Args) < 4 { fmt.Println("Usage: main goodbye -w ") os.Exit(1) } workflowID := os.Args[3] var resp helloworldv1.GoodbyeResponse err := svc.ExecuteWorkflow(context.Background(), "helloworld-task-queue", internal.GreeterWorkflow.Goodbye, workflowID, client.WorkflowOptions{ID: fmt.Sprintf("goodbye/%s", workflowID), StartToCloseTimeout: 10000000000}) if err != nil { log.Fatalf("unable to execute workflow: %v", err) } log.Printf("Workflow executed successfully. Result: %v\n", resp.GetMessage()) default: fmt.Println("Unknown command") os.Exit(1) } } ```