mirror of
https://github.com/oarkflow/mq.git
synced 2025-09-27 12:22:08 +08:00
394 lines
11 KiB
Go
394 lines
11 KiB
Go
package workflow
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// ProcessorFactory creates processor instances for different node types
|
|
type ProcessorFactory struct {
|
|
processors map[string]func() Processor
|
|
}
|
|
|
|
// NewProcessorFactory creates a new processor factory with all registered processors
|
|
func NewProcessorFactory() *ProcessorFactory {
|
|
factory := &ProcessorFactory{
|
|
processors: make(map[string]func() Processor),
|
|
}
|
|
|
|
// Register basic processors
|
|
factory.RegisterProcessor("task", func() Processor { return &TaskProcessor{} })
|
|
factory.RegisterProcessor("api", func() Processor { return &APIProcessor{} })
|
|
factory.RegisterProcessor("transform", func() Processor { return &TransformProcessor{} })
|
|
factory.RegisterProcessor("decision", func() Processor { return &DecisionProcessor{} })
|
|
factory.RegisterProcessor("timer", func() Processor { return &TimerProcessor{} })
|
|
factory.RegisterProcessor("parallel", func() Processor { return &ParallelProcessor{} })
|
|
factory.RegisterProcessor("sequence", func() Processor { return &SequenceProcessor{} })
|
|
factory.RegisterProcessor("loop", func() Processor { return &LoopProcessor{} })
|
|
factory.RegisterProcessor("filter", func() Processor { return &FilterProcessor{} })
|
|
factory.RegisterProcessor("aggregator", func() Processor { return &AggregatorProcessor{} })
|
|
factory.RegisterProcessor("error", func() Processor { return &ErrorProcessor{} })
|
|
|
|
// Register advanced processors
|
|
factory.RegisterProcessor("subdag", func() Processor { return &SubDAGProcessor{} })
|
|
factory.RegisterProcessor("html", func() Processor { return &HTMLProcessor{} })
|
|
factory.RegisterProcessor("sms", func() Processor { return &SMSProcessor{} })
|
|
factory.RegisterProcessor("auth", func() Processor { return &AuthProcessor{} })
|
|
factory.RegisterProcessor("validator", func() Processor { return &ValidatorProcessor{} })
|
|
factory.RegisterProcessor("router", func() Processor { return &RouterProcessor{} })
|
|
factory.RegisterProcessor("storage", func() Processor { return &StorageProcessor{} })
|
|
factory.RegisterProcessor("notify", func() Processor { return &NotifyProcessor{} })
|
|
factory.RegisterProcessor("webhook_receiver", func() Processor { return &WebhookReceiverProcessor{} })
|
|
|
|
return factory
|
|
}
|
|
|
|
// RegisterProcessor registers a new processor type
|
|
func (f *ProcessorFactory) RegisterProcessor(nodeType string, creator func() Processor) {
|
|
f.processors[nodeType] = creator
|
|
}
|
|
|
|
// CreateProcessor creates a processor instance for the given node type
|
|
func (f *ProcessorFactory) CreateProcessor(nodeType string) (Processor, error) {
|
|
creator, exists := f.processors[nodeType]
|
|
if !exists {
|
|
return nil, fmt.Errorf("unknown processor type: %s", nodeType)
|
|
}
|
|
return creator(), nil
|
|
}
|
|
|
|
// Basic Processors
|
|
|
|
// TaskProcessor handles task execution
|
|
type TaskProcessor struct{}
|
|
|
|
func (p *TaskProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
log.Printf("Executing task: %s", input.Node.Name)
|
|
|
|
// Execute the task based on configuration
|
|
config := input.Node.Config
|
|
|
|
// Simulate task execution based on script or command
|
|
if config.Script != "" {
|
|
log.Printf("Executing script: %s", config.Script)
|
|
} else if config.Command != "" {
|
|
log.Printf("Executing command: %s", config.Command)
|
|
}
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
result := &ProcessingResult{
|
|
Success: true,
|
|
Data: map[string]interface{}{"task_completed": true, "task_name": input.Node.Name},
|
|
Message: fmt.Sprintf("Task %s completed successfully", input.Node.Name),
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// APIProcessor handles API calls
|
|
type APIProcessor struct{}
|
|
|
|
func (p *APIProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
config := input.Node.Config
|
|
|
|
url := config.URL
|
|
if url == "" {
|
|
return &ProcessingResult{
|
|
Success: false,
|
|
Error: "URL not specified in API configuration",
|
|
}, nil
|
|
}
|
|
|
|
method := "GET"
|
|
if config.Method != "" {
|
|
method = strings.ToUpper(config.Method)
|
|
}
|
|
|
|
log.Printf("Making %s request to %s", method, url)
|
|
|
|
// Simulate API call
|
|
time.Sleep(200 * time.Millisecond)
|
|
|
|
// Mock response
|
|
response := map[string]interface{}{
|
|
"status": "success",
|
|
"url": url,
|
|
"method": method,
|
|
"data": "mock response data",
|
|
}
|
|
|
|
return &ProcessingResult{
|
|
Success: true,
|
|
Data: response,
|
|
Message: fmt.Sprintf("API call to %s completed", url),
|
|
}, nil
|
|
}
|
|
|
|
// TransformProcessor handles data transformation
|
|
type TransformProcessor struct{}
|
|
|
|
func (p *TransformProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
config := input.Node.Config
|
|
|
|
// Get transformation rules from Custom config
|
|
transforms, ok := config.Custom["transforms"].(map[string]interface{})
|
|
if !ok {
|
|
return &ProcessingResult{
|
|
Success: false,
|
|
Error: "No transformation rules specified",
|
|
}, nil
|
|
}
|
|
|
|
// Apply transformations to input data
|
|
result := make(map[string]interface{})
|
|
for key, rule := range transforms {
|
|
// Simple field mapping for now
|
|
if sourceField, ok := rule.(string); ok {
|
|
if value, exists := input.Data[sourceField]; exists {
|
|
result[key] = value
|
|
}
|
|
}
|
|
}
|
|
|
|
return &ProcessingResult{
|
|
Success: true,
|
|
Data: result,
|
|
Message: "Data transformation completed",
|
|
}, nil
|
|
}
|
|
|
|
// DecisionProcessor handles conditional logic
|
|
type DecisionProcessor struct{}
|
|
|
|
func (p *DecisionProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
config := input.Node.Config
|
|
|
|
condition := config.Condition
|
|
if condition == "" {
|
|
return &ProcessingResult{
|
|
Success: false,
|
|
Error: "No condition specified",
|
|
}, nil
|
|
}
|
|
|
|
// Simple condition evaluation
|
|
decision := p.evaluateCondition(condition, input.Data)
|
|
|
|
result := &ProcessingResult{
|
|
Success: true,
|
|
Data: map[string]interface{}{
|
|
"decision": decision,
|
|
"condition": condition,
|
|
},
|
|
Message: fmt.Sprintf("Decision made: %t", decision),
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (p *DecisionProcessor) evaluateCondition(condition string, data map[string]interface{}) bool {
|
|
// Simple condition evaluation - in real implementation, use expression parser
|
|
if strings.Contains(condition, "==") {
|
|
parts := strings.Split(condition, "==")
|
|
if len(parts) == 2 {
|
|
field := strings.TrimSpace(parts[0])
|
|
expectedValue := strings.TrimSpace(strings.Trim(parts[1], "\"'"))
|
|
|
|
if value, exists := data[field]; exists {
|
|
return fmt.Sprintf("%v", value) == expectedValue
|
|
}
|
|
}
|
|
}
|
|
|
|
// Default to true for simplicity
|
|
return true
|
|
}
|
|
|
|
// TimerProcessor handles time-based operations
|
|
type TimerProcessor struct{}
|
|
|
|
func (p *TimerProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
config := input.Node.Config
|
|
|
|
duration := 1 * time.Second
|
|
if config.Duration > 0 {
|
|
duration = config.Duration
|
|
} else if config.Schedule != "" {
|
|
// Simple schedule parsing - just use 1 second for demo
|
|
duration = 1 * time.Second
|
|
}
|
|
|
|
log.Printf("Timer waiting for %v", duration)
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return &ProcessingResult{
|
|
Success: false,
|
|
Error: "Timer cancelled",
|
|
}, ctx.Err()
|
|
case <-time.After(duration):
|
|
return &ProcessingResult{
|
|
Success: true,
|
|
Data: map[string]interface{}{"waited": duration.String()},
|
|
Message: fmt.Sprintf("Timer completed after %v", duration),
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
// ParallelProcessor handles parallel execution
|
|
type ParallelProcessor struct{}
|
|
|
|
func (p *ParallelProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
// This would typically trigger parallel execution of child nodes
|
|
// For now, just return success
|
|
return &ProcessingResult{
|
|
Success: true,
|
|
Data: map[string]interface{}{"parallel_execution": "started"},
|
|
Message: "Parallel execution initiated",
|
|
}, nil
|
|
}
|
|
|
|
// SequenceProcessor handles sequential execution
|
|
type SequenceProcessor struct{}
|
|
|
|
func (p *SequenceProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
// This would typically ensure sequential execution of child nodes
|
|
// For now, just return success
|
|
return &ProcessingResult{
|
|
Success: true,
|
|
Data: map[string]interface{}{"sequence_execution": "started"},
|
|
Message: "Sequential execution initiated",
|
|
}, nil
|
|
}
|
|
|
|
// LoopProcessor handles loop operations
|
|
type LoopProcessor struct{}
|
|
|
|
func (p *LoopProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
config := input.Node.Config
|
|
|
|
iterations := 1
|
|
if iterValue, ok := config.Custom["iterations"].(float64); ok {
|
|
iterations = int(iterValue)
|
|
}
|
|
|
|
results := make([]interface{}, 0, iterations)
|
|
|
|
for i := 0; i < iterations; i++ {
|
|
// In real implementation, this would execute child nodes
|
|
results = append(results, map[string]interface{}{
|
|
"iteration": i + 1,
|
|
"data": input.Data,
|
|
})
|
|
}
|
|
|
|
return &ProcessingResult{
|
|
Success: true,
|
|
Data: map[string]interface{}{"loop_results": results},
|
|
Message: fmt.Sprintf("Loop completed %d iterations", iterations),
|
|
}, nil
|
|
}
|
|
|
|
// FilterProcessor handles data filtering
|
|
type FilterProcessor struct{}
|
|
|
|
func (p *FilterProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
config := input.Node.Config
|
|
|
|
filterField, ok := config.Custom["field"].(string)
|
|
if !ok {
|
|
return &ProcessingResult{
|
|
Success: false,
|
|
Error: "No filter field specified",
|
|
}, nil
|
|
}
|
|
|
|
filterValue := config.Custom["value"]
|
|
|
|
// Simple filtering logic
|
|
if value, exists := input.Data[filterField]; exists {
|
|
if fmt.Sprintf("%v", value) == fmt.Sprintf("%v", filterValue) {
|
|
return &ProcessingResult{
|
|
Success: true,
|
|
Data: input.Data,
|
|
Message: "Filter passed",
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
return &ProcessingResult{
|
|
Success: false,
|
|
Data: nil,
|
|
Message: "Filter failed",
|
|
}, nil
|
|
}
|
|
|
|
// AggregatorProcessor handles data aggregation
|
|
type AggregatorProcessor struct{}
|
|
|
|
func (p *AggregatorProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
config := input.Node.Config
|
|
|
|
operation := "sum"
|
|
if op, ok := config.Custom["operation"].(string); ok {
|
|
operation = op
|
|
}
|
|
|
|
field, ok := config.Custom["field"].(string)
|
|
if !ok {
|
|
return &ProcessingResult{
|
|
Success: false,
|
|
Error: "No aggregation field specified",
|
|
}, nil
|
|
}
|
|
|
|
// Simple aggregation - in real implementation, collect data from multiple sources
|
|
value := input.Data[field]
|
|
|
|
result := map[string]interface{}{
|
|
"operation": operation,
|
|
"field": field,
|
|
"result": value,
|
|
}
|
|
|
|
return &ProcessingResult{
|
|
Success: true,
|
|
Data: result,
|
|
Message: fmt.Sprintf("Aggregation completed: %s on %s", operation, field),
|
|
}, nil
|
|
}
|
|
|
|
// ErrorProcessor handles error scenarios
|
|
type ErrorProcessor struct{}
|
|
|
|
func (p *ErrorProcessor) Process(ctx context.Context, input ProcessingContext) (*ProcessingResult, error) {
|
|
config := input.Node.Config
|
|
|
|
errorMessage := "Simulated error"
|
|
if msg, ok := config.Custom["message"].(string); ok {
|
|
errorMessage = msg
|
|
}
|
|
|
|
shouldFail := true
|
|
if fail, ok := config.Custom["fail"].(bool); ok {
|
|
shouldFail = fail
|
|
}
|
|
|
|
if shouldFail {
|
|
return &ProcessingResult{
|
|
Success: false,
|
|
Error: errorMessage,
|
|
}, nil
|
|
}
|
|
|
|
return &ProcessingResult{
|
|
Success: true,
|
|
Data: map[string]interface{}{"error_handled": true},
|
|
Message: "Error processor completed without error",
|
|
}, nil
|
|
}
|