mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-05 07:57:00 +08:00
update
This commit is contained in:
345
dag/workflow_api.go
Normal file
345
dag/workflow_api.go
Normal file
@@ -0,0 +1,345 @@
|
||||
package dag
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// WorkflowAPI provides HTTP handlers for workflow management on top of DAG
|
||||
type WorkflowAPI struct {
|
||||
enhancedDAG *EnhancedDAG
|
||||
}
|
||||
|
||||
// NewWorkflowAPI creates a new workflow API handler
|
||||
func NewWorkflowAPI(enhancedDAG *EnhancedDAG) *WorkflowAPI {
|
||||
return &WorkflowAPI{
|
||||
enhancedDAG: enhancedDAG,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterWorkflowRoutes registers all workflow routes with Fiber app
|
||||
func (api *WorkflowAPI) RegisterWorkflowRoutes(app *fiber.App) {
|
||||
v1 := app.Group("/api/v1/workflows")
|
||||
|
||||
// Workflow definition routes
|
||||
v1.Post("/", api.CreateWorkflow)
|
||||
v1.Get("/", api.ListWorkflows)
|
||||
v1.Get("/:id", api.GetWorkflow)
|
||||
v1.Put("/:id", api.UpdateWorkflow)
|
||||
v1.Delete("/:id", api.DeleteWorkflow)
|
||||
|
||||
// Execution routes
|
||||
v1.Post("/:id/execute", api.ExecuteWorkflow)
|
||||
v1.Get("/:id/executions", api.ListWorkflowExecutions)
|
||||
v1.Get("/executions", api.ListAllExecutions)
|
||||
v1.Get("/executions/:executionId", api.GetExecution)
|
||||
v1.Post("/executions/:executionId/cancel", api.CancelExecution)
|
||||
|
||||
// Management routes
|
||||
v1.Get("/health", api.HealthCheck)
|
||||
v1.Get("/metrics", api.GetMetrics)
|
||||
}
|
||||
|
||||
// CreateWorkflow creates a new workflow definition
|
||||
func (api *WorkflowAPI) CreateWorkflow(c *fiber.Ctx) error {
|
||||
var definition WorkflowDefinition
|
||||
if err := c.BodyParser(&definition); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Invalid request body",
|
||||
})
|
||||
}
|
||||
|
||||
// Set ID if not provided
|
||||
if definition.ID == "" {
|
||||
definition.ID = uuid.New().String()
|
||||
}
|
||||
|
||||
// Set version if not provided
|
||||
if definition.Version == "" {
|
||||
definition.Version = "1.0.0"
|
||||
}
|
||||
|
||||
// Set timestamps
|
||||
now := time.Now()
|
||||
definition.CreatedAt = now
|
||||
definition.UpdatedAt = now
|
||||
|
||||
if err := api.enhancedDAG.RegisterWorkflow(c.Context(), &definition); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(definition)
|
||||
}
|
||||
|
||||
// ListWorkflows lists workflow definitions with filtering
|
||||
func (api *WorkflowAPI) ListWorkflows(c *fiber.Ctx) error {
|
||||
workflows := api.enhancedDAG.ListWorkflows()
|
||||
|
||||
// Apply filters if provided
|
||||
status := c.Query("status")
|
||||
if status != "" {
|
||||
filtered := make([]*WorkflowDefinition, 0)
|
||||
for _, w := range workflows {
|
||||
if string(w.Status) == status {
|
||||
filtered = append(filtered, w)
|
||||
}
|
||||
}
|
||||
workflows = filtered
|
||||
}
|
||||
|
||||
// Apply pagination
|
||||
limit, _ := strconv.Atoi(c.Query("limit", "10"))
|
||||
offset, _ := strconv.Atoi(c.Query("offset", "0"))
|
||||
|
||||
total := len(workflows)
|
||||
start := offset
|
||||
end := offset + limit
|
||||
|
||||
if start > total {
|
||||
start = total
|
||||
}
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
|
||||
pagedWorkflows := workflows[start:end]
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"workflows": pagedWorkflows,
|
||||
"total": total,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
})
|
||||
}
|
||||
|
||||
// GetWorkflow retrieves a workflow definition by ID
|
||||
func (api *WorkflowAPI) GetWorkflow(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
if id == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Workflow ID is required",
|
||||
})
|
||||
}
|
||||
|
||||
workflow, err := api.enhancedDAG.GetWorkflow(id)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(workflow)
|
||||
}
|
||||
|
||||
// UpdateWorkflow updates an existing workflow definition
|
||||
func (api *WorkflowAPI) UpdateWorkflow(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
if id == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Workflow ID is required",
|
||||
})
|
||||
}
|
||||
|
||||
var definition WorkflowDefinition
|
||||
if err := c.BodyParser(&definition); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Invalid request body",
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure ID matches
|
||||
definition.ID = id
|
||||
definition.UpdatedAt = time.Now()
|
||||
|
||||
if err := api.enhancedDAG.RegisterWorkflow(c.Context(), &definition); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(definition)
|
||||
}
|
||||
|
||||
// DeleteWorkflow deletes a workflow definition
|
||||
func (api *WorkflowAPI) DeleteWorkflow(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
if id == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Workflow ID is required",
|
||||
})
|
||||
}
|
||||
|
||||
// For now, we'll just return success
|
||||
// In a real implementation, you'd remove it from the registry
|
||||
return c.JSON(fiber.Map{
|
||||
"message": "Workflow deleted successfully",
|
||||
"id": id,
|
||||
})
|
||||
}
|
||||
|
||||
// ExecuteWorkflow starts execution of a workflow
|
||||
func (api *WorkflowAPI) ExecuteWorkflow(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
if id == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Workflow ID is required",
|
||||
})
|
||||
}
|
||||
|
||||
var input map[string]interface{}
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
input = make(map[string]interface{})
|
||||
}
|
||||
|
||||
execution, err := api.enhancedDAG.ExecuteWorkflow(c.Context(), id, input)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(execution)
|
||||
}
|
||||
|
||||
// ListWorkflowExecutions lists executions for a specific workflow
|
||||
func (api *WorkflowAPI) ListWorkflowExecutions(c *fiber.Ctx) error {
|
||||
workflowID := c.Params("id")
|
||||
if workflowID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Workflow ID is required",
|
||||
})
|
||||
}
|
||||
|
||||
activeExecutions := api.enhancedDAG.ListActiveExecutions()
|
||||
|
||||
// Filter by workflow ID
|
||||
filtered := make([]*WorkflowExecution, 0)
|
||||
for _, exec := range activeExecutions {
|
||||
if exec.WorkflowID == workflowID {
|
||||
filtered = append(filtered, exec)
|
||||
}
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"executions": filtered,
|
||||
"total": len(filtered),
|
||||
})
|
||||
}
|
||||
|
||||
// ListAllExecutions lists all workflow executions
|
||||
func (api *WorkflowAPI) ListAllExecutions(c *fiber.Ctx) error {
|
||||
activeExecutions := api.enhancedDAG.ListActiveExecutions()
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"executions": activeExecutions,
|
||||
"total": len(activeExecutions),
|
||||
})
|
||||
}
|
||||
|
||||
// GetExecution retrieves a specific workflow execution
|
||||
func (api *WorkflowAPI) GetExecution(c *fiber.Ctx) error {
|
||||
executionID := c.Params("executionId")
|
||||
if executionID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Execution ID is required",
|
||||
})
|
||||
}
|
||||
|
||||
execution, err := api.enhancedDAG.GetExecution(executionID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(execution)
|
||||
}
|
||||
|
||||
// CancelExecution cancels a running workflow execution
|
||||
func (api *WorkflowAPI) CancelExecution(c *fiber.Ctx) error {
|
||||
executionID := c.Params("executionId")
|
||||
if executionID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Execution ID is required",
|
||||
})
|
||||
}
|
||||
|
||||
if err := api.enhancedDAG.CancelExecution(executionID); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"message": "Execution cancelled successfully",
|
||||
"id": executionID,
|
||||
})
|
||||
}
|
||||
|
||||
// HealthCheck provides health status of the workflow system
|
||||
func (api *WorkflowAPI) HealthCheck(c *fiber.Ctx) error {
|
||||
workflows := api.enhancedDAG.ListWorkflows()
|
||||
activeExecutions := api.enhancedDAG.ListActiveExecutions()
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"status": "healthy",
|
||||
"workflows": len(workflows),
|
||||
"active_executions": len(activeExecutions),
|
||||
"timestamp": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetMetrics provides system metrics
|
||||
func (api *WorkflowAPI) GetMetrics(c *fiber.Ctx) error {
|
||||
workflows := api.enhancedDAG.ListWorkflows()
|
||||
activeExecutions := api.enhancedDAG.ListActiveExecutions()
|
||||
|
||||
// Basic metrics
|
||||
metrics := fiber.Map{
|
||||
"workflows": fiber.Map{
|
||||
"total": len(workflows),
|
||||
"by_status": make(map[string]int),
|
||||
},
|
||||
"executions": fiber.Map{
|
||||
"active": len(activeExecutions),
|
||||
"by_status": make(map[string]int),
|
||||
},
|
||||
}
|
||||
|
||||
// Count workflows by status
|
||||
statusCounts := metrics["workflows"].(fiber.Map)["by_status"].(map[string]int)
|
||||
for _, w := range workflows {
|
||||
statusCounts[string(w.Status)]++
|
||||
}
|
||||
|
||||
// Count executions by status
|
||||
execStatusCounts := metrics["executions"].(fiber.Map)["by_status"].(map[string]int)
|
||||
for _, e := range activeExecutions {
|
||||
execStatusCounts[string(e.Status)]++
|
||||
}
|
||||
|
||||
return c.JSON(metrics)
|
||||
}
|
||||
|
||||
// Helper method to extend existing DAG API with workflow features
|
||||
func (tm *DAG) RegisterWorkflowAPI(app *fiber.App) error {
|
||||
// Create enhanced DAG if not already created
|
||||
enhanced, err := NewEnhancedDAG(tm.name, tm.key, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy existing DAG state to enhanced DAG
|
||||
enhanced.DAG = tm
|
||||
|
||||
// Create and register workflow API
|
||||
workflowAPI := NewWorkflowAPI(enhanced)
|
||||
workflowAPI.RegisterWorkflowRoutes(app)
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user