Documentation
¶
Overview ¶
Package sdk provides the public API for building AWF plugins.
This package contains interfaces, types, and utilities for developing plugins that extend AWF workflow capabilities. Plugin developers import this package to create custom operations that integrate with AWF workflows.
Key features:
- Plugin interface with lifecycle methods (Init, Shutdown)
- OperationHandler and OperationProvider interfaces for custom operations
- BasePlugin for embedding (provides default implementations)
- OperationResult for structured operation responses
- Input helpers for extracting typed values from operation inputs
- Schema types for declaring operation signatures
Core Interfaces ¶
## Plugin (sdk.go)
Plugin interface defines the plugin lifecycle:
- Name: Unique plugin identifier
- Version: Semantic version string
- Init: Initialize with configuration
- Shutdown: Graceful cleanup on exit
## OperationHandler (sdk.go)
OperationHandler processes single operation requests:
- Handle: Execute operation with inputs, return outputs
## OperationProvider (sdk.go)
OperationProvider for plugins offering multiple operations:
- Operations: List of operation names
- HandleOperation: Execute named operation with inputs
Base Types ¶
## BasePlugin (sdk.go)
BasePlugin provides minimal implementation for embedding:
- Implements Plugin interface with no-op Init/Shutdown
- Stores PluginName and PluginVersion
- Embed in your plugin struct to inherit base behavior
## OperationResult (sdk.go)
OperationResult holds execution results:
- Success: Boolean success flag
- Output: Human-readable output text
- Data: Structured output data (map[string]any)
- Error: Error message if failed
- ToMap: Convert to map for handler return values
Helper Functions ¶
## Result Constructors (sdk.go)
NewSuccessResult(output string, data map[string]any) *OperationResult NewErrorResult(errMsg string) *OperationResult NewErrorResultf(format string, args ...any) *OperationResult
## Input Extractors (sdk.go)
Type-safe input extraction with optional defaults:
GetString(inputs map[string]any, key string) (string, bool) GetStringDefault(inputs map[string]any, key, defaultValue string) string GetInt(inputs map[string]any, key string) (int, bool) GetIntDefault(inputs map[string]any, key string, defaultValue int) int GetBool(inputs map[string]any, key string) (value, ok bool) GetBoolDefault(inputs map[string]any, key string, defaultValue bool) bool
Schema Types ¶
## InputSchema (sdk.go)
InputSchema defines an operation input parameter:
- Type: string, integer, boolean, array, object
- Required: Whether parameter is mandatory
- Default: Default value if not provided
- Description: Human-readable description
- Validation: Optional validation rule (url, email, etc.)
## OperationSchema (sdk.go)
OperationSchema defines a plugin-provided operation:
- Name: Operation name (e.g., "slack.send")
- Description: Human-readable description
- Inputs: Map of input parameter schemas
- Outputs: List of output field names
## Schema Helpers (sdk.go)
RequiredInput(inputType, description string) InputSchema OptionalInput(inputType, description string, defaultValue any) InputSchema IsValidInputType(t string) bool
Constants ¶
## Input Types (sdk.go)
InputTypeString = "string" InputTypeInteger = "integer" InputTypeBoolean = "boolean" InputTypeArray = "array" InputTypeObject = "object"
## Errors (sdk.go)
ErrNotImplemented # Stub method needs implementation ErrInvalidInput # Invalid operation input ErrOperationFailed # Operation execution failed
Usage Examples ¶
## Simple Plugin with Single Operation
package main
import (
"context"
"github.com/awf-project/cli/pkg/plugin/sdk"
)
type SlackPlugin struct {
sdk.BasePlugin
webhookURL string
}
func NewSlackPlugin() *SlackPlugin {
return &SlackPlugin{
BasePlugin: sdk.BasePlugin{
PluginName: "slack",
PluginVersion: "1.0.0",
},
}
}
func (p *SlackPlugin) Init(ctx context.Context, config map[string]any) error {
url, ok := sdk.GetString(config, "webhook_url")
if !ok {
return sdk.ErrInvalidInput
}
p.webhookURL = url
return nil
}
func (p *SlackPlugin) Handle(ctx context.Context, inputs map[string]any) (map[string]any, error) {
message := sdk.GetStringDefault(inputs, "message", "")
if message == "" {
return sdk.NewErrorResult("message is required").ToMap(), nil
}
// Send message to Slack (implementation omitted)
err := p.sendMessage(message)
if err != nil {
return sdk.NewErrorResult(err.Error()).ToMap(), nil
}
result := sdk.NewSuccessResult("Message sent", map[string]any{
"timestamp": time.Now().Unix(),
})
return result.ToMap(), nil
}
## Plugin with Multiple Operations
type GithubPlugin struct {
sdk.BasePlugin
token string
}
func (p *GithubPlugin) Operations() []string {
return []string{"github.create_issue", "github.list_repos"}
}
func (p *GithubPlugin) HandleOperation(ctx context.Context, name string, inputs map[string]any) (*sdk.OperationResult, error) {
switch name {
case "github.create_issue":
return p.createIssue(ctx, inputs)
case "github.list_repos":
return p.listRepos(ctx, inputs)
default:
return sdk.NewErrorResult("unknown operation"), nil
}
}
func (p *GithubPlugin) createIssue(ctx context.Context, inputs map[string]any) (*sdk.OperationResult, error) {
title := sdk.GetStringDefault(inputs, "title", "")
body := sdk.GetStringDefault(inputs, "body", "")
// Create issue via GitHub API (implementation omitted)
issueNumber, err := p.api.CreateIssue(title, body)
if err != nil {
return sdk.NewErrorResult(err.Error()), nil
}
return sdk.NewSuccessResult("Issue created", map[string]any{
"issue_number": issueNumber,
}), nil
}
## Operation Schema Declaration
func (p *SlackPlugin) Schema() sdk.OperationSchema {
return sdk.OperationSchema{
Name: "slack.send",
Description: "Send message to Slack channel",
Inputs: map[string]sdk.InputSchema{
"message": sdk.RequiredInput(sdk.InputTypeString, "Message text"),
"channel": sdk.OptionalInput(sdk.InputTypeString, "Target channel", "#general"),
"urgent": sdk.OptionalInput(sdk.InputTypeBoolean, "Urgent notification", false),
},
Outputs: []string{"timestamp", "channel_id"},
}
}
## Input Validation and Extraction
func (p *Plugin) Handle(ctx context.Context, inputs map[string]any) (map[string]any, error) {
// Required string input
name, ok := sdk.GetString(inputs, "name")
if !ok {
return sdk.NewErrorResult("name is required").ToMap(), nil
}
// Optional integer input with default
count := sdk.GetIntDefault(inputs, "count", 10)
// Optional boolean input
verbose, _ := sdk.GetBool(inputs, "verbose")
// Execute operation
result := performOperation(name, count, verbose)
return sdk.NewSuccessResult("Done", map[string]any{
"processed": result,
}).ToMap(), nil
}
## Error Handling
func (p *Plugin) Handle(ctx context.Context, inputs map[string]any) (map[string]any, error) {
url, ok := sdk.GetString(inputs, "url")
if !ok {
// Invalid input - return error result
return sdk.NewErrorResult("url is required").ToMap(), nil
}
data, err := fetchURL(url)
if err != nil {
// Operation failed - return error result with formatted message
return sdk.NewErrorResultf("fetch failed: %v", err).ToMap(), nil
}
return sdk.NewSuccessResult("Fetched", map[string]any{
"size": len(data),
}).ToMap(), nil
}
## Graceful Shutdown
func (p *DatabasePlugin) Shutdown(ctx context.Context) error {
// Close database connections
if p.db != nil {
return p.db.Close()
}
return nil
}
Workflow Integration ¶
Plugins are used in workflows via the operation step type:
steps:
send_notification:
type: operation
operation: slack.send
inputs:
message: "Deployment completed"
channel: "#alerts"
urgent: true
on_success: end
Plugin output is available in subsequent steps:
{{states.send_notification.Data.timestamp}}
{{states.send_notification.Output}}
Testing Support ¶
## MockPlugin (testing.go)
MockPlugin provides a test double for plugin testing:
- Implements Plugin and OperationHandler interfaces
- Configurable responses for testing workflows
- Call tracking for verification
Example usage:
mock := sdk.NewMockPlugin("test-plugin", "1.0.0")
mock.SetResponse("success output", map[string]any{"key": "value"})
result, err := mock.Handle(ctx, inputs)
// result contains configured response
Design Principles ¶
## Public API Stability
This is the stable plugin SDK for external developers:
- Semantic versioning for breaking changes
- Backward compatibility for minor/patch releases
- Clear deprecation warnings before removal
## Minimal Dependencies
SDK has minimal external dependencies:
- Standard library only (context, errors)
- No AWF internal packages imported
- Plugin authors control their own dependencies
## Simple Interface
Easy to implement, hard to misuse:
- Single Handle method for simple plugins
- Optional OperationProvider for multi-operation plugins
- BasePlugin provides sensible defaults
- Helper functions reduce boilerplate
## Type Safety
Structured types for clarity:
- OperationResult enforces success/error pattern
- InputSchema declares expected types
- Helper functions handle type conversions
Related Documentation ¶
See also:
- internal/domain/operation: Domain operation interface and result types
- internal/infrastructure/pluginmgr: Plugin loader and registry implementation
- docs/plugin-development.md: Complete plugin development guide
- examples/plugins/: Example plugin implementations
Package sdk provides the public API for AWF plugin authors.
This package contains interfaces and utilities that plugin developers use to create AWF-compatible plugins. Import this package to build plugins that integrate with AWF workflows.
Example usage:
type MyPlugin struct {
sdk.BasePlugin
}
func (p *MyPlugin) Init(ctx context.Context, config map[string]any) error {
// Initialize your plugin
return nil
}
// Implement OperationHandler for custom operations
func (p *MyPlugin) Handle(ctx context.Context, inputs map[string]any) (map[string]any, error) {
result := sdk.NewSuccessResult("done", nil)
return result.ToMap(), nil
}
Package sdk provides testing utilities for AWF plugin development.
Index ¶
- Constants
- Variables
- func GetBool(inputs map[string]any, key string) (value, ok bool)
- func GetBoolDefault(inputs map[string]any, key string, defaultValue bool) bool
- func GetInt(inputs map[string]any, key string) (int, bool)
- func GetIntDefault(inputs map[string]any, key string, defaultValue int) int
- func GetString(inputs map[string]any, key string) (string, bool)
- func GetStringDefault(inputs map[string]any, key, defaultValue string) string
- func IsValidInputType(t string) bool
- func Serve(p Plugin)
- func TestConfig(keyValues ...any) map[string]any
- func TestInputs(keyValues ...any) map[string]any
- type BasePlugin
- type GRPCPluginBridge
- type InputSchema
- type MockOperationHandler
- type MockOperationProvider
- func (m *MockOperationProvider) HandleOperation(ctx context.Context, name string, inputs map[string]any) (*OperationResult, error)
- func (m *MockOperationProvider) Operations() []string
- func (m *MockOperationProvider) SetCommandResult(operation string, result *OperationResult)
- func (m *MockOperationProvider) SetError(operation string, err error)
- func (m *MockOperationProvider) SetResult(operation string, result *OperationResult)
- type MockPlugin
- func (m *MockPlugin) Init(_ context.Context, config map[string]any) error
- func (m *MockPlugin) Name() string
- func (m *MockPlugin) Reset()
- func (m *MockPlugin) Shutdown(_ context.Context) error
- func (m *MockPlugin) Version() string
- func (m *MockPlugin) WasInitCalled() bool
- func (m *MockPlugin) WasShutdownCalled() bool
- type OperationHandler
- type OperationProvider
- type OperationResult
- type OperationSchema
- type Plugin
- type Severity
- type StepDefinition
- type StepExecuteRequest
- type StepExecuteResult
- type StepTypeHandler
- type StepTypeInfo
- type ValidationIssue
- type Validator
- type WorkflowDefinition
Constants ¶
const ( InputTypeString = "string" InputTypeInteger = "integer" InputTypeBoolean = "boolean" InputTypeArray = "array" InputTypeObject = "object" )
Input types for operation parameters.
Variables ¶
var ( // ErrNotImplemented indicates a stub method that needs implementation. ErrNotImplemented = errors.New("not implemented") // ErrInvalidInput indicates invalid operation input. ErrInvalidInput = errors.New("invalid input") // ErrOperationFailed indicates the operation failed to execute. ErrOperationFailed = errors.New("operation failed") )
Common errors for plugin implementations.
var Handshake = goplugin.HandshakeConfig{
MagicCookieKey: "AWF_PLUGIN",
MagicCookieValue: "awf-plugin-v1",
ProtocolVersion: 1,
}
Handshake is the shared handshake config between AWF host and plugins. Plugin binaries must use this config to be recognized by AWF.
var ValidInputTypes = []string{ InputTypeString, InputTypeInteger, InputTypeBoolean, InputTypeArray, InputTypeObject, }
ValidInputTypes lists all recognized input types.
Functions ¶
func GetBoolDefault ¶
func GetStringDefault ¶
func IsValidInputType ¶
func Serve ¶
func Serve(p Plugin)
Serve starts the plugin process, blocking until the host disconnects. Plugin binaries call this from their main() after constructing their Plugin.
func TestConfig ¶
func TestInputs ¶
Types ¶
type BasePlugin ¶
BasePlugin provides a minimal implementation for embedding in plugins.
func (*BasePlugin) Name ¶
func (p *BasePlugin) Name() string
func (*BasePlugin) Version ¶
func (p *BasePlugin) Version() string
type GRPCPluginBridge ¶
type GRPCPluginBridge struct {
goplugin.NetRPCUnsupportedPlugin
// contains filtered or unexported fields
}
GRPCPluginBridge adapts sdk.Plugin to the go-plugin GRPCPlugin interface. Implementation in grpc_plugin.go bridges go-plugin interface to gRPC servers/clients.
func (*GRPCPluginBridge) GRPCClient ¶
func (b *GRPCPluginBridge) GRPCClient(_ context.Context, _ *goplugin.GRPCBroker, _ *grpc.ClientConn) (interface{}, error)
GRPCClient is required by the go-plugin GRPCPlugin interface but is never called on the plugin side. The host uses its own client implementation.
func (*GRPCPluginBridge) GRPCServer ¶
func (b *GRPCPluginBridge) GRPCServer(_ *goplugin.GRPCBroker, s *grpc.Server) (err error)
GRPCServer implements the go-plugin GRPCPlugin interface by registering the PluginService and OperationService gRPC servers.
type InputSchema ¶
type InputSchema struct {
Type string
Required bool
Default any
Description string
Validation string
}
func OptionalInput ¶
func OptionalInput(inputType, description string, defaultValue any) InputSchema
func RequiredInput ¶
func RequiredInput(inputType, description string) InputSchema
type MockOperationHandler ¶
type MockOperationHandler struct {
Outputs map[string]any
Error error
LastInputs map[string]any
CallCount int
HandleFunc func(ctx context.Context, inputs map[string]any) (map[string]any, error)
// contains filtered or unexported fields
}
MockOperationHandler is a test implementation of OperationHandler.
func NewMockOperationHandler ¶
func NewMockOperationHandler() *MockOperationHandler
func (*MockOperationHandler) GetCallCount ¶
func (m *MockOperationHandler) GetCallCount() int
func (*MockOperationHandler) Handle ¶
func (m *MockOperationHandler) Handle(ctx context.Context, inputs map[string]any) (map[string]any, error)
Handle executes the operation with recording.
func (*MockOperationHandler) Reset ¶
func (m *MockOperationHandler) Reset()
type MockOperationProvider ¶
type MockOperationProvider struct {
OperationNames []string
Results map[string]*OperationResult
Errors map[string]error
LastOperation string
LastInputs map[string]any
HandleFunc func(ctx context.Context, name string, inputs map[string]any) (*OperationResult, error)
// contains filtered or unexported fields
}
MockOperationProvider is a test implementation of OperationProvider.
func NewMockOperationProvider ¶
func NewMockOperationProvider(operations ...string) *MockOperationProvider
func (*MockOperationProvider) HandleOperation ¶
func (m *MockOperationProvider) HandleOperation(ctx context.Context, name string, inputs map[string]any) (*OperationResult, error)
func (*MockOperationProvider) Operations ¶
func (m *MockOperationProvider) Operations() []string
Operations returns the list of operation names.
func (*MockOperationProvider) SetCommandResult ¶
func (m *MockOperationProvider) SetCommandResult(operation string, result *OperationResult)
func (*MockOperationProvider) SetError ¶
func (m *MockOperationProvider) SetError(operation string, err error)
func (*MockOperationProvider) SetResult ¶
func (m *MockOperationProvider) SetResult(operation string, result *OperationResult)
type MockPlugin ¶
type MockPlugin struct {
PluginName string
PluginVersion string
InitCalled bool
ShutdownCalled bool
LastConfig map[string]any
InitError error
ShutdownError error
// contains filtered or unexported fields
}
MockPlugin is a test implementation of the Plugin interface.
func NewMockPlugin ¶
func NewMockPlugin(name, version string) *MockPlugin
func (*MockPlugin) Name ¶
func (m *MockPlugin) Name() string
func (*MockPlugin) Reset ¶
func (m *MockPlugin) Reset()
func (*MockPlugin) Shutdown ¶
func (m *MockPlugin) Shutdown(_ context.Context) error
Shutdown records the call and returns the configured error.
func (*MockPlugin) Version ¶
func (m *MockPlugin) Version() string
func (*MockPlugin) WasInitCalled ¶
func (m *MockPlugin) WasInitCalled() bool
WasInitCalled returns whether Init was called (thread-safe).
func (*MockPlugin) WasShutdownCalled ¶
func (m *MockPlugin) WasShutdownCalled() bool
type OperationHandler ¶
type OperationProvider ¶
type OperationResult ¶
func NewErrorResult ¶
func NewErrorResult(errMsg string) *OperationResult
func NewErrorResultf ¶
func NewErrorResultf(format string, args ...any) *OperationResult
func NewSuccessResult ¶
func NewSuccessResult(output string, data map[string]any) *OperationResult
func (*OperationResult) ToMap ¶
func (r *OperationResult) ToMap() map[string]any
type OperationSchema ¶
type OperationSchema struct {
Name string
Description string
Inputs map[string]InputSchema
Outputs []string
}
type Severity ¶ added in v0.5.0
type Severity int
Severity of a validation issue. SeverityError (zero value) matches proto SEVERITY_UNSPECIFIED being treated as ERROR by the host.
type StepDefinition ¶ added in v0.5.0
type StepDefinition struct {
Type string `json:"type"`
Description string `json:"description,omitempty"`
Command string `json:"command,omitempty"`
Operation string `json:"operation,omitempty"`
Timeout int `json:"timeout,omitempty"`
OnSuccess string `json:"on_success,omitempty"`
OnFailure string `json:"on_failure,omitempty"`
Config map[string]any `json:"config,omitempty"`
}
StepDefinition is the SDK representation of a workflow step for validator plugins.
type StepExecuteRequest ¶ added in v0.5.0
type StepExecuteRequest struct {
StepName string
StepType string
Config map[string]any
Inputs map[string]any
}
StepExecuteRequest carries the execution context for a custom step type.
type StepExecuteResult ¶ added in v0.5.0
StepExecuteResult holds the result of a custom step type execution.
type StepTypeHandler ¶ added in v0.5.0
type StepTypeHandler interface {
StepTypes() []StepTypeInfo
ExecuteStep(ctx context.Context, req StepExecuteRequest) (StepExecuteResult, error)
}
StepTypeHandler is the plugin-author interface for custom step types. Implement this interface to add new step types available in workflow YAML via `type:`.
StepTypes is called once after Init() to register available types. ExecuteStep is called each time a workflow step with a matching type runs.
type StepTypeInfo ¶ added in v0.5.0
StepTypeInfo describes a custom step type registered by a plugin.
type ValidationIssue ¶ added in v0.5.0
type ValidationIssue struct {
Severity Severity
Message string
Step string // step name, empty for workflow-level issues
Field string // field name within step, empty if not applicable
}
ValidationIssue describes a single validation problem found by a validator plugin.
type Validator ¶ added in v0.5.0
type Validator interface {
ValidateWorkflow(ctx context.Context, workflow WorkflowDefinition) ([]ValidationIssue, error)
ValidateStep(ctx context.Context, workflow WorkflowDefinition, stepName string) ([]ValidationIssue, error)
}
Validator is the plugin-author interface for custom workflow validation. Implement this interface to add validation rules that run during `awf validate`.
The host calls ValidateWorkflow once per validation run, then ValidateStep for each step in the workflow. Return nil issues for no problems.
type WorkflowDefinition ¶ added in v0.5.0
type WorkflowDefinition struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Version string `json:"version,omitempty"`
Author string `json:"author,omitempty"`
Tags []string `json:"tags,omitempty"`
Initial string `json:"initial"`
Steps map[string]StepDefinition `json:"steps"`
}
WorkflowDefinition is the SDK representation of a workflow for validator plugins. It is deserialized from the JSON payload sent by the host during validation.