|
@@ -64,6 +64,11 @@ Workflow nodes represent individual steps in a running workflow.`,
|
|
|
Usage: "Create a new workflow template",
|
|
Usage: "Create a new workflow template",
|
|
|
Action: workflowTemplateCreate,
|
|
Action: workflowTemplateCreate,
|
|
|
Flags: []cli.Flag{
|
|
Flags: []cli.Flag{
|
|
|
|
|
+ &cli.StringFlag{
|
|
|
|
|
+ Name: "file",
|
|
|
|
|
+ Aliases: []string{"f"},
|
|
|
|
|
+ Usage: "Path to JSON file containing template data",
|
|
|
|
|
+ },
|
|
|
&cli.StringFlag{
|
|
&cli.StringFlag{
|
|
|
Name: "name",
|
|
Name: "name",
|
|
|
Aliases: []string{"n"},
|
|
Aliases: []string{"n"},
|
|
@@ -76,7 +81,7 @@ Workflow nodes represent individual steps in a running workflow.`,
|
|
|
},
|
|
},
|
|
|
&cli.StringFlag{
|
|
&cli.StringFlag{
|
|
|
Name: "definition",
|
|
Name: "definition",
|
|
|
- Aliases: []string{"f"},
|
|
|
|
|
|
|
+ Aliases: []string{"D"},
|
|
|
Usage: "Workflow definition (JSON string or @filename)",
|
|
Usage: "Workflow definition (JSON string or @filename)",
|
|
|
},
|
|
},
|
|
|
&cli.BoolFlag{
|
|
&cli.BoolFlag{
|
|
@@ -136,6 +141,19 @@ Workflow nodes represent individual steps in a running workflow.`,
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
|
|
+ {
|
|
|
|
|
+ Name: "example",
|
|
|
|
|
+ Usage: "Generate an example template file",
|
|
|
|
|
+ Action: workflowTemplateExample,
|
|
|
|
|
+ Flags: []cli.Flag{
|
|
|
|
|
+ &cli.StringFlag{
|
|
|
|
|
+ Name: "output",
|
|
|
|
|
+ Aliases: []string{"o"},
|
|
|
|
|
+ Usage: "Output file path",
|
|
|
|
|
+ Value: "workflow_template.json",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
// WorkflowInstance commands
|
|
// WorkflowInstance commands
|
|
@@ -273,6 +291,14 @@ Workflow nodes represent individual steps in a running workflow.`,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// WorkflowTemplateFile represents a workflow template stored in a JSON file
|
|
|
|
|
+type WorkflowTemplateFile struct {
|
|
|
|
|
+ Name string `json:"name"`
|
|
|
|
|
+ Description string `json:"description"`
|
|
|
|
|
+ Definition interface{} `json:"definition"`
|
|
|
|
|
+ IsActive bool `json:"isActive"`
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// WorkflowTemplate represents a workflow template
|
|
// WorkflowTemplate represents a workflow template
|
|
|
type WorkflowTemplate struct {
|
|
type WorkflowTemplate struct {
|
|
|
ID string `json:"id"`
|
|
ID string `json:"id"`
|
|
@@ -430,32 +456,71 @@ func workflowTemplateCreate(ctx context.Context, cmd *cli.Command) error {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- name := cmd.String("name")
|
|
|
|
|
- description := cmd.String("description")
|
|
|
|
|
- definition := cmd.String("definition")
|
|
|
|
|
- isActive := cmd.Bool("active")
|
|
|
|
|
|
|
+ var name, description, definition string
|
|
|
|
|
+ var isActive bool
|
|
|
|
|
|
|
|
- if name == "" {
|
|
|
|
|
- prompt := &survey.Input{Message: "Template name:"}
|
|
|
|
|
- if err := survey.AskOne(prompt, &name, survey.WithValidator(survey.Required)); err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
|
|
+ // Check if a file was provided
|
|
|
|
|
+ filePath := cmd.String("file")
|
|
|
|
|
+ if filePath != "" {
|
|
|
|
|
+ // Read and parse the template file
|
|
|
|
|
+ data, err := os.ReadFile(filePath)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("failed to read file: %w", err)
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- if description == "" {
|
|
|
|
|
- prompt := &survey.Input{Message: "Description (optional):"}
|
|
|
|
|
- survey.AskOne(prompt, &description)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ var templateFile WorkflowTemplateFile
|
|
|
|
|
+ if err := json.Unmarshal(data, &templateFile); err != nil {
|
|
|
|
|
+ return fmt.Errorf("failed to parse template file: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if definition == "" {
|
|
|
|
|
- prompt := &survey.Multiline{Message: "Workflow definition (JSON):"}
|
|
|
|
|
- if err := survey.AskOne(prompt, &definition, survey.WithValidator(survey.Required)); err != nil {
|
|
|
|
|
- return err
|
|
|
|
|
|
|
+ // Validate required fields
|
|
|
|
|
+ if templateFile.Name == "" {
|
|
|
|
|
+ return fmt.Errorf("name is required in template file")
|
|
|
|
|
+ }
|
|
|
|
|
+ if templateFile.Definition == nil {
|
|
|
|
|
+ return fmt.Errorf("definition is required in template file")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ name = templateFile.Name
|
|
|
|
|
+ description = templateFile.Description
|
|
|
|
|
+ isActive = templateFile.IsActive
|
|
|
|
|
+
|
|
|
|
|
+ // Marshal definition to JSON string
|
|
|
|
|
+ defBytes, err := json.Marshal(templateFile.Definition)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("failed to marshal definition: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ definition = string(defBytes)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Use command-line flags
|
|
|
|
|
+ name = cmd.String("name")
|
|
|
|
|
+ description = cmd.String("description")
|
|
|
|
|
+ definition = cmd.String("definition")
|
|
|
|
|
+ isActive = cmd.Bool("active")
|
|
|
|
|
+
|
|
|
|
|
+ // Interactive prompts for missing values
|
|
|
|
|
+ if name == "" {
|
|
|
|
|
+ prompt := &survey.Input{Message: "Template name:"}
|
|
|
|
|
+ if err := survey.AskOne(prompt, &name, survey.WithValidator(survey.Required)); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if description == "" {
|
|
|
|
|
+ prompt := &survey.Input{Message: "Description (optional):"}
|
|
|
|
|
+ survey.AskOne(prompt, &description)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if definition == "" {
|
|
|
|
|
+ prompt := &survey.Multiline{Message: "Workflow definition (JSON):"}
|
|
|
|
|
+ if err := survey.AskOne(prompt, &definition, survey.WithValidator(survey.Required)); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- // Handle file input for definition
|
|
|
|
|
- definition = ReadFileOrString(definition)
|
|
|
|
|
|
|
+ // Handle file input for definition
|
|
|
|
|
+ definition = ReadFileOrString(definition)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
mutation := `mutation CreateWorkflowTemplate($input: NewWorkflowTemplate!) { createWorkflowTemplate(input: $input) { id name description definition isActive createdBy { id email } createdAt updatedAt } }`
|
|
mutation := `mutation CreateWorkflowTemplate($input: NewWorkflowTemplate!) { createWorkflowTemplate(input: $input) { id name description definition isActive createdBy { id email } createdAt updatedAt } }`
|
|
|
|
|
|
|
@@ -604,6 +669,67 @@ func workflowTemplateDelete(ctx context.Context, cmd *cli.Command) error {
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func workflowTemplateExample(ctx context.Context, cmd *cli.Command) error {
|
|
|
|
|
+ outputPath := cmd.String("output")
|
|
|
|
|
+
|
|
|
|
|
+ // Example template content
|
|
|
|
|
+ exampleTemplate := WorkflowTemplateFile{
|
|
|
|
|
+ Name: "Standard Approval Workflow",
|
|
|
|
|
+ Description: "A simple linear approval workflow for testing",
|
|
|
|
|
+ Definition: map[string]interface{}{
|
|
|
|
|
+ "nodes": map[string]interface{}{
|
|
|
|
|
+ "start": map[string]interface{}{
|
|
|
|
|
+ "type": "task",
|
|
|
|
|
+ "title": "Initial Review",
|
|
|
|
|
+ "content": "Review the incoming request",
|
|
|
|
|
+ "assignee": "1",
|
|
|
|
|
+ "dependsOn": []interface{}{},
|
|
|
|
|
+ },
|
|
|
|
|
+ "analysis": map[string]interface{}{
|
|
|
|
|
+ "type": "task",
|
|
|
|
|
+ "title": "Data Analysis",
|
|
|
|
|
+ "content": "Analyze the data",
|
|
|
|
|
+ "assignee": "2",
|
|
|
|
|
+ "dependsOn": []interface{}{"start"},
|
|
|
|
|
+ },
|
|
|
|
|
+ "approval": map[string]interface{}{
|
|
|
|
|
+ "type": "task",
|
|
|
|
|
+ "title": "Manager Approval",
|
|
|
|
|
+ "content": "Approve the request",
|
|
|
|
|
+ "assignee": "3",
|
|
|
|
|
+ "dependsOn": []interface{}{"analysis"},
|
|
|
|
|
+ },
|
|
|
|
|
+ "complete": map[string]interface{}{
|
|
|
|
|
+ "type": "task",
|
|
|
|
|
+ "title": "Complete",
|
|
|
|
|
+ "content": "Finalize",
|
|
|
|
|
+ "assignee": "1",
|
|
|
|
|
+ "dependsOn": []interface{}{"approval"},
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ IsActive: true,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Marshal to JSON with indentation
|
|
|
|
|
+ data, err := json.MarshalIndent(exampleTemplate, "", " ")
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("failed to marshal example template: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Write to file
|
|
|
|
|
+ err = os.WriteFile(outputPath, data, 0644)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("failed to write example file: %w", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fmt.Printf("Example workflow template written to: %s\n", outputPath)
|
|
|
|
|
+ fmt.Println("Edit the file to customize your workflow, then use:")
|
|
|
|
|
+ fmt.Printf(" arp_cli workflow template create --file %s\n", outputPath)
|
|
|
|
|
+
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// WorkflowInstance operations
|
|
// WorkflowInstance operations
|
|
|
|
|
|
|
|
func workflowInstanceList(ctx context.Context, cmd *cli.Command) error {
|
|
func workflowInstanceList(ctx context.Context, cmd *cli.Command) error {
|