| name | this-sample-workflow-execute-3-steps-in-sequence |
| description | Apply for this-sample-workflow-execute-3-steps-in-sequence. --- description: globs: |
| source | cursorrules |
This sample workflow execute 3 steps in sequence.
description:
globs:
alwaysApply: false
This file provides rules and context for generating or understanding Go code related to a custom Domain Specific Language (DSL) for defining Temporal workflows within this project.
DSL Overview:
This project uses a specific Go-based DSL to define Temporal workflows declaratively. The core idea is to represent workflow logic using nested structures rather than imperative Go code directly within the workflow function.
Core DSL Structures (Defined in dsl package):
Workflow: The top-level structure representing the entire workflow definition.
Variables map[string]string: Initial variables available to the workflow. These act as the initial state or input.
Root Statement: The starting point of the workflow logic (can be a single activity, a sequence, or parallel steps).
Statement: A building block representing a single step or a composite structure. A Statement can contain one of the following:
Activity *ActivityInvocation: Represents a call to a Temporal Activity.
Sequence *Sequence: Represents a series of statements executed sequentially.
Parallel *Parallel: Represents a set of statements executed in parallel.
Sequence: Contains a slice Elements []*Statement which are executed one after another.
Parallel: Contains a slice Branches []*Statement which are executed concurrently. The parallel execution waits for all branches to complete. If one branch errors, others are cancelled.
ActivityInvocation: Defines how to invoke a specific Temporal Activity.
Name string: The registered name of the Temporal Activity to call.
Arguments []string: A list of variable names (keys from the bindings map) whose values should be passed as arguments to the activity.
Result string: The name of the variable (key in the bindings map) where the activity's result should be stored.
Execution Flow (SimpleDSLWorkflow):
- The entry point for workflows defined using this DSL is the
SimpleDSLWorkflow function: func SimpleDSLWorkflow(ctx workflow.Context, dslWorkflow Workflow) ([]byte, error).
- It initializes a
bindings map from the dslWorkflow.Variables.
- It sets default
workflow.ActivityOptions (e.g., StartToCloseTimeout).
- It uses
workflow.GetLogger(ctx) for logging.
- It recursively calls the
execute method on the Root statement, passing the ctx and bindings map.
Data Flow (bindings):
- The
bindings map[string]string acts as the shared state or memory for the workflow execution.
- Initial values come from
Workflow.Variables.
ActivityInvocation.Arguments specifies which values from bindings to use as input for an activity.
ActivityInvocation.Result specifies the key in bindings where the activity's return value should be stored.
- The
makeInput helper function retrieves argument values from the bindings map based on the names listed in ActivityInvocation.Arguments.
Concurrency (Parallel execution):
- The
Parallel.execute method uses workflow.Go to launch each branch concurrently.
- It uses
workflow.NewSelector and Future.Get to wait for branches to complete.
workflow.WithCancel is used to cancel pending branches if one branch encounters an error.
Working with the DSL:
- When asked to create or modify workflows, structure the logic using the
Workflow, Statement, Sequence, Parallel, and ActivityInvocation types.
- Define the flow of execution by nesting
Sequence and Parallel structures within Statements.
- Specify activity calls using
ActivityInvocation, ensuring Name, Arguments, and Result are correctly defined based on the available activities and the desired data flow through the bindings map.
- Remember that the actual activity implementation exists separately (see
activities.mdc) and is invoked by name.
package dsl
import (
"time"
"go.temporal.io/sdk/workflow"
)
type (
Workflow struct {
Variables map[string]string
Root Statement
}
Statement struct {
Activity *ActivityInvocation
Sequence *Sequence
Parallel *Parallel
}
Sequence struct {
Elements []*Statement
}
Parallel struct {
Branches []*Statement
}
ActivityInvocation struct {
Name string
Arguments []string
Result string
}
executable interface {
execute(ctx workflow.Context, bindings map[string]string) error
}
)
func SimpleDSLWorkflow(ctx workflow.Context, dslWorkflow Workflow) ([]byte, error) {
bindings := make([])
k, v := dslWorkflow.Variables {
bindings[k] = v
}
ao := workflow.ActivityOptions{
StartToCloseTimeout: * time.Second,
}
ctx = workflow.WithActivityOptions(ctx, ao)
logger := workflow.GetLogger(ctx)
err := dslWorkflow.Root.execute(ctx, bindings)
err != {
logger.Error(, , err)
, err
}
logger.Info()
, err
}
execute(ctx workflow.Context, bindings []) {
b.Parallel != {
err := b.Parallel.execute(ctx, bindings)
err != {
err
}
}
b.Sequence != {
err := b.Sequence.execute(ctx, bindings)
err != {
err
}
}
b.Activity != {
err := b.Activity.execute(ctx, bindings)
err != {
err
}
}
}
execute(ctx workflow.Context, bindings []) {
inputParam := makeInput(a.Arguments, bindings)
result
err := workflow.ExecuteActivity(ctx, a.Name, inputParam).Get(ctx, &result)
err != {
err
}
a.Result != {
bindings[a.Result] = result
}
}
execute(ctx workflow.Context, bindings []) {
_, a := s.Elements {
err := a.execute(ctx, bindings)
err != {
err
}
}
}
execute(ctx workflow.Context, bindings []) {
childCtx, cancelHandler := workflow.WithCancel(ctx)
selector := workflow.NewSelector(ctx)
activityErr
_, s := p.Branches {
f := executeAsync(s, childCtx, bindings)
selector.AddFuture(f, {
err := f.Get(ctx, )
err != {
cancelHandler()
activityErr = err
}
})
}
i := ; i < (p.Branches); i++ {
selector.Select(ctx)
activityErr != {
activityErr
}
}
}
workflow.Future {
future, settable := workflow.NewFuture(ctx)
workflow.Go(ctx, {
err := exe.execute(ctx, bindings)
settable.Set(, err)
})
future
}
[] {
args []
_, arg := argNames {
args = (args, argsMap[arg])
}
args
}
Example Workflows:
Here are some example workflow implementations:
variables:
arg1: value1
arg2: value2
root:
sequence:
elements:
- activity:
name: SampleActivity1
arguments:
- arg1
result: result1
- activity:
name: SampleActivity2
arguments:
- result1
result: result2
- activity:
name: SampleActivity3
arguments:
- arg2
- result2
result: result3
variables:
arg1: value1
arg2: value2
arg3: value3
root:
sequence:
elements:
- activity:
name: SampleActivity1
arguments:
- arg1
result: result1
- parallel:
branches:
- sequence:
elements:
- activity:
name: SampleActivity2
arguments:
- result1
result: result2
-