Go SDK

Use the Go builder to register endpoints, serve Step handlers, and start or inspect Flows.

Installation

go get github.com/kode4food/argyll/sdk/go

Client

The package name is argyll:

import (
    "time"

    "github.com/kode4food/argyll/sdk/go"
)

client := argyll.NewClient("http://localhost:8080", 30*time.Second)

The examples below use this client.

Register a Script Step

import (
    "context"
    "github.com/kode4food/argyll/engine/pkg/api"
    argyll "github.com/kode4food/argyll/sdk/go"
)

err := client.NewStep().
    WithID("price-calculator").
    WithName("Price Calculator").
    Required("quantity", api.TypeNumber).
    Required("unit_price", api.TypeNumber).
    Output("total", api.TypeNumber).
    WithScript(api.ScriptConfig{
        Language: api.ScriptLangLua,
        Script:   `return {total = quantity * unit_price}`,
    }).
    Register(context.Background())

Executable Script Steps use Lua. The jpath language identifier is available for JSONPath predicates and attribute mappings, not executable Script Steps.

Register an HTTP Step

err := client.NewStep().
    WithID("lookup-customer").
    WithName("Lookup Customer").
    WithSyncExecution().
    WithEndpoint("https://api.example.com/customers/{customer_id}").
    WithMethod("GET").
    WithTimeout(5000).
    Required("customer_id", api.TypeString).
    Output("customer", api.TypeObject).
    Register(context.Background())

Serve a Sync Step

Start registers the Step and starts an HTTP server that handles invocations:

handler := func(ctx *argyll.StepContext, args api.Args) (api.Args, error) {
    name := args["name"].(string)
    return api.Args{"greeting": "Hello, " + name}, nil
}

err := client.NewStep().
    WithID("greet").
    WithName("Greet").
    Required("name", api.TypeString).
    Output("greeting", api.TypeString).
    Start(handler)

Serve an Async Step

handler := func(ctx *argyll.StepContext, args api.Args) (api.Args, error) {
    asyncCtx, err := argyll.NewAsyncContext(ctx)
    if err != nil {
        return nil, err
    }

    go func() {
        result := doLongRunningWork(args)
        asyncCtx.Success(api.Args{"result": result})
    }()

    return api.Args{}, nil  // return immediately
}

err := client.NewStep().
    WithID("process").
    WithName("Process").
    WithAsyncExecution().
    Required("input", api.TypeObject).
    Output("result", api.TypeObject).
    Start(handler)

Serve a Step with Compensation

Use WithCompensateHandler to register a handler that undoes a completed Work Item. Start generates the compensation URL:

handler := func(ctx *argyll.StepContext, args api.Args) (api.Args, error) {
    chargeID := chargeCard(args["amount"])
    return api.Args{"charge_id": chargeID}, nil
}

compensateHandler := func(
    ctx *argyll.StepContext, args api.Args,
) error {
    chargeID := args["charge_id"].(string)
    return refundCharge(ctx, chargeID)
}

err := client.NewStep().
    WithID("charge-card").
    WithName("Charge Card").
    Required("amount", api.TypeNumber).
    Output("charge_id", api.TypeString).
    WithCompensated("charge_id").
    WithCompensateHandler(compensateHandler).
    Start(handler)

The compensation handler receives a flat argument map containing the selected Attributes under their invocation names. Return nil for success, api.ErrWorkNotCompleted to request another attempt, or another error to report permanent failure.

Code Generation

The Go Step Generator derives a Step contract and handler from an ordinary Go function.

Builder Methods

Identity

  • WithID(id), WithName(name), WithLabel(k, v), WithLabels(labels)

Attributes

  • Required(name, type): required input
  • Optional(name, type, default): optional input with default value
  • Const(name, type, value): constant — a fixed value baked into the Step definition
  • Meta(name, metaKey): metadata — injects a named metadata field (e.g. flow_id, webhook_url) at execution time
  • Output(name, type): output attribute
  • WithForEach(names...): mark array inputs for parallel expansion
  • WithCompensated(names...): include Attributes in compensation requests

Execution Type

  • WithScriptExecution() selects Script execution

Action Mode

  • WithInvokeMode(api.ActionMode), WithCompensateMode(api.ActionMode): set how an action reports its result, api.ActionModeSync (default) or api.ActionModeAsync
  • WithSyncExecution(), WithAsyncExecution(): shorthand for the invoke action’s mode

HTTP

  • WithEndpoint(url), WithMethod(method), WithTimeout(ms), WithHealthCheck(url)
  • WithCompensate(url): set the endpoint URL and compensated handling
  • WithCompensateMethod(method): compensate HTTP method, defaults to POST
  • WithCompensateTimeout(ms): compensate timeout, defaults to the Step timeout
  • WithCompensateHandler(handler): register a compensation handler (auto-generates the URL when used with Start)

Script

  • WithScript(api.ScriptConfig{...}) - executable Script Steps use Lua

Predicate / Match

  • WithPredicate(api.ScriptConfig{...}) - predicates may use Lua or JSONPath ("jpath")
  • WithRequiredMatch(name, api.ScriptConfig{...}): match predicate on a required input

Behavior

  • WithHandling(api.Handling): choose standard, memoized, or compensated handling
  • WithFlowGoals(goals...): configure a Flow-type Step
  • WithFlowSpace(spaceID): restrict its child flow to a Space

Lifecycle

  • Register(ctx): register or update the Step definition, retrying transient failures
  • Start(handler): register or update with retries, then serve

The builder applies the engine’s validation rules before registration and returns the same validation error locally.

Registering a Prepared Step

RegisterStep accepts an existing *api.Step. It validates the definition, retries transient failures, and updates an existing registration after a conflict.

err := client.RegisterStep(context.Background(), step)

List Steps

steps, err := client.ListSteps(context.Background())

Start a Flow

Use WithGoals(goals...) to set all Goals at once, or WithGoal(goal) to add one at a time. Both WithLabel and WithLabels are also available on Flows.

err := client.NewFlow("order-123").
    WithGoals("send-confirmation").
    WithInitialState(api.InitArgs{
        "customer_id": []any{"cust-456"},
        "order_amount": []any{99.99},
    }).
    Start(context.Background())

Initial values are arrays. Wrap each value in a slice.

Query Flow State

flow := client.Flow("order-123")
state, err := flow.GetState(context.Background())
status, err := flow.GetStatus(context.Background())