| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- package workflow
- import (
- "encoding/json"
- "fmt"
- "os"
- )
- // LoadWorkflowFromFile loads a workflow from a JSON file
- func LoadWorkflowFromFile(filename string) (*Workflow, error) {
- data, err := os.ReadFile(filename)
- if err != nil {
- return nil, fmt.Errorf("failed to read file: %w", err)
- }
- var wf Workflow
- if err := json.Unmarshal(data, &wf); err != nil {
- return nil, fmt.Errorf("failed to unmarshal workflow: %w", err)
- }
- return &wf, nil
- }
- // SaveWorkflowToFile saves a workflow to a JSON file
- func SaveWorkflowToFile(wf *Workflow, filename string) error {
- data, err := json.MarshalIndent(wf, "", " ")
- if err != nil {
- return fmt.Errorf("failed to marshal workflow: %w", err)
- }
- if err := os.WriteFile(filename, data, 0644); err != nil {
- return fmt.Errorf("failed to write file: %w", err)
- }
- return nil
- }
- // LoadWorkflowFromJSON loads a workflow from a JSON string
- func LoadWorkflowFromJSON(jsonStr string) (*Workflow, error) {
- var wf Workflow
- if err := json.Unmarshal([]byte(jsonStr), &wf); err != nil {
- return nil, fmt.Errorf("failed to unmarshal workflow: %w", err)
- }
- return &wf, nil
- }
- // ToJSON converts a workflow to a JSON string
- func (w *Workflow) ToJSON() (string, error) {
- data, err := json.MarshalIndent(w, "", " ")
- if err != nil {
- return "", fmt.Errorf("failed to marshal workflow: %w", err)
- }
- return string(data), nil
- }
- // GetStepByID finds a step by its ID
- func (w *Workflow) GetStepByID(stepID string) *Step {
- for i := range w.Steps {
- if w.Steps[i].ID == stepID {
- return &w.Steps[i]
- }
- }
- return nil
- }
- // GetStepsByType returns all steps of a specific type
- func (w *Workflow) GetStepsByType(stepType StepType) []Step {
- var steps []Step
- prefix := string(stepType) + "_"
- for _, step := range w.Steps {
- if len(step.ID) > len(prefix) && step.ID[:len(prefix)] == prefix {
- steps = append(steps, step)
- }
- }
- return steps
- }
- // ValidateStepReferences validates that all step references (next, children, cases) exist
- func (w *Workflow) ValidateStepReferences() error {
- stepIDs := make(map[string]bool)
- for _, step := range w.Steps {
- stepIDs[step.ID] = true
- }
- for _, step := range w.Steps {
- // Validate next
- if step.Next != "" && !stepIDs[step.Next] {
- return fmt.Errorf("step %s references non-existent next step: %s", step.ID, step.Next)
- }
- // Validate children
- for _, childID := range step.Children {
- if !stepIDs[childID] {
- return fmt.Errorf("step %s references non-existent child step: %s", step.ID, childID)
- }
- }
- // Validate cases
- for _, c := range step.Cases {
- if len(c) >= 2 && c[1] != "" && !stepIDs[c[1]] {
- return fmt.Errorf("step %s references non-existent case step: %s", step.ID, c[1])
- }
- }
- // Validate onError (v3.10+)
- if step.OnError != "" && !stepIDs[step.OnError] {
- return fmt.Errorf("step %s references non-existent onError step: %s", step.ID, step.OnError)
- }
- }
- return nil
- }
- // Clone creates a deep copy of the workflow
- func (w *Workflow) Clone() (*Workflow, error) {
- data, err := json.Marshal(w)
- if err != nil {
- return nil, fmt.Errorf("failed to marshal workflow: %w", err)
- }
- var clone Workflow
- if err := json.Unmarshal(data, &clone); err != nil {
- return nil, fmt.Errorf("failed to unmarshal workflow: %w", err)
- }
- return &clone, nil
- }
- // GetVariableNames returns all declared variable names
- func (r *Registry) GetVariableNames() ([]string, error) {
- var names []string
- for _, varDecl := range r.Vars {
- parsed, err := ParseVariableDeclaration(varDecl)
- if err != nil {
- return nil, err
- }
- names = append(names, parsed.Name)
- }
- return names, nil
- }
- // GetServiceNames returns all declared service names
- func (r *Registry) GetServiceNames() ([]string, error) {
- var names []string
- for _, sig := range r.Services {
- parsed, err := ParseServiceSignature(sig)
- if err != nil {
- return nil, err
- }
- names = append(names, parsed.Name)
- }
- return names, nil
- }
- // BuildWorkflow provides a fluent builder for creating workflows
- type WorkflowBuilder struct {
- workflow *Workflow
- }
- // NewWorkflowBuilder creates a new workflow builder
- func NewWorkflowBuilder(name string) *WorkflowBuilder {
- return &WorkflowBuilder{
- workflow: &Workflow{
- Version: "3.13",
- Name: name,
- Registry: Registry{
- Services: []string{},
- APIs: []APIDefinition{},
- Components: []string{},
- Vars: []string{},
- Files: FilesRegistry{
- Inputs: []string{},
- Artifacts: []string{},
- },
- },
- Steps: []Step{},
- },
- }
- }
- // AddService adds a service to the registry
- func (b *WorkflowBuilder) AddService(signature string) *WorkflowBuilder {
- b.workflow.Registry.Services = append(b.workflow.Registry.Services, signature)
- return b
- }
- // AddAPI adds an API definition to the registry
- func (b *WorkflowBuilder) AddAPI(apiDef APIDefinition) *WorkflowBuilder {
- b.workflow.Registry.APIs = append(b.workflow.Registry.APIs, apiDef)
- return b
- }
- // AddComponent adds a component to the registry
- func (b *WorkflowBuilder) AddComponent(componentID string) *WorkflowBuilder {
- b.workflow.Registry.Components = append(b.workflow.Registry.Components, componentID)
- return b
- }
- // AddVar adds a variable to the registry
- func (b *WorkflowBuilder) AddVar(varDecl string) *WorkflowBuilder {
- b.workflow.Registry.Vars = append(b.workflow.Registry.Vars, varDecl)
- return b
- }
- // AddInput adds an input file pattern
- func (b *WorkflowBuilder) AddInput(pattern string) *WorkflowBuilder {
- b.workflow.Registry.Files.Inputs = append(b.workflow.Registry.Files.Inputs, pattern)
- return b
- }
- // AddArtifact adds an artifact file pattern
- func (b *WorkflowBuilder) AddArtifact(pattern string) *WorkflowBuilder {
- b.workflow.Registry.Files.Artifacts = append(b.workflow.Registry.Files.Artifacts, pattern)
- return b
- }
- // AddStep adds a step to the workflow
- func (b *WorkflowBuilder) AddStep(step Step) *WorkflowBuilder {
- b.workflow.Steps = append(b.workflow.Steps, step)
- return b
- }
- // Build returns the constructed workflow
- func (b *WorkflowBuilder) Build() (*Workflow, error) {
- if err := b.workflow.Validate(); err != nil {
- return nil, err
- }
- return b.workflow, nil
- }
- // MustBuild returns the workflow or panics on error
- func (b *WorkflowBuilder) MustBuild() *Workflow {
- wf, err := b.Build()
- if err != nil {
- panic(err)
- }
- return wf
- }
|