utils.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. package workflow
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. )
  7. // LoadWorkflowFromFile loads a workflow from a JSON file
  8. func LoadWorkflowFromFile(filename string) (*Workflow, error) {
  9. data, err := os.ReadFile(filename)
  10. if err != nil {
  11. return nil, fmt.Errorf("failed to read file: %w", err)
  12. }
  13. var wf Workflow
  14. if err := json.Unmarshal(data, &wf); err != nil {
  15. return nil, fmt.Errorf("failed to unmarshal workflow: %w", err)
  16. }
  17. return &wf, nil
  18. }
  19. // SaveWorkflowToFile saves a workflow to a JSON file
  20. func SaveWorkflowToFile(wf *Workflow, filename string) error {
  21. data, err := json.MarshalIndent(wf, "", " ")
  22. if err != nil {
  23. return fmt.Errorf("failed to marshal workflow: %w", err)
  24. }
  25. if err := os.WriteFile(filename, data, 0644); err != nil {
  26. return fmt.Errorf("failed to write file: %w", err)
  27. }
  28. return nil
  29. }
  30. // LoadWorkflowFromJSON loads a workflow from a JSON string
  31. func LoadWorkflowFromJSON(jsonStr string) (*Workflow, error) {
  32. var wf Workflow
  33. if err := json.Unmarshal([]byte(jsonStr), &wf); err != nil {
  34. return nil, fmt.Errorf("failed to unmarshal workflow: %w", err)
  35. }
  36. return &wf, nil
  37. }
  38. // ToJSON converts a workflow to a JSON string
  39. func (w *Workflow) ToJSON() (string, error) {
  40. data, err := json.MarshalIndent(w, "", " ")
  41. if err != nil {
  42. return "", fmt.Errorf("failed to marshal workflow: %w", err)
  43. }
  44. return string(data), nil
  45. }
  46. // GetStepByID finds a step by its ID
  47. func (w *Workflow) GetStepByID(stepID string) *Step {
  48. for i := range w.Steps {
  49. if w.Steps[i].ID == stepID {
  50. return &w.Steps[i]
  51. }
  52. }
  53. return nil
  54. }
  55. // GetStepsByType returns all steps of a specific type
  56. func (w *Workflow) GetStepsByType(stepType StepType) []Step {
  57. var steps []Step
  58. prefix := string(stepType) + "_"
  59. for _, step := range w.Steps {
  60. if len(step.ID) > len(prefix) && step.ID[:len(prefix)] == prefix {
  61. steps = append(steps, step)
  62. }
  63. }
  64. return steps
  65. }
  66. // ValidateStepReferences validates that all step references (next, children, cases) exist
  67. func (w *Workflow) ValidateStepReferences() error {
  68. stepIDs := make(map[string]bool)
  69. for _, step := range w.Steps {
  70. stepIDs[step.ID] = true
  71. }
  72. for _, step := range w.Steps {
  73. // Validate next
  74. if step.Next != "" && !stepIDs[step.Next] {
  75. return fmt.Errorf("step %s references non-existent next step: %s", step.ID, step.Next)
  76. }
  77. // Validate children
  78. for _, childID := range step.Children {
  79. if !stepIDs[childID] {
  80. return fmt.Errorf("step %s references non-existent child step: %s", step.ID, childID)
  81. }
  82. }
  83. // Validate cases
  84. for _, c := range step.Cases {
  85. if len(c) >= 2 && c[1] != "" && !stepIDs[c[1]] {
  86. return fmt.Errorf("step %s references non-existent case step: %s", step.ID, c[1])
  87. }
  88. }
  89. // Validate onError (v3.10+)
  90. if step.OnError != "" && !stepIDs[step.OnError] {
  91. return fmt.Errorf("step %s references non-existent onError step: %s", step.ID, step.OnError)
  92. }
  93. }
  94. return nil
  95. }
  96. // Clone creates a deep copy of the workflow
  97. func (w *Workflow) Clone() (*Workflow, error) {
  98. data, err := json.Marshal(w)
  99. if err != nil {
  100. return nil, fmt.Errorf("failed to marshal workflow: %w", err)
  101. }
  102. var clone Workflow
  103. if err := json.Unmarshal(data, &clone); err != nil {
  104. return nil, fmt.Errorf("failed to unmarshal workflow: %w", err)
  105. }
  106. return &clone, nil
  107. }
  108. // GetVariableNames returns all declared variable names
  109. func (r *Registry) GetVariableNames() ([]string, error) {
  110. var names []string
  111. for _, varDecl := range r.Vars {
  112. parsed, err := ParseVariableDeclaration(varDecl)
  113. if err != nil {
  114. return nil, err
  115. }
  116. names = append(names, parsed.Name)
  117. }
  118. return names, nil
  119. }
  120. // GetServiceNames returns all declared service names
  121. func (r *Registry) GetServiceNames() ([]string, error) {
  122. var names []string
  123. for _, sig := range r.Services {
  124. parsed, err := ParseServiceSignature(sig)
  125. if err != nil {
  126. return nil, err
  127. }
  128. names = append(names, parsed.Name)
  129. }
  130. return names, nil
  131. }
  132. // BuildWorkflow provides a fluent builder for creating workflows
  133. type WorkflowBuilder struct {
  134. workflow *Workflow
  135. }
  136. // NewWorkflowBuilder creates a new workflow builder
  137. func NewWorkflowBuilder(name string) *WorkflowBuilder {
  138. return &WorkflowBuilder{
  139. workflow: &Workflow{
  140. Version: "3.13",
  141. Name: name,
  142. Registry: Registry{
  143. Services: []string{},
  144. APIs: []APIDefinition{},
  145. Components: []string{},
  146. Vars: []string{},
  147. Files: FilesRegistry{
  148. Inputs: []string{},
  149. Artifacts: []string{},
  150. },
  151. },
  152. Steps: []Step{},
  153. },
  154. }
  155. }
  156. // AddService adds a service to the registry
  157. func (b *WorkflowBuilder) AddService(signature string) *WorkflowBuilder {
  158. b.workflow.Registry.Services = append(b.workflow.Registry.Services, signature)
  159. return b
  160. }
  161. // AddAPI adds an API definition to the registry
  162. func (b *WorkflowBuilder) AddAPI(apiDef APIDefinition) *WorkflowBuilder {
  163. b.workflow.Registry.APIs = append(b.workflow.Registry.APIs, apiDef)
  164. return b
  165. }
  166. // AddComponent adds a component to the registry
  167. func (b *WorkflowBuilder) AddComponent(componentID string) *WorkflowBuilder {
  168. b.workflow.Registry.Components = append(b.workflow.Registry.Components, componentID)
  169. return b
  170. }
  171. // AddVar adds a variable to the registry
  172. func (b *WorkflowBuilder) AddVar(varDecl string) *WorkflowBuilder {
  173. b.workflow.Registry.Vars = append(b.workflow.Registry.Vars, varDecl)
  174. return b
  175. }
  176. // AddInput adds an input file pattern
  177. func (b *WorkflowBuilder) AddInput(pattern string) *WorkflowBuilder {
  178. b.workflow.Registry.Files.Inputs = append(b.workflow.Registry.Files.Inputs, pattern)
  179. return b
  180. }
  181. // AddArtifact adds an artifact file pattern
  182. func (b *WorkflowBuilder) AddArtifact(pattern string) *WorkflowBuilder {
  183. b.workflow.Registry.Files.Artifacts = append(b.workflow.Registry.Files.Artifacts, pattern)
  184. return b
  185. }
  186. // AddStep adds a step to the workflow
  187. func (b *WorkflowBuilder) AddStep(step Step) *WorkflowBuilder {
  188. b.workflow.Steps = append(b.workflow.Steps, step)
  189. return b
  190. }
  191. // Build returns the constructed workflow
  192. func (b *WorkflowBuilder) Build() (*Workflow, error) {
  193. if err := b.workflow.Validate(); err != nil {
  194. return nil, err
  195. }
  196. return b.workflow, nil
  197. }
  198. // MustBuild returns the workflow or panics on error
  199. func (b *WorkflowBuilder) MustBuild() *Workflow {
  200. wf, err := b.Build()
  201. if err != nil {
  202. panic(err)
  203. }
  204. return wf
  205. }