From 886ea7acfd3fe49de556fd9c86c84263b9f283d5 Mon Sep 17 00:00:00 2001 From: Jon Langevin Date: Mon, 23 Feb 2026 03:54:58 -0500 Subject: [PATCH] refactor: replace concrete type assertions with capability interfaces (closes #59) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace two concrete type assertions in cmd/server/main.go with interface checks: 1. svc.(*module.OpenAPIGenerator) → svc.(interfaces.SchemaRegistrar) - Define SchemaRegistrar in interfaces/ with Name(), RegisterAdminSchemas(), ApplySchemas() methods - Add RegisterAdminSchemas() method to *OpenAPIGenerator that delegates to the package-level RegisterAdminSchemas function - Add compile-time check: var _ interfaces.SchemaRegistrar = (*OpenAPIGenerator)(nil) 2. svc.(*module.WorkflowRegistry) → svc.(interfaces.WorkflowStoreProvider) - Define WorkflowStoreProvider in interfaces/ with Name() and WorkflowStore() any - Add WorkflowStore() any method to *WorkflowRegistry returning the store opaquely (avoids interfaces→module circular import) - Add compile-time check: var _ interfaces.WorkflowStoreProvider = (*WorkflowRegistry)(nil) No behaviour is changed; only the mechanism for identifying capable services is updated. Co-Authored-By: Claude Opus 4.6 --- cmd/server/main.go | 15 +++++++++------ interfaces/registry.go | 35 +++++++++++++++++++++++++++++++++++ module/openapi_generator.go | 11 +++++++++++ module/workflow_registry.go | 11 +++++++++++ 4 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 interfaces/registry.go diff --git a/cmd/server/main.go b/cmd/server/main.go index 35f5b15d..e829149c 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -29,6 +29,7 @@ import ( "github.com/GoCodeAlone/workflow/dynamic" "github.com/GoCodeAlone/workflow/environment" "github.com/GoCodeAlone/workflow/handlers" + "github.com/GoCodeAlone/workflow/interfaces" "github.com/GoCodeAlone/workflow/module" "github.com/GoCodeAlone/workflow/observability" "github.com/GoCodeAlone/workflow/observability/tracing" @@ -471,8 +472,8 @@ func registerManagementServices(logger *slog.Logger, app *serverApp) { // Enrich OpenAPI spec via the service registry for _, svc := range engine.GetApp().SvcRegistry() { - if gen, ok := svc.(*module.OpenAPIGenerator); ok { - module.RegisterAdminSchemas(gen) + if gen, ok := svc.(interfaces.SchemaRegistrar); ok { + gen.RegisterAdminSchemas() gen.ApplySchemas() logger.Info("Registered typed OpenAPI schemas", "module", gen.Name()) } @@ -498,10 +499,12 @@ func (app *serverApp) initStores(logger *slog.Logger) error { // Discover the WorkflowRegistry from the service registry var store *module.V1Store for _, svc := range engine.GetApp().SvcRegistry() { - if reg, ok := svc.(*module.WorkflowRegistry); ok { - store = reg.Store() - logger.Info("Using WorkflowRegistry store", "module", reg.Name()) - break + if provider, ok := svc.(interfaces.WorkflowStoreProvider); ok { + if s, ok := provider.WorkflowStore().(*module.V1Store); ok { + store = s + logger.Info("Using WorkflowRegistry store", "module", provider.Name()) + break + } } } diff --git a/interfaces/registry.go b/interfaces/registry.go new file mode 100644 index 00000000..77955078 --- /dev/null +++ b/interfaces/registry.go @@ -0,0 +1,35 @@ +package interfaces + +// SchemaRegistrar is implemented by any service that can register admin +// schemas into an OpenAPI specification and apply them. Using this interface +// in cmd/server allows the server to enrich the OpenAPI spec without holding +// a concrete *module.OpenAPIGenerator pointer. +// +// *module.OpenAPIGenerator satisfies this interface. +type SchemaRegistrar interface { + // Name returns the module name (used for logging). + Name() string + + // RegisterAdminSchemas registers all admin API request/response schemas + // onto this generator. Equivalent to calling module.RegisterAdminSchemas(gen). + RegisterAdminSchemas() + + // ApplySchemas applies all previously registered component schemas and + // operation schema overrides to the current OpenAPI spec. + ApplySchemas() +} + +// WorkflowStoreProvider is implemented by any service that exposes a workflow +// data store. Using this interface in cmd/server decouples the server startup +// code from the concrete *module.WorkflowRegistry type. +// +// *module.WorkflowRegistry satisfies this interface. +type WorkflowStoreProvider interface { + // Name returns the module name (used for logging). + Name() string + + // WorkflowStore returns the underlying workflow data store as an opaque + // value. The caller is responsible for asserting the concrete type + // (typically *module.V1Store) when further operations are required. + WorkflowStore() any +} diff --git a/module/openapi_generator.go b/module/openapi_generator.go index fe739f78..44e2cb32 100644 --- a/module/openapi_generator.go +++ b/module/openapi_generator.go @@ -10,9 +10,13 @@ import ( "sync" "github.com/CrisisTextLine/modular" + "github.com/GoCodeAlone/workflow/interfaces" "gopkg.in/yaml.v3" ) +// Compile-time assertion: *OpenAPIGenerator must satisfy interfaces.SchemaRegistrar. +var _ interfaces.SchemaRegistrar = (*OpenAPIGenerator)(nil) + // --- OpenAPI 3.0 spec structs (minimal inline definitions) --- // OpenAPISpec represents a minimal OpenAPI 3.0 specification document. @@ -681,3 +685,10 @@ func (g *OpenAPIGenerator) ApplySchemas() { } } } + +// RegisterAdminSchemas satisfies the interfaces.SchemaRegistrar interface. +// It delegates to the package-level RegisterAdminSchemas function, registering +// all admin API request/response schemas onto this generator. +func (g *OpenAPIGenerator) RegisterAdminSchemas() { + RegisterAdminSchemas(g) +} diff --git a/module/workflow_registry.go b/module/workflow_registry.go index d7fb741f..63ba5959 100644 --- a/module/workflow_registry.go +++ b/module/workflow_registry.go @@ -5,8 +5,12 @@ import ( "fmt" "github.com/CrisisTextLine/modular" + "github.com/GoCodeAlone/workflow/interfaces" ) +// Compile-time assertion: *WorkflowRegistry must satisfy interfaces.WorkflowStoreProvider. +var _ interfaces.WorkflowStoreProvider = (*WorkflowRegistry)(nil) + // WorkflowRegistry is a module that provides the V1Store as a service, // making the workflow data store (companies, projects, workflows) available // to other modules via the service registry. It can either use a shared @@ -84,6 +88,13 @@ func (w *WorkflowRegistry) Store() *V1Store { return w.store } +// WorkflowStore satisfies the interfaces.WorkflowStoreProvider interface. +// It returns the underlying V1Store as an opaque any value so that the +// interfaces package does not need to import the module package. +func (w *WorkflowRegistry) WorkflowStore() any { + return w.store +} + func (w *WorkflowRegistry) ProvidesServices() []modular.ServiceProvider { return []modular.ServiceProvider{ {Name: w.name, Description: "Workflow data registry (companies, projects, workflows)", Instance: w},