Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 2 additions & 51 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,31 +40,8 @@ import (
_ "github.com/GoCodeAlone/workflow/plugin/docmanager"
pluginexternal "github.com/GoCodeAlone/workflow/plugin/external"
_ "github.com/GoCodeAlone/workflow/plugin/storebrowser"
pluginai "github.com/GoCodeAlone/workflow/plugins/ai"
pluginapi "github.com/GoCodeAlone/workflow/plugins/api"
pluginauth "github.com/GoCodeAlone/workflow/plugins/auth"
plugincicd "github.com/GoCodeAlone/workflow/plugins/cicd"
plugindlq "github.com/GoCodeAlone/workflow/plugins/dlq"
pluginevstore "github.com/GoCodeAlone/workflow/plugins/eventstore"
pluginff "github.com/GoCodeAlone/workflow/plugins/featureflags"
pluginhttp "github.com/GoCodeAlone/workflow/plugins/http"
pluginintegration "github.com/GoCodeAlone/workflow/plugins/integration"
pluginlicense "github.com/GoCodeAlone/workflow/plugins/license"
pluginmessaging "github.com/GoCodeAlone/workflow/plugins/messaging"
pluginmodcompat "github.com/GoCodeAlone/workflow/plugins/modularcompat"
pluginobs "github.com/GoCodeAlone/workflow/plugins/observability"
allplugins "github.com/GoCodeAlone/workflow/plugins/all"
pluginpipeline "github.com/GoCodeAlone/workflow/plugins/pipelinesteps"
plugincloud "github.com/GoCodeAlone/workflow/plugins/cloud"
plugindatastores "github.com/GoCodeAlone/workflow/plugins/datastores"
plugingitlab "github.com/GoCodeAlone/workflow/plugins/gitlab"
pluginmarketplace "github.com/GoCodeAlone/workflow/plugins/marketplace"
pluginplatform "github.com/GoCodeAlone/workflow/plugins/platform"
pluginpolicy "github.com/GoCodeAlone/workflow/plugins/policy"
pluginscheduler "github.com/GoCodeAlone/workflow/plugins/scheduler"
pluginsecrets "github.com/GoCodeAlone/workflow/plugins/secrets"
pluginsm "github.com/GoCodeAlone/workflow/plugins/statemachine"
pluginstorage "github.com/GoCodeAlone/workflow/plugins/storage"
plugintimeline "github.com/GoCodeAlone/workflow/plugins/timeline"
"github.com/GoCodeAlone/workflow/provider"
_ "github.com/GoCodeAlone/workflow/provider/aws"
_ "github.com/GoCodeAlone/workflow/provider/azure"
Expand Down Expand Up @@ -109,33 +86,7 @@ var (
// defaultEnginePlugins returns the standard set of engine plugins used by all engine instances.
// Centralising the list here avoids duplication between buildEngine and runMultiWorkflow.
func defaultEnginePlugins() []plugin.EnginePlugin {
return []plugin.EnginePlugin{
pluginlicense.New(),
pluginhttp.New(),
pluginobs.New(),
pluginmessaging.New(),
pluginsm.New(),
pluginauth.New(),
pluginstorage.New(),
pluginapi.New(),
pluginpipeline.New(),
plugincicd.New(),
pluginff.New(),
pluginevstore.New(),
plugintimeline.New(),
plugindlq.New(),
pluginsecrets.New(),
pluginmodcompat.New(),
pluginscheduler.New(),
pluginintegration.New(),
pluginai.New(),
pluginplatform.New(),
plugincloud.New(),
plugingitlab.New(),
plugindatastores.New(),
pluginpolicy.New(),
pluginmarketplace.New(),
}
return allplugins.DefaultPlugins()
}

// buildEngine creates the workflow engine with all handlers registered and built from config.
Expand Down
5 changes: 5 additions & 0 deletions example/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/BurntSushi/toml v1.6.0 // indirect
github.com/CrisisTextLine/modular/modules/auth v0.4.0 // indirect
github.com/CrisisTextLine/modular/modules/cache v0.4.0 // indirect
github.com/CrisisTextLine/modular/modules/eventbus/v2 v2.0.0 // indirect
github.com/CrisisTextLine/modular/modules/reverseproxy/v2 v2.2.0 // indirect
github.com/CrisisTextLine/modular/modules/scheduler v0.4.0 // indirect
github.com/DataDog/datadog-go/v5 v5.4.0 // indirect
github.com/GoCodeAlone/yaegi v0.17.1 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
Expand Down Expand Up @@ -82,6 +85,7 @@ require (
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/golobby/cast v1.3.3 // indirect
Expand Down Expand Up @@ -138,6 +142,7 @@ require (
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect
github.com/redis/go-redis/v9 v9.18.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions example/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
Expand Down
14 changes: 7 additions & 7 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/CrisisTextLine/modular"
"github.com/GoCodeAlone/workflow"
"github.com/GoCodeAlone/workflow/config"
"github.com/GoCodeAlone/workflow/handlers"
allplugins "github.com/GoCodeAlone/workflow/plugins/all"
)

func main() {
Expand Down Expand Up @@ -50,12 +50,12 @@ func main() {
// Create workflow engine
engine := workflow.NewStdEngine(app, logger)

// Register workflow handlers
engine.RegisterWorkflowHandler(handlers.NewHTTPWorkflowHandler())
engine.RegisterWorkflowHandler(handlers.NewMessagingWorkflowHandler())
engine.RegisterWorkflowHandler(handlers.NewStateMachineWorkflowHandler())
engine.RegisterWorkflowHandler(handlers.NewSchedulerWorkflowHandler())
engine.RegisterWorkflowHandler(handlers.NewIntegrationWorkflowHandler())
// Load all built-in plugins in one call.
// To use a custom set, call allplugins.DefaultPlugins(), modify the slice,
// and load each plugin individually with engine.LoadPlugin.
if err = allplugins.LoadAll(engine); err != nil {
log.Fatalf("Failed to load plugins: %v", err)
}

// Build and start the workflows
if err = engine.BuildFromConfig(cfg); err != nil {
Expand Down
101 changes: 101 additions & 0 deletions plugins/all/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Package all provides a single import point for all built-in workflow engine plugins.
// Applications that embed the workflow engine can call [LoadAll] to register every
// standard plugin in one step instead of importing and wiring each plugin package
// individually.
//
// Example – minimal embedded engine setup:
//
// engine := workflow.NewStdEngine(app, logger)
// if err := all.LoadAll(engine); err != nil {
// log.Fatalf("failed to load plugins: %v", err)
// }
//
// If you need finer control (e.g. to skip a plugin or add your own), use
// [DefaultPlugins] to obtain the slice and modify it before loading:
//
// plugins := all.DefaultPlugins()
// plugins = append(plugins, myCustomPlugin)
// for _, p := range plugins {
// engine.LoadPlugin(p)
// }
package all

import (
"github.com/GoCodeAlone/workflow/plugin"
pluginai "github.com/GoCodeAlone/workflow/plugins/ai"
pluginapi "github.com/GoCodeAlone/workflow/plugins/api"
pluginauth "github.com/GoCodeAlone/workflow/plugins/auth"
plugincicd "github.com/GoCodeAlone/workflow/plugins/cicd"
plugincloud "github.com/GoCodeAlone/workflow/plugins/cloud"
plugindatastores "github.com/GoCodeAlone/workflow/plugins/datastores"
plugindlq "github.com/GoCodeAlone/workflow/plugins/dlq"
pluginevstore "github.com/GoCodeAlone/workflow/plugins/eventstore"
pluginff "github.com/GoCodeAlone/workflow/plugins/featureflags"
plugingitlab "github.com/GoCodeAlone/workflow/plugins/gitlab"
pluginhttp "github.com/GoCodeAlone/workflow/plugins/http"
pluginintegration "github.com/GoCodeAlone/workflow/plugins/integration"
pluginlicense "github.com/GoCodeAlone/workflow/plugins/license"
pluginmarketplace "github.com/GoCodeAlone/workflow/plugins/marketplace"
pluginmessaging "github.com/GoCodeAlone/workflow/plugins/messaging"
pluginmodcompat "github.com/GoCodeAlone/workflow/plugins/modularcompat"
pluginobs "github.com/GoCodeAlone/workflow/plugins/observability"
pluginpipeline "github.com/GoCodeAlone/workflow/plugins/pipelinesteps"
pluginplatform "github.com/GoCodeAlone/workflow/plugins/platform"
pluginpolicy "github.com/GoCodeAlone/workflow/plugins/policy"
pluginscheduler "github.com/GoCodeAlone/workflow/plugins/scheduler"
pluginsecrets "github.com/GoCodeAlone/workflow/plugins/secrets"
pluginsm "github.com/GoCodeAlone/workflow/plugins/statemachine"
pluginstorage "github.com/GoCodeAlone/workflow/plugins/storage"
plugintimeline "github.com/GoCodeAlone/workflow/plugins/timeline"
)

// PluginLoader is the minimal interface required by [LoadAll].
// *workflow.StdEngine satisfies this interface.
type PluginLoader interface {
LoadPlugin(p plugin.EnginePlugin) error
}

// DefaultPlugins returns the standard set of built-in engine plugins.
// The slice is freshly allocated on each call so callers may safely append
// custom plugins without affecting other callers.
func DefaultPlugins() []plugin.EnginePlugin {
return []plugin.EnginePlugin{
pluginlicense.New(),
pluginhttp.New(),
pluginobs.New(),
pluginmessaging.New(),
pluginsm.New(),
pluginauth.New(),
pluginstorage.New(),
pluginapi.New(),
pluginpipeline.New(),
plugincicd.New(),
pluginff.New(),
pluginevstore.New(),
plugintimeline.New(),
plugindlq.New(),
pluginsecrets.New(),
pluginmodcompat.New(),
pluginscheduler.New(),
pluginintegration.New(),
pluginai.New(),
pluginplatform.New(),
plugincloud.New(),
plugingitlab.New(),
plugindatastores.New(),
pluginpolicy.New(),
pluginmarketplace.New(),
}
}

// LoadAll loads all default built-in plugins into the given engine.
// It is equivalent to calling engine.LoadPlugin for each plugin returned by
// [DefaultPlugins]. The first error encountered is returned immediately.
func LoadAll(engine PluginLoader) error {
for _, p := range DefaultPlugins() {
if err := engine.LoadPlugin(p); err != nil {
return err
}
}
return nil
}
124 changes: 124 additions & 0 deletions plugins/all/all_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package all

import (
"testing"

"github.com/GoCodeAlone/workflow/capability"
"github.com/GoCodeAlone/workflow/plugin"
"github.com/GoCodeAlone/workflow/schema"
)

func TestDefaultPlugins_NotEmpty(t *testing.T) {
plugins := DefaultPlugins()
if len(plugins) == 0 {
t.Fatal("DefaultPlugins() returned empty slice")
}
}

func TestDefaultPlugins_AllNonNil(t *testing.T) {
for i, p := range DefaultPlugins() {
if p == nil {
t.Errorf("DefaultPlugins()[%d] is nil", i)
}
}
}

func TestDefaultPlugins_UniqueNames(t *testing.T) {
seen := make(map[string]bool)
for _, p := range DefaultPlugins() {
name := p.Name()
if seen[name] {
t.Errorf("duplicate plugin name %q in DefaultPlugins()", name)
}
seen[name] = true
}
}

func TestDefaultPlugins_IndependentSlices(t *testing.T) {
a := DefaultPlugins()
b := DefaultPlugins()
if len(a) != len(b) {
t.Fatalf("successive calls returned different lengths: %d vs %d", len(a), len(b))
}
// Modifying one slice must not affect the other.
a[0] = nil
if b[0] == nil {
t.Error("modifying slice returned by DefaultPlugins() affected a separate call")
}
}

// stubEngine is a minimal PluginLoader used in tests to avoid importing the
// workflow package (which would create a circular dependency).
type stubEngine struct {
loaded []string
errOn string // if non-empty, return an error when this plugin name is loaded
}

func (s *stubEngine) LoadPlugin(p plugin.EnginePlugin) error {
if s.errOn != "" && p.Name() == s.errOn {
return &testError{p.Name()}
}
s.loaded = append(s.loaded, p.Name())
return nil
}

type testError struct{ name string }

func (e *testError) Error() string { return "test error for " + e.name }

func TestLoadAll_LoadsAllPlugins(t *testing.T) {
eng := &stubEngine{}
if err := LoadAll(eng); err != nil {
t.Fatalf("LoadAll() unexpected error: %v", err)
}

want := len(DefaultPlugins())
if len(eng.loaded) != want {
t.Errorf("LoadAll() loaded %d plugins, want %d", len(eng.loaded), want)
}
}

func TestLoadAll_ReturnsFirstError(t *testing.T) {
plugins := DefaultPlugins()
if len(plugins) == 0 {
t.Skip("no plugins")
}
// Trigger an error on the second plugin to verify early return.
targetName := plugins[1].Name()
eng := &stubEngine{errOn: targetName}

err := LoadAll(eng)
if err == nil {
t.Fatal("LoadAll() expected error, got nil")
}

// Only the first plugin should have been loaded (the second triggered the error).
if len(eng.loaded) != 1 {
t.Errorf("LoadAll() loaded %d plugins before error, want 1", len(eng.loaded))
}
}

// TestLoadAll_WithRealLoader verifies that all default plugins can be loaded
// into a real PluginLoader without conflicts.
func TestLoadAll_WithRealLoader(t *testing.T) {
capReg := capability.NewRegistry()
schemaReg := schema.NewModuleSchemaRegistry()
loader := plugin.NewPluginLoader(capReg, schemaReg)

for _, p := range DefaultPlugins() {
if err := loader.LoadPlugin(p); err != nil {
t.Fatalf("LoadPlugin(%q) error: %v", p.Name(), err)
}
}

// All plugins were loaded — there should be module, step, and trigger factories.
if len(loader.ModuleFactories()) == 0 {
t.Error("no module factories registered after loading all plugins")
}
if len(loader.StepFactories()) == 0 {
t.Error("no step factories registered after loading all plugins")
}
if len(loader.TriggerFactories()) == 0 {
t.Error("no trigger factories registered after loading all plugins")
}
}
Loading