### Start Temporal Workflow Starter
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
This Go code demonstrates how to create a Temporal client, configure workflow options including ID, task queue, and timeouts, and then execute a workflow. It also shows how to retrieve the workflow result.
```go
package main
import (
"context"
"log"
"time"
"github.com/google/uuid"
"go.temporal.io/sdk/client"
"myproject/workflows"
)
func main() {
// Create Temporal client
c, err := client.Dial(client.Options{})
if err != nil {
log.Fatalln("Unable to create client", err)
}
defer c.Close()
// Configure workflow options
workflowOptions := client.StartWorkflowOptions{
ID: uuid.New().String(),
TaskQueue: "my-task-queue",
WorkflowTaskTimeout: 10 * time.Minute,
}
// Execute workflow
we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, workflows.MyDAGWorkflow, "input-data")
if err != nil {
log.Fatalln("Unable to execute workflow", err)
}
log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID())
// Wait for result
var result string
err = we.Get(context.Background(), &result)
if err != nil {
log.Fatalln("Unable to get workflow result", err)
}
log.Println("Workflow result:", result)
}
```
--------------------------------
### Set Up Temporal Worker
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
This Go code sets up a Temporal worker, registers workflows and activities, and starts the worker listening on a specified task queue. Ensure Temporal client and necessary workflow/activity packages are imported.
```go
package main
import (
"log"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"
"myproject/workflows"
)
func main() {
// Create Temporal client
c, err := client.Dial(client.Options{})
if err != nil {
log.Fatalln("Unable to create client", err)
}
defer c.Close()
// Create worker listening on task queue
w := worker.New(c, "my-task-queue", worker.Options{})
// Register workflows
w.RegisterWorkflow(workflows.MyDAGWorkflow)
// Register all activities used in the workflows
w.RegisterActivity(workflows.ProcessAActivity)
w.RegisterActivity(workflows.ProcessBActivity)
w.RegisterActivity(workflows.MergeActivity)
// Start the worker
err = w.Run(worker.InterruptCh())
if err != nil {
log.Fatalln("Unable to start worker", err)
}
}
```
--------------------------------
### Install TemporalGraph Go Library
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/README.md
Use this command to add the TemporalGraph library to your Go project dependencies.
```bash
go get github.com/Nickqiaoo/temporalgraph
```
--------------------------------
### Start Temporal Server
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Initiates the Temporal development server. This command should be run in a separate terminal before starting the worker or executing workflows.
```bash
temporal server start-dev
```
--------------------------------
### Create Basic Sequential Workflow with Temporal Graph
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Demonstrates how to create a type-safe sequential workflow using Temporal Graph. This example defines activities, configures activity options, adds nodes and edges to form a DAG, compiles the graph, and invokes the workflow.
```go
package main
import (
"context"
"time"
"go.temporal.io/sdk/temporal"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
// Define your activities
func ProcessActivity(ctx context.Context, input string) (string, error) {
return "processed_" + input, nil
}
func ValidateActivity(ctx context.Context, input string) (string, error) {
return "validated_" + input, nil
}
// Create a sequential workflow: START -> process -> validate -> END
func SequentialWorkflow(ctx workflow.Context, input string) (string, error) {
// Create typed graph with string input and string output
g := compose.NewGraph[string, string]()
// Configure activity options
activityOptions := workflow.ActivityOptions{
StartToCloseTimeout: 10 * time.Minute,
RetryPolicy: &temporal.RetryPolicy{
InitialInterval: time.Second,
BackoffCoefficient: 2.0,
MaximumInterval: 100 * time.Second,
MaximumAttempts: 3,
},
}
// Add activity nodes
if err := g.AddNode("process", ProcessActivity, input, activityOptions); err != nil {
return "", err
}
if err := g.AddNode("validate", ValidateActivity, input, activityOptions); err != nil {
return "", err
}
// Define the DAG structure with edges
if err := g.AddEdge(compose.START, "process"); err != nil {
return "", err
}
if err := g.AddEdge("process", "validate"); err != nil {
return "", err
}
if err := g.AddEdge("validate", compose.END); err != nil {
return "", err
}
// Compile the graph into a runnable
runner, err := g.Compile(ctx)
if err != nil {
return "", err
}
// Execute the workflow
return runner.Invoke(ctx, input)
}
// Output: "validated_processed_"
```
--------------------------------
### Run Worker
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Starts the Temporal worker. Navigate to the worker directory and run this command in a separate terminal after the Temporal server has started.
```bash
cd example/helloworld/worker
go run main.go
```
--------------------------------
### Build All Packages
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Use this command to build all packages within the project. Ensure you are in the project's root directory.
```bash
go build ./...
```
--------------------------------
### Configure Nodes with Options
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Configure individual nodes with display names, input/output key mappings for map-based data flow. Use `WithNodeName`, `WithInputKey`, and `WithOutputKey` to customize node behavior.
```go
package main
import (
"context"
"time"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
func EnrichActivity(ctx context.Context, in string) (string, error) {
return "enriched_" + in, nil
}
func TransformActivity(ctx context.Context, in string) (string, error) {
return "transformed_" + in, nil
}
func MergeMapActivity(ctx context.Context, in map[string]any) (string, error) {
enriched := in["enriched"].(string)
transformed := in["transformed"].(string)
return enriched + "+" + transformed, nil
}
// Configure nodes with various options
func NodeOptionsWorkflow(ctx workflow.Context, input string) (string, error) {
g := compose.NewGraph[string, string]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
// Add nodes with display names and output keys
if err := g.AddNode("enrich", EnrichActivity, input, ao,
compose.WithNodeName("Data Enrichment"), // Display name for debugging
compose.WithOutputKey("enriched"), // Output will be wrapped as {"enriched": }
); err != nil {
return "", err
}
if err := g.AddNode("transform", TransformActivity, input, ao,
compose.WithNodeName("Data Transform"),
compose.WithOutputKey("transformed"),
); err != nil {
return "", err
}
// Merge receives map[string]any with both keys
if err := g.AddNode("merge", MergeMapActivity, make(map[string]any), ao,
compose.WithNodeName("Merge Results"),
); err != nil {
return "", err
}
// Wire the graph
if err := g.AddEdge(compose.START, "enrich"); err != nil {
return "", err
}
if err := g.AddEdge(compose.START, "transform"); err != nil {
return "", err
}
if err := g.AddEdge("enrich", "merge"); err != nil {
return "", err
}
if err := g.AddEdge("transform", "merge"); err != nil {
return "", err
}
if err := g.AddEdge("merge", compose.END); err != nil {
return "", err
}
runner, err := g.Compile(ctx)
if err != nil {
return "", err
}
return runner.Invoke(ctx, input)
}
// Output: "enriched_+transformed_"
```
--------------------------------
### Execute Workflow
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Runs the workflow starter. Navigate to the starter directory and execute this command in a separate terminal after the worker is running.
```bash
cd example/helloworld/starter
go run main.go
```
--------------------------------
### Workflow with Dependency Types
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Demonstrates controlling execution order and data flow between activities using AddDependency and AddInputWithOptions. Use AddDependency for execution-only dependencies and WithNoDirectDependency for data flow without direct execution dependencies.
```go
package main
import (
"context"
"time"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
func InitActivity(ctx context.Context, in string) (string, error) {
return "initialized", nil
}
func ProcessActivity(ctx context.Context, in string) (string, error) {
return "processed_" + in, nil
}
func LogActivity(ctx context.Context, in string) (string, error) {
return "logged", nil
}
// Workflow with different dependency types
func DependencyWorkflow(ctx workflow.Context, input string) (string, error) {
wf := compose.NewWorkflow[string, string]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
// Init must complete before process starts, but no data flows between them
wf.AddNode("init", InitActivity, nil, ao).AddInput(compose.START)
// Process depends on init completing (execution dependency only)
wf.AddNode("process", ProcessActivity, nil, ao).
AddInput(compose.START). // Data comes from START
AddDependency("init") // But waits for init to complete
// Log receives data from START but without requiring START to complete
// (useful when START is a passthrough)
wf.AddNode("log", LogActivity, nil, ao).
AddInputWithOptions(compose.START, nil, compose.WithNoDirectDependency())
wf.End().AddInput("process")
r, err := wf.Compile(ctx)
if err != nil {
return "", err
}
return r.Invoke(ctx, input)
}
```
--------------------------------
### Parallel Execution with Output Keys
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Execute multiple activities concurrently and collect their outputs using named keys for downstream merging. Ensure activities are correctly defined and their outputs are mapped to the expected keys.
```go
package main
import (
"context"
"time"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
func ProcessAActivity(ctx context.Context, input string) (string, error) {
return "A_" + input, nil
}
func ProcessBActivity(ctx context.Context, input string) (string, error) {
return "B_" + input, nil
}
func MergeActivity(ctx context.Context, inputs map[string]any) (string, error) {
var a, b string
if v, ok := inputs["processA"].(string); ok {
a = v
}
if v, ok := inputs["processB"].(string); ok {
b = v
}
return "merged_" + a + "_" + b, nil
}
// Parallel workflow: START -> (processA, processB) -> merge -> END
func ParallelDAGWorkflow(ctx workflow.Context, input string) (string, error) {
g := compose.NewGraph[string, string]()
ao := workflow.ActivityOptions{
StartToCloseTimeout: 10 * time.Minute,
}
// Add parallel nodes with output keys to collect results
if err := g.AddNode("processA", ProcessAActivity, input, ao, compose.WithOutputKey("processA")); err != nil {
return "", err
}
if err := g.AddNode("processB", ProcessBActivity, input, ao, compose.WithOutputKey("processB")); err != nil {
return "", err
}
// Merge node receives map[string]any with keys "processA" and "processB"
if err := g.AddNode("merge", MergeActivity, make(map[string]any), ao); err != nil {
return "", err
}
// Both parallel branches start from START
if err := g.AddEdge(compose.START, "processA"); err != nil {
return "", err
}
if err := g.AddEdge(compose.START, "processB"); err != nil {
return "", err
}
// Both converge into merge
if err := g.AddEdge("processA", "merge"); err != nil {
return "", err
}
if err := g.AddEdge("processB", "merge"); err != nil {
return "", err
}
if err := g.AddEdge("merge", compose.END); err != nil {
return "", err
}
runner, err := g.Compile(ctx)
if err != nil {
return "", err
}
return runner.Invoke(ctx, input)
}
// Output: "merged_A__B_"
```
--------------------------------
### Test Temporal Workflow with Mocked Activity
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
This Go code demonstrates unit testing a Temporal workflow using the test suite. It mocks an activity using testify/mock and asserts the workflow completion and result.
```go
package compose_test
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.temporal.io/sdk/testsuite"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
func MockProcess(ctx context.Context, in string) (string, error) {
return "processed_" + in, nil
}
func TestWorkflow(ctx workflow.Context, input string) (string, error) {
g := compose.NewGraph[string, string]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
_ = g.AddNode("process", MockProcess, input, ao)
_ = g.AddEdge(compose.START, "process")
_ = g.AddEdge("process", compose.END)
runner, _ := g.Compile(ctx)
return runner.Invoke(ctx, input)
}
func TestSequentialWorkflow(t *testing.T) {
testSuite := &testsuite.WorkflowTestSuite{}
env := testSuite.NewTestWorkflowEnvironment()
// Mock the activity
env.OnActivity(MockProcess, mock.Anything, "test").Return("processed_test", nil)
// Execute workflow
env.ExecuteWorkflow(TestWorkflow, "test")
// Assertions
require.True(t, env.IsWorkflowCompleted())
require.NoError(t, env.GetWorkflowError())
var result string
require.NoError(t, env.GetWorkflowResult(&result))
require.Equal(t, "processed_test", result)
}
```
--------------------------------
### Create Typed Graph
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Initializes a new typed graph with specified input and output types. This is the first step in building a workflow graph.
```go
compose.NewGraph[InputType, OutputType]()
```
--------------------------------
### Run All Tests
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Execute all tests across all packages in the project. The -v flag provides verbose output.
```bash
go test -v ./...
```
--------------------------------
### Update Dependencies
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Synchronize and update project dependencies according to go.mod and go.sum files.
```bash
go mod tidy
```
--------------------------------
### Compile Graph
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Compiles the constructed graph into a runnable workflow. This step validates the graph structure and prepares it for execution.
```go
g.Compile(ctx)
```
--------------------------------
### Declarative Field Mapping Workflow in Go
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
This Go workflow demonstrates declarative field mapping for data dependencies. It uses `AddInput` with `FromField` and `ToField` to manage data flow between activities and the workflow's input/output structures. Ensure the `compose` package is available.
```go
package main
import (
"context"
"time"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
type InputStruct struct {
Name string
Counter int
}
type OutputStruct struct {
ProcessedName string
DoubledCount int
}
func ProcessNameActivity(ctx context.Context, name string) (string, error) {
return "processed_" + name, nil
}
func DoubleCountActivity(ctx context.Context, count int) (int, error) {
return count * 2, nil
}
// Workflow with field mappings: extract fields from input, process, and combine into output
func FieldMappingWorkflow(ctx workflow.Context, input *InputStruct) (*OutputStruct, error) {
wf := compose.NewWorkflow[*InputStruct, *OutputStruct]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
// Add nodes and declare their input dependencies with field mappings
// FromField extracts a single field from predecessor output
wf.AddNode("processName", ProcessNameActivity, nil, ao).
AddInput(compose.START, compose.FromField("Name"))
wf.AddNode("doubleCount", DoubleCountActivity, nil, ao).
AddInput(compose.START, compose.FromField("Counter"))
// End node combines outputs from multiple nodes using ToField
// ToField maps entire predecessor output to a specific field in successor
wf.End().
AddInput("processName", compose.ToField("ProcessedName")),
AddInput("doubleCount", compose.ToField("DoubledCount"))
r, err := wf.Compile(ctx)
if err != nil {
return nil, err
}
return r.Invoke(ctx, input)
}
// Input: {Name: "test", Counter: 5}
// Output: {ProcessedName: "processed_test", DoubledCount: 10}
```
--------------------------------
### Compile Workflow with Named Graph
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Configure graph compilation with options like naming for debugging and logging purposes. Use `WithGraphName` to assign a name to the compiled graph.
```go
package main
import (
"context"
"time"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
func SimpleActivity(ctx context.Context, in string) (string, error) {
return "result_" + in, nil
}
// Compile with named graph for better debugging
func NamedGraphWorkflow(ctx workflow.Context, input string) (string, error) {
g := compose.NewGraph[string, string]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
if err := g.AddNode("process", SimpleActivity, input, ao); err != nil {
return "", err
}
if err := g.AddEdge(compose.START, "process"); err != nil {
return "", err
}
if err := g.AddEdge("process", compose.END); err != nil {
return "", err
}
// Compile with a named graph for debugging
runner, err := g.Compile(ctx, compose.WithGraphName("MyProcessingPipeline"))
if err != nil {
return "", err
}
return runner.Invoke(ctx, input)
}
```
--------------------------------
### Run Tests for Specific Package
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Run tests for a particular package, such as 'compose' or 'internal/safe'. The -v flag provides verbose output.
```bash
go test -v ./compose
```
```bash
go test -v ./internal/safe
```
--------------------------------
### Execute Runnable Workflow
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Invokes the compiled workflow with the provided input. This function executes the workflow defined by the graph.
```go
runner.Invoke(ctx, input)
```
--------------------------------
### Map Predecessor Output to Successor Field
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Maps the entire output of a predecessor node to a specific field in the successor node's input.
```go
ToField(field)
```
--------------------------------
### Complex Field Mapping Workflow
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Use this workflow to perform complex field mapping with nested paths, extracting user data and formatting it into a final output structure.
```go
package main
import (
"context"
"fmt"
"time"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
type UserInput struct {
User struct {
Profile struct {
Name string
Email string
}
}
}
type ProcessedData struct {
Field1 string
Field2 string
}
type FinalOutput struct {
Result string
}
func ExtractUserActivity(ctx context.Context, in *UserInput) (*ProcessedData, error) {
return &ProcessedData{
Field1: in.User.Profile.Name,
Field2: in.User.Profile.Email,
}, nil
}
func FormatActivity(ctx context.Context, in *ProcessedData) (string, error) {
return fmt.Sprintf("%s <%s>", in.Field1, in.Field2), nil
}
// Complex field mapping with nested paths
func AdvancedMappingWorkflow(ctx workflow.Context, input *UserInput) (*FinalOutput, error) {
wf := compose.NewWorkflow[*UserInput, *FinalOutput]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
// Extract user data
wf.AddNode("extract", ExtractUserActivity, nil, ao).
AddInput(compose.START)
// Format the extracted data
wf.AddNode("format", FormatActivity, nil, ao).
AddInput("extract")
// Map format output to result field
wf.End().AddInput("format", compose.ToField("Result"))
r, err := wf.Compile(ctx)
if err != nil {
return nil, err
}
return r.Invoke(ctx, input)
}
```
--------------------------------
### Map Predecessor Field to Successor Input
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Maps a specific field from a predecessor node's output to the entire input of the successor node.
```go
FromField(field)
```
--------------------------------
### Multi-Field Mapping with MapFields
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Utilize MapFields to map specific fields between workflow nodes, demonstrating renaming from 'source_key' to 'dest_key' and then to 'final_key'.
```go
// Using MapFields to map specific fields between nodes
func MultiFieldMappingWorkflow(ctx workflow.Context, input map[string]any) (map[string]any, error) {
wf := compose.NewWorkflow[map[string]any, map[string]any]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
// Identity activity for demonstration
identityActivity := func(ctx context.Context, in map[string]any) (map[string]any, error) {
return in, nil
}
// MapFields maps a specific field from predecessor to successor
wf.AddNode("transform", identityActivity, nil, ao).
AddInput(compose.START, compose.MapFields("source_key", "dest_key"))
wf.End().AddInput("transform", compose.MapFields("dest_key", "final_key"))
r, err := wf.Compile(ctx)
if err != nil {
return nil, err
}
return r.Invoke(ctx, input)
}
// Input: {"source_key": "value"}
// Output: {"final_key": "value"}
```
--------------------------------
### Compose Complex Workflows with Subgraphs
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Nest graphs as nodes within parent graphs to create modular and reusable workflows. Ensure activity options are correctly configured for the subgraph.
```go
package main
import (
"context"
"time"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
type StructA struct {
Field1 string
Field2 int
}
type StructB struct {
Field1 string
Field2 int
}
func StringToStructBActivity(ctx context.Context, input string) (*StructB, error) {
return &StructB{Field1: input, Field2: 42}, nil
}
func ProcessStructBActivity(ctx context.Context, in *StructB) (string, error) {
return in.Field1 + "_processed", nil
}
// Create a reusable subgraph
func createSubGraph() *compose.Graph[string, *StructB] {
subGraph := compose.NewGraph[string, *StructB]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
_ = subGraph.AddNode("transform", StringToStructBActivity, nil, ao)
_ = subGraph.AddEdge(compose.START, "transform")
_ = subGraph.AddEdge("transform", compose.END)
return subGraph
}
// Main workflow using subgraph
func SubGraphWorkflow(ctx workflow.Context, input *StructA) (string, error) {
wf := compose.NewWorkflow[*StructA, string]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
// Create and add subgraph as a node
subGraph := createSubGraph()
// AddGraphNode embeds the subgraph; FromField extracts Field1 from input
wf.AddGraphNode("subgraphNode", subGraph).
AddInput(compose.START, compose.FromField("Field1"))
// Process the subgraph output
wf.AddNode("finalProcess", ProcessStructBActivity, nil, ao).
AddInput("subgraphNode")
wf.End().AddInput("finalProcess")
r, err := wf.Compile(ctx)
if err != nil {
return "", err
}
return r.Invoke(ctx, input)
}
// Input: {Field1: "test", Field2: 10}
// Subgraph produces: {Field1: "test", Field2: 42}
// Output: "test_processed"
```
--------------------------------
### Conditional Branching with NewGraphBranch
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Create dynamic execution paths in a workflow based on runtime conditions. The branch condition function determines the next node to execute, allowing for flexible control flow.
```go
package main
import (
"context"
"time"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
func IdentityActivity(ctx context.Context, in string) (string, error) {
return in, nil
}
func HighPriorityActivity(ctx context.Context, in string) (string, error) {
return "HIGH_" + in, nil
}
func LowPriorityActivity(ctx context.Context, in string) (string, error) {
return "LOW_" + in, nil
}
// Branching workflow: START -> decide -> branch{highPriority|lowPriority} -> END
func BranchWorkflow(ctx workflow.Context, input string) (string, error) {
g := compose.NewGraph[string, string]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
// Decision node passes through the input
if err := g.AddNode("decide", IdentityActivity, input, ao); err != nil {
return "", err
}
// Branch end nodes
if err := g.AddNode("highPriority", HighPriorityActivity, input, ao); err != nil {
return "", err
}
if err := g.AddNode("lowPriority", LowPriorityActivity, input, ao); err != nil {
return "", err
}
// Wire START to decision node
if err := g.AddEdge(compose.START, "decide"); err != nil {
return "", err
}
// Define branch condition - routes based on input content
condition := func(ctx workflow.Context, in string) (string, error) {
if in == "urgent" {
return "highPriority", nil
}
return "lowPriority", nil
}
// Create branch with possible end nodes
branch := compose.NewGraphBranch(condition, map[string]bool{
"highPriority": true,
"lowPriority": true,
})
// Add branch after decision node
if err := g.AddBranch("decide", branch); err != nil {
return "", err
}
// Both branch paths lead to END
if err := g.AddEdge("highPriority", compose.END); err != nil {
return "", err
}
if err := g.AddEdge("lowPriority", compose.END); err != nil {
return "", err
}
runner, err := g.Compile(ctx)
if err != nil {
return "", err
}
return runner.Invoke(ctx, input)
}
// Input "urgent" -> Output: "HIGH_urgent"
// Input "normal" -> Output: "LOW_normal"
```
--------------------------------
### Inject Static Values with SetStaticValue
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Inject constant values into node inputs at runtime without requiring predecessor nodes. Use SetStaticValue to provide values for specific fields.
```go
package main
import (
"context"
"time"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
type ConfiguredInput struct {
Data string
Config string
Mode int
}
func ConfiguredActivity(ctx context.Context, in *ConfiguredInput) (string, error) {
return in.Data + "_" + in.Config + "_mode" + string(rune('0'+in.Mode)), nil
}
// Workflow with static values injected into node inputs
func StaticValueWorkflow(ctx workflow.Context, input string) (string, error) {
wf := compose.NewWorkflow[string, string]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
// Add node with static values for Config and Mode fields
wf.AddNode("process", ConfiguredActivity, nil, ao).
AddInput(compose.START, compose.ToField("Data")),
SetStaticValue(compose.FieldPath{"Config"}, "production"),
SetStaticValue(compose.FieldPath{"Mode"}, 1)
wf.End().AddInput("process")
r, err := wf.Compile(ctx)
if err != nil {
return "", err
}
return r.Invoke(ctx, input)
}
// Input: "mydata"
// Activity receives: {Data: "mydata", Config: "production", Mode: 1}
// Output: "mydata_production_mode1"
```
--------------------------------
### Map Specific Fields Between Nodes
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Maps a specific field from a predecessor's output to a specific field in the successor's input.
```go
MapFields(from, to)
```
--------------------------------
### Workflow with Conditional Branching
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Implements conditional branching within a workflow using the AddBranch API. This allows for declarative definition of different execution paths based on runtime conditions.
```go
package main
import (
"context"
"time"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
func StringConcatActivity(ctx context.Context, in string) (string, error) {
return in + "_" + in, nil
}
func MapIdentityActivity(ctx context.Context, in map[string]any) (map[string]any, error) {
return in, nil
}
// Workflow with branch: conditionally execute "concat" or skip to END
func WorkflowBranchExample(ctx workflow.Context, input string) (map[string]any, error) {
wf := compose.NewWorkflow[string, map[string]any]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
// Concat activity with indirect dependency (no direct execution dependency from START)
wf.AddNode("concat", StringConcatActivity, nil, ao).
AddInputWithOptions(compose.START, nil, compose.WithNoDirectDependency())
// Decision node that determines the branch
wf.AddNode("decision", MapIdentityActivity, nil, ao).
AddInput(compose.START, compose.ToField(compose.START))
// Branch condition: if input is "hello", go to concat; otherwise go to END
branch := compose.NewGraphBranch(func(ctx workflow.Context, in map[string]any) (string, error) {
if in[compose.START] == "hello" {
return "concat", nil
}
return compose.END, nil
}, map[string]bool{
"concat": true,
compose.END: true,
})
wf.AddBranch("decision", branch)
// End receives from concat and from START (with no direct dependency)
wf.End().
AddInput("concat", compose.ToField("concat")),
AddInputWithOptions(compose.START, []*compose.FieldMapping{compose.ToField(compose.START)}, compose.WithNoDirectDependency())
r, err := wf.Compile(ctx)
if err != nil {
return nil, err
}
return r.Invoke(ctx, input)
}
// Input "hello" -> Output: {"concat": "hello_hello", "start": "hello"}
// Input "world" -> Output: {"start": "world"}
```
--------------------------------
### Add Node to Graph
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Adds a new node to the graph, associating it with a unique key, an activity, input data, and optional configuration.
```go
g.AddNode(key, activity, input, options)
```
--------------------------------
### Field Mapping for Nested Access
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Utilizes field paths to support mapping for nested struct fields or map keys within the data flow.
```go
FromFieldPath/ToFieldPath
```
--------------------------------
### Connect Nodes with Edges
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Defines the data flow between nodes by creating edges and specifying field mappings for type-safe data transfer.
```go
g.AddEdge(fromKey, toKey, fieldMappings...)
```
--------------------------------
### Nested Field Access with FieldPaths
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
Employ MapFieldPaths for nested struct access, mapping a deeply nested field like 'user.profile.name' to a target field 'response.userName'.
```go
// Using FieldPaths for nested struct access
func NestedFieldPathWorkflow(ctx workflow.Context, input map[string]any) (map[string]any, error) {
wf := compose.NewWorkflow[map[string]any, map[string]any]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
identityActivity := func(ctx context.Context, in map[string]any) (map[string]any, error) {
return in, nil
}
wf.AddNode("process", identityActivity, nil, ao).AddInput(compose.START)
// MapFieldPaths for nested access: user.profile.name -> response.userName
wf.End().AddInput("process", compose.MapFieldPaths(
compose.FieldPath{"user", "profile", "name"},
compose.FieldPath{"response", "userName"},
))
r, err := wf.Compile(ctx)
if err != nil {
return nil, err
}
return r.Invoke(ctx, input)
}
```
--------------------------------
### Add Conditional Branch
Source: https://github.com/nickqiaoo/temporalgraph/blob/main/CLAUDE.md
Incorporates conditional execution paths into the workflow graph, allowing different branches to be taken based on a condition.
```go
g.AddBranch(fromKey, condition, branchNodes)
```
--------------------------------
### Test Workflow Validation for Missing End Edge
Source: https://context7.com/nickqiaoo/temporalgraph/llms.txt
This Go code tests workflow validation using Temporal's test suite. It specifically checks for an error when a workflow graph is compiled without a connection to the end node.
```go
package compose_test
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.temporal.io/sdk/testsuite"
"go.temporal.io/sdk/workflow"
"github.com/Nickqiaoo/temporalgraph/compose"
)
func TestWorkflowValidation(t *testing.T) {
testSuite := &testsuite.WorkflowTestSuite{}
env := testSuite.NewTestWorkflowEnvironment()
// Test invalid graph - missing end edge
invalidWorkflow := func(ctx workflow.Context) error {
g := compose.NewGraph[string, string]()
ao := workflow.ActivityOptions{StartToCloseTimeout: time.Minute}
_ = g.AddNode("process", MockProcess, "test", ao)
_ = g.AddEdge(compose.START, "process")
// Missing: g.AddEdge("process", compose.END)
_, err := g.Compile(ctx)
return err
}
env.ExecuteWorkflow(invalidWorkflow)
require.True(t, env.IsWorkflowCompleted())
require.Error(t, env.GetWorkflowError())
require.Contains(t, env.GetWorkflowError().Error(), "end node not set")
}
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.