PARAMS_EXAMPLE.md 5.0 KB

Workflow Parameters Feature

Overview

Workflow parameters are user-provided input values that:

  • Start with a letter (no $ prefix)
  • Can be used in expressions throughout the workflow
  • Are read-only during workflow execution (cannot be modified)
  • Are declared in the workflow registry with type information

Example Workflow

{
  "version": "3.6",
  "name": "UserDataProcessor",
  "registry": {
    "params": [
      "userId(STRING)",
      "maxRecords(INT)",
      "config(OBJECT)"
    ],
    "vars": [
      "$processedData(OBJECT)",
      "$recordCount(INT)"
    ]
  },
  "steps": [
    {
      "id": "Set_RecordCount",
      "target": "$recordCount",
      "value": "=maxRecords + 10",
      "next": "Set_Data"
    },
    {
      "id": "Set_Data",
      "target": "$processedData",
      "value": "{\"userId\": \"=userId\", \"limit\": \"=$recordCount\", \"host\": \"=config.host\"}",
      "next": "Stop_End"
    },
    {
      "id": "Stop_End"
    }
  ]
}

Usage in Go

// Define workflow with parameters
workflow := &workflow.Workflow{
    Version: "3.6",
    Name:    "UserDataProcessor",
    Registry: workflow.Registry{
        Params: []string{
            "userId(STRING)",
            "maxRecords(INT)",
            "config(OBJECT)",
        },
        Vars: []string{
            "$processedData(OBJECT)",
            "$recordCount(INT)",
        },
    },
    Steps: []workflow.Step{
        // ... steps as shown above
    },
}

// Create engine
engine, _ := workflow.NewEngine(workflow)

// Execute with parameter values
result, _ := engine.Execute(
    context.Background(),
    map[string]interface{}{
        "userId":     "user123",
        "maxRecords": 100,
        "config": map[string]interface{}{
            "host": "api.example.com",
            "port": 443,
        },
    },
    adapters,
)

Key Features

1. Type Safety with Automatic Conversion

Parameters can have type declarations similar to variables:

  • userId(STRING) - string parameter
  • maxRecords(INT) - integer parameter
  • config(OBJECT) - object parameter (automatically converts JSON strings to maps)
  • items([STRING]) - array parameter

OBJECT Parameter Conversion: When you declare a parameter as OBJECT type and pass a JSON string during workflow execution, it's automatically parsed and converted to a map[string]interface{}:

// Workflow declares config(OBJECT)
engine.Execute(ctx, map[string]interface{}{
    "config": `{"host": "localhost", "port": 8080}`,  // String input
}, adapters)

// Inside workflow, config is accessible as a map:
// "=config.host" → "localhost"
// "=config.port" → 8080

2. Expression Support

Parameters can be used in any expression:

// Direct reference
"=userId"

// Arithmetic
"=maxRecords + 10"

// String concatenation
"=userId + \"_processed\""

// Nested access
"=config.host"

// Comparisons
"=maxRecords > 100"

3. Read-Only Enforcement

Attempting to modify a parameter will result in an error:

{
  "id": "Set_Param",
  "target": "userId",
  "value": "newValue"
}

This will fail with: "cannot modify parameter: userId (parameters are read-only)"

4. Distinction from Variables

  • Parameters: No $ prefix, read-only, passed at workflow execution
  • Variables: $ prefix, mutable, can be modified during execution

Passing Parameters

When executing a workflow, parameters can be passed through initialVars:

initialVars := map[string]interface{}{
    "userId":   "user123",      // Parameter (no $ prefix)
    "config":   configObject,   // Parameter
    "$result":  nil,            // Variable ($ prefix)
}

engine.Execute(ctx, initialVars, adapters)

The engine will automatically:

  1. Check if a key is declared in registry.params
  2. Store declared parameters in ExecutionContext.Params
  3. Store variables (with $ prefix) in ExecutionContext.Variables
  4. Store undeclared keys as variables with $ prefix added

Error Handling

Undefined Parameter

// If "unknownParam" is not in Params
"=unknownParam"
// Error: "parameter not found: unknownParam"

Attempting to Modify

{
  "target": "userId",
  "value": "newValue"
}
// Error: "cannot modify parameter: userId (parameters are read-only)"

Invalid JSON for OBJECT Parameter

// If passing invalid JSON for OBJECT param
engine.Execute(ctx, map[string]interface{}{
    "config": "not valid json",  // Invalid JSON
}, adapters)
// Error: "failed to convert parameter config to OBJECT: ..."

Best Practices

  1. Use Parameters for:

    • User inputs
    • Configuration values
    • Read-only reference data
    • API keys and credentials
  2. Use Variables for:

    • Intermediate computation results
    • Data that needs to be modified
    • Loop iteration values
    • Step output mappings
  3. Naming Conventions:

    • Parameters: camelCase (e.g., userId, maxRecords)
    • Variables: camelCase with $ prefix (e.g., $result, $processedData)
    • Local vars: camelCase with _ prefix (e.g., _item, _index)