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