mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-07 08:50:54 +08:00
update
This commit is contained in:
261
workflow/json-engine/blog-engine.json
Normal file
261
workflow/json-engine/blog-engine.json
Normal file
File diff suppressed because one or more lines are too long
276
workflow/json-engine/email-campaign.json
Normal file
276
workflow/json-engine/email-campaign.json
Normal file
File diff suppressed because one or more lines are too long
@@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
htmlTemplate "text/template"
|
||||
"time"
|
||||
@@ -17,36 +18,104 @@ import (
|
||||
"github.com/oarkflow/mq/workflow"
|
||||
)
|
||||
|
||||
// Helper functions for default values
|
||||
func getDefaultInt(value, defaultValue int) int {
|
||||
if value != 0 {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getDefaultString(value, defaultValue string) string {
|
||||
if value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getDefaultBool(value, defaultValue bool) bool {
|
||||
if value {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getDefaultStringSlice(value, defaultValue []string) []string {
|
||||
if value != nil && len(value) > 0 {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getDefaultDuration(value string, defaultValue time.Duration) time.Duration {
|
||||
if value != "" {
|
||||
if d, err := time.ParseDuration(value); err == nil {
|
||||
return d
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// NewJSONEngine creates a new JSON-driven workflow engine
|
||||
func NewJSONEngine() *JSONEngine {
|
||||
// Initialize real workflow engine with default config
|
||||
workflowConfig := &workflow.Config{
|
||||
func NewJSONEngine(config *AppConfiguration) *JSONEngine {
|
||||
// Use configuration from JSON or defaults
|
||||
var workflowEngineConfig *WorkflowEngineConfig
|
||||
if config.WorkflowEngine != nil {
|
||||
workflowEngineConfig = config.WorkflowEngine
|
||||
} else {
|
||||
// Default configuration
|
||||
workflowEngineConfig = &WorkflowEngineConfig{
|
||||
MaxWorkers: 4,
|
||||
ExecutionTimeout: 30 * time.Second,
|
||||
ExecutionTimeout: "30s",
|
||||
EnableMetrics: true,
|
||||
EnableAudit: true,
|
||||
EnableTracing: true,
|
||||
LogLevel: "info",
|
||||
Storage: workflow.StorageConfig{
|
||||
Storage: StorageConfig{
|
||||
Type: "memory",
|
||||
MaxConnections: 10,
|
||||
},
|
||||
Security: workflow.SecurityConfig{
|
||||
Security: SecurityConfig{
|
||||
EnableAuth: false,
|
||||
AllowedOrigins: []string{"*"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
workflowConfig := &workflow.Config{
|
||||
MaxWorkers: workflowEngineConfig.MaxWorkers,
|
||||
ExecutionTimeout: getDefaultDuration(workflowEngineConfig.ExecutionTimeout, 30*time.Second),
|
||||
EnableMetrics: workflowEngineConfig.EnableMetrics,
|
||||
EnableAudit: workflowEngineConfig.EnableAudit,
|
||||
EnableTracing: workflowEngineConfig.EnableTracing,
|
||||
LogLevel: getDefaultString(workflowEngineConfig.LogLevel, "info"),
|
||||
Storage: workflow.StorageConfig{
|
||||
Type: getDefaultString(workflowEngineConfig.Storage.Type, "memory"),
|
||||
MaxConnections: getDefaultInt(workflowEngineConfig.Storage.MaxConnections, 10),
|
||||
},
|
||||
Security: workflow.SecurityConfig{
|
||||
EnableAuth: workflowEngineConfig.Security.EnableAuth,
|
||||
AllowedOrigins: getDefaultStringSlice(workflowEngineConfig.Security.AllowedOrigins, []string{"*"}),
|
||||
},
|
||||
}
|
||||
workflowEngine := workflow.NewWorkflowEngine(workflowConfig)
|
||||
|
||||
return &JSONEngine{
|
||||
engine := &JSONEngine{
|
||||
workflowEngine: workflowEngine,
|
||||
workflowEngineConfig: workflowEngineConfig,
|
||||
templates: make(map[string]*Template),
|
||||
workflows: make(map[string]*Workflow),
|
||||
functions: make(map[string]*Function),
|
||||
validators: make(map[string]*Validator),
|
||||
middleware: make(map[string]*Middleware),
|
||||
data: make(map[string]interface{}),
|
||||
genericData: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// Store the configuration
|
||||
engine.config = config
|
||||
|
||||
return engine
|
||||
}
|
||||
|
||||
// LoadConfiguration loads and parses JSON configuration
|
||||
@@ -69,6 +138,19 @@ func (e *JSONEngine) LoadConfiguration(configPath string) error {
|
||||
func (e *JSONEngine) Compile() error {
|
||||
log.Println("🔨 Compiling JSON configuration...")
|
||||
|
||||
// Store global data and merge with genericData
|
||||
e.data = e.config.Data
|
||||
|
||||
// Initialize genericData with config data for backward compatibility
|
||||
if e.genericData == nil {
|
||||
e.genericData = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// Merge config data into genericData
|
||||
for key, value := range e.config.Data {
|
||||
e.genericData[key] = value
|
||||
}
|
||||
|
||||
// 1. Compile templates
|
||||
if err := e.compileTemplates(); err != nil {
|
||||
return fmt.Errorf("template compilation failed: %v", err)
|
||||
@@ -94,9 +176,19 @@ func (e *JSONEngine) Compile() error {
|
||||
return fmt.Errorf("middleware compilation failed: %v", err)
|
||||
}
|
||||
|
||||
// 6. Store global data
|
||||
// 6. Store global data and merge with genericData
|
||||
e.data = e.config.Data
|
||||
|
||||
// Initialize genericData with config data for backward compatibility
|
||||
if e.genericData == nil {
|
||||
e.genericData = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// Merge config data into genericData
|
||||
for key, value := range e.config.Data {
|
||||
e.genericData[key] = value
|
||||
}
|
||||
|
||||
log.Println("✅ JSON configuration compiled successfully")
|
||||
return nil
|
||||
}
|
||||
@@ -171,7 +263,7 @@ func (e *JSONEngine) compileTemplates() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// compileFunctions compiles all function definitions
|
||||
// compileFunctions compiles all function definitions dynamically
|
||||
func (e *JSONEngine) compileFunctions() error {
|
||||
for id, functionConfig := range e.config.Functions {
|
||||
log.Printf("Compiling function: %s", id)
|
||||
@@ -179,9 +271,10 @@ func (e *JSONEngine) compileFunctions() error {
|
||||
function := &Function{
|
||||
ID: id,
|
||||
Config: functionConfig,
|
||||
Runtime: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// Compile function based on type
|
||||
// Compile function based on type - completely generic approach
|
||||
switch functionConfig.Type {
|
||||
case "http":
|
||||
function.Handler = e.createHTTPFunction(functionConfig)
|
||||
@@ -189,10 +282,15 @@ func (e *JSONEngine) compileFunctions() error {
|
||||
function.Handler = e.createExpressionFunction(functionConfig)
|
||||
case "template":
|
||||
function.Handler = e.createTemplateFunction(functionConfig)
|
||||
case "js":
|
||||
case "js", "javascript":
|
||||
function.Handler = e.createJSFunction(functionConfig)
|
||||
case "builtin":
|
||||
function.Handler = e.createBuiltinFunction(functionConfig)
|
||||
case "custom":
|
||||
function.Handler = e.createCustomFunction(functionConfig)
|
||||
default:
|
||||
return fmt.Errorf("unknown function type: %s", functionConfig.Type)
|
||||
// For unknown types, create a generic function that can be extended
|
||||
function.Handler = e.createGenericFunction(functionConfig)
|
||||
}
|
||||
|
||||
e.functions[id] = function
|
||||
@@ -200,7 +298,7 @@ func (e *JSONEngine) compileFunctions() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// compileValidators compiles all validators
|
||||
// compileValidators compiles all validators with generic approach
|
||||
func (e *JSONEngine) compileValidators() error {
|
||||
for id, validatorConfig := range e.config.Validators {
|
||||
log.Printf("Compiling validator: %s", id)
|
||||
@@ -208,7 +306,8 @@ func (e *JSONEngine) compileValidators() error {
|
||||
e.validators[id] = &Validator{
|
||||
ID: id,
|
||||
Config: validatorConfig,
|
||||
Rules: validatorConfig.Rules,
|
||||
Rules: validatorConfig.Rules, // Now using generic map
|
||||
Runtime: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -357,7 +456,7 @@ func (e *JSONEngine) createRouteHandler(routeConfig RouteConfig) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
log.Printf("Handler called for route: %s %s", routeConfig.Method, routeConfig.Path)
|
||||
|
||||
// Create execution context
|
||||
// Create execution context with enhanced generic data
|
||||
ctx := &ExecutionContext{
|
||||
Request: c,
|
||||
Data: make(map[string]interface{}),
|
||||
@@ -366,9 +465,18 @@ func (e *JSONEngine) createRouteHandler(routeConfig RouteConfig) fiber.Handler {
|
||||
User: make(map[string]interface{}),
|
||||
Functions: e.functions,
|
||||
Validators: e.validators,
|
||||
Config: e.config,
|
||||
Runtime: make(map[string]interface{}),
|
||||
Context: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
// Apply route middleware - skip auth middleware as we handle it below
|
||||
// Add global and generic data to context
|
||||
for k, v := range e.data {
|
||||
ctx.Data[k] = v
|
||||
}
|
||||
for k, v := range e.genericData {
|
||||
ctx.Data[k] = v
|
||||
} // Apply route middleware - skip auth middleware as we handle it below
|
||||
for _, middlewareID := range routeConfig.Middleware {
|
||||
if middlewareID == "auth" {
|
||||
continue // Skip auth middleware, handle it in route
|
||||
@@ -391,7 +499,7 @@ func (e *JSONEngine) createRouteHandler(routeConfig RouteConfig) fiber.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// Execute handler based on type
|
||||
// Execute handler based on type - with generic fallback
|
||||
switch routeConfig.Handler.Type {
|
||||
case "template":
|
||||
return e.handleTemplate(ctx, routeConfig)
|
||||
@@ -403,8 +511,13 @@ func (e *JSONEngine) createRouteHandler(routeConfig RouteConfig) fiber.Handler {
|
||||
return c.Redirect(routeConfig.Handler.Target)
|
||||
case "static":
|
||||
return e.handleStatic(ctx, routeConfig)
|
||||
case "json":
|
||||
return e.handleJSON(ctx, routeConfig)
|
||||
case "api":
|
||||
return e.handleAPI(ctx, routeConfig)
|
||||
default:
|
||||
return c.Status(500).JSON(fiber.Map{"error": "Unknown handler type: " + routeConfig.Handler.Type})
|
||||
// Generic handler for unknown types
|
||||
return e.handleGeneric(ctx, routeConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,6 +548,37 @@ func (e *JSONEngine) handleTemplate(ctx *ExecutionContext, routeConfig RouteConf
|
||||
data[k] = v
|
||||
}
|
||||
|
||||
// For employee_form template, ensure proper employee data structure
|
||||
if templateID == "employee_form" {
|
||||
if emp, exists := data["employee"]; !exists || emp == nil {
|
||||
// For add mode: provide empty employee object and set isEditMode to false
|
||||
data["employee"] = map[string]interface{}{
|
||||
"id": "",
|
||||
"name": "",
|
||||
"email": "",
|
||||
"department": "",
|
||||
"position": "",
|
||||
"salary": "",
|
||||
"hire_date": "",
|
||||
"status": "active",
|
||||
}
|
||||
data["isEditMode"] = false
|
||||
} else {
|
||||
// For edit mode: ensure employee has all required fields and set isEditMode to true
|
||||
if empMap, ok := emp.(map[string]interface{}); ok {
|
||||
// Fill in any missing fields with empty values
|
||||
fields := []string{"id", "name", "email", "department", "position", "salary", "hire_date", "status"}
|
||||
for _, field := range fields {
|
||||
if _, exists := empMap[field]; !exists {
|
||||
empMap[field] = ""
|
||||
}
|
||||
}
|
||||
data["employee"] = empMap
|
||||
data["isEditMode"] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add request data
|
||||
data["request"] = map[string]interface{}{
|
||||
"method": ctx.Request.Method(),
|
||||
@@ -452,7 +596,17 @@ func (e *JSONEngine) handleTemplate(ctx *ExecutionContext, routeConfig RouteConf
|
||||
// Execute template
|
||||
tmpl := template.Compiled.(*htmlTemplate.Template)
|
||||
var buf strings.Builder
|
||||
|
||||
log.Printf("DEBUG: About to execute template %s with data keys: %v", templateID, func() []string {
|
||||
keys := make([]string, 0, len(data))
|
||||
for k := range data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}())
|
||||
|
||||
if err := tmpl.Execute(&buf, data); err != nil {
|
||||
log.Printf("DEBUG: Template execution error: %v", err)
|
||||
return ctx.Request.Status(500).JSON(fiber.Map{"error": "Template execution failed: " + err.Error()})
|
||||
}
|
||||
|
||||
@@ -470,6 +624,16 @@ func (e *JSONEngine) handleTemplate(ctx *ExecutionContext, routeConfig RouteConf
|
||||
return ctx.Request.Send([]byte(buf.String()))
|
||||
}
|
||||
|
||||
// renderTemplate renders a template with data
|
||||
func (e *JSONEngine) renderTemplate(template *Template, data map[string]interface{}) (string, error) {
|
||||
tmpl := template.Compiled.(*htmlTemplate.Template)
|
||||
var buf strings.Builder
|
||||
if err := tmpl.Execute(&buf, data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handleWorkflow(ctx *ExecutionContext, routeConfig RouteConfig) error {
|
||||
workflowID := routeConfig.Handler.Target
|
||||
log.Printf("Looking for workflow: %s", workflowID)
|
||||
@@ -521,24 +685,92 @@ func (e *JSONEngine) handleWorkflow(ctx *ExecutionContext, routeConfig RouteConf
|
||||
func (e *JSONEngine) handleFunction(ctx *ExecutionContext, routeConfig RouteConfig) error {
|
||||
functionID := routeConfig.Handler.Target
|
||||
|
||||
// Handle special built-in functions
|
||||
switch functionID {
|
||||
case "authenticate_user":
|
||||
return e.handleAuthFunction(ctx)
|
||||
default:
|
||||
// Look up the function in our compiled functions
|
||||
function, exists := e.functions[functionID]
|
||||
if !exists {
|
||||
return ctx.Request.Status(404).JSON(fiber.Map{"error": "Function not found: " + functionID})
|
||||
}
|
||||
|
||||
// Prepare input data
|
||||
input := make(map[string]interface{})
|
||||
|
||||
// Add handler input if specified
|
||||
if routeConfig.Handler.Input != nil {
|
||||
for k, v := range routeConfig.Handler.Input {
|
||||
input[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Add request body for POST/PUT requests
|
||||
if ctx.Request.Method() == "POST" || ctx.Request.Method() == "PUT" {
|
||||
var body map[string]interface{}
|
||||
if err := ctx.Request.BodyParser(&body); err == nil {
|
||||
for k, v := range body {
|
||||
input[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add query parameters
|
||||
for k, v := range ctx.Request.Queries() {
|
||||
input[k] = v
|
||||
}
|
||||
|
||||
// Add route parameters (if any) - specifically handle common parameter names
|
||||
if id := ctx.Request.Params("id"); id != "" {
|
||||
input["id"] = id
|
||||
}
|
||||
if userId := ctx.Request.Params("userId"); userId != "" {
|
||||
input["userId"] = userId
|
||||
}
|
||||
if employeeId := ctx.Request.Params("employeeId"); employeeId != "" {
|
||||
input["employeeId"] = employeeId
|
||||
}
|
||||
// Add any other route parameters that might exist
|
||||
// Note: Fiber v2 doesn't have a direct AllParams() method, so we handle common cases
|
||||
|
||||
// Execute function
|
||||
result, err := e.executeFunction(ctx, function, routeConfig.Handler.Input)
|
||||
result, err := e.executeFunction(ctx, function, input)
|
||||
if err != nil {
|
||||
return ctx.Request.Status(500).JSON(fiber.Map{"error": "Function execution failed: " + err.Error()})
|
||||
}
|
||||
|
||||
return ctx.Request.JSON(result)
|
||||
// Check if we should render a template with the result
|
||||
if routeConfig.Handler.Template != "" {
|
||||
templateID := routeConfig.Handler.Template
|
||||
template, exists := e.templates[templateID]
|
||||
if !exists {
|
||||
return ctx.Request.Status(404).JSON(fiber.Map{"error": "Template not found: " + templateID})
|
||||
}
|
||||
|
||||
// Merge function result with context data
|
||||
templateData := make(map[string]interface{})
|
||||
|
||||
// Add global data first
|
||||
for k, v := range ctx.Data {
|
||||
templateData[k] = v
|
||||
}
|
||||
|
||||
// Add all function result data directly (this includes the employee field)
|
||||
for k, v := range result {
|
||||
templateData[k] = v
|
||||
}
|
||||
|
||||
// For employee_form template, ensure employee field exists (even if nil)
|
||||
if templateID == "employee_form" && templateData["employee"] == nil {
|
||||
templateData["employee"] = nil
|
||||
}
|
||||
|
||||
// Render template
|
||||
rendered, err := e.renderTemplate(template, templateData)
|
||||
if err != nil {
|
||||
return ctx.Request.Status(500).JSON(fiber.Map{"error": "Template rendering failed: " + err.Error()})
|
||||
}
|
||||
|
||||
return ctx.Request.Type("html").Send([]byte(rendered))
|
||||
}
|
||||
|
||||
return ctx.Request.JSON(result)
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handleStatic(ctx *ExecutionContext, routeConfig RouteConfig) error {
|
||||
@@ -557,13 +789,21 @@ func (e *JSONEngine) handleAuthFunction(ctx *ExecutionContext) error {
|
||||
return ctx.Request.Status(400).JSON(fiber.Map{"error": "Invalid request body"})
|
||||
}
|
||||
|
||||
// Simple authentication using demo users from data
|
||||
demoUsers, ok := e.data["demo_users"].([]interface{})
|
||||
if !ok {
|
||||
return ctx.Request.Status(500).JSON(fiber.Map{"error": "User data not configured"})
|
||||
// Generic authentication using user data from configuration
|
||||
// Look for users in multiple possible data keys for flexibility
|
||||
var users []interface{}
|
||||
|
||||
if demoUsers, ok := e.data["demo_users"].([]interface{}); ok {
|
||||
users = demoUsers
|
||||
} else if configUsers, ok := e.data["users"].([]interface{}); ok {
|
||||
users = configUsers
|
||||
} else if authUsers, ok := e.data["auth_users"].([]interface{}); ok {
|
||||
users = authUsers
|
||||
} else {
|
||||
return ctx.Request.Status(500).JSON(fiber.Map{"error": "User authentication data not configured"})
|
||||
}
|
||||
|
||||
for _, userInterface := range demoUsers {
|
||||
for _, userInterface := range users {
|
||||
user, ok := userInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
@@ -630,7 +870,7 @@ func (e *JSONEngine) checkAuthentication(ctx *ExecutionContext, auth *AuthConfig
|
||||
// Function executors
|
||||
func (e *JSONEngine) createHTTPFunction(config FunctionConfig) interface{} {
|
||||
return func(ctx *ExecutionContext, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
client := &http.Client{Timeout: getDefaultDuration(e.workflowEngineConfig.ExecutionTimeout, 30*time.Second)}
|
||||
|
||||
method := config.Method
|
||||
if method == "" {
|
||||
@@ -642,9 +882,13 @@ func (e *JSONEngine) createHTTPFunction(config FunctionConfig) interface{} {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add headers
|
||||
// Add headers with type assertion
|
||||
for k, v := range config.Headers {
|
||||
req.Header.Set(k, v)
|
||||
if vStr, ok := v.(string); ok {
|
||||
req.Header.Set(k, vStr)
|
||||
} else {
|
||||
req.Header.Set(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
@@ -673,8 +917,52 @@ func (e *JSONEngine) createHTTPFunction(config FunctionConfig) interface{} {
|
||||
|
||||
func (e *JSONEngine) createExpressionFunction(config FunctionConfig) interface{} {
|
||||
return func(ctx *ExecutionContext, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
// If there's a response configuration, use it directly
|
||||
if config.Response != nil {
|
||||
result := make(map[string]interface{})
|
||||
|
||||
// Process response template with data substitution
|
||||
for key, value := range config.Response {
|
||||
if valueStr, ok := value.(string); ok {
|
||||
// Handle template substitution like {{.employees}}, {{.blog_posts}}, etc.
|
||||
if strings.Contains(valueStr, "{{.") {
|
||||
// Extract the data key from the template using regex
|
||||
re := regexp.MustCompile(`\{\{\.(\w+)\}\}`)
|
||||
matches := re.FindAllStringSubmatch(valueStr, -1)
|
||||
|
||||
resultValue := valueStr
|
||||
for _, match := range matches {
|
||||
if len(match) > 1 {
|
||||
dataKey := match[1]
|
||||
if dataValue, exists := e.data[dataKey]; exists {
|
||||
// Replace the template with actual data
|
||||
resultValue = strings.ReplaceAll(resultValue, "{{"+dataKey+"}}", "")
|
||||
result[key] = dataValue
|
||||
} else {
|
||||
result[key] = valueStr // Keep original if data not found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if resultValue != valueStr {
|
||||
// Template was processed
|
||||
continue
|
||||
}
|
||||
}
|
||||
result[key] = value
|
||||
} else {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Enhanced expression evaluation with JSON parsing and variable substitution
|
||||
expression := config.Code
|
||||
if expression == "" {
|
||||
expression = config.Expression
|
||||
}
|
||||
|
||||
// Only replace variables that are formatted as placeholders {{variable}} to avoid corrupting JSON
|
||||
for key, value := range input {
|
||||
@@ -739,6 +1027,121 @@ func (e *JSONEngine) createJSFunction(config FunctionConfig) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Additional generic route handlers for any application type
|
||||
func (e *JSONEngine) handleJSON(ctx *ExecutionContext, routeConfig RouteConfig) error {
|
||||
// Handle pure JSON responses
|
||||
response := make(map[string]interface{})
|
||||
|
||||
// Add handler output if specified
|
||||
if routeConfig.Handler.Output != nil {
|
||||
for k, v := range routeConfig.Handler.Output {
|
||||
response[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Add any data from the handler input
|
||||
if routeConfig.Handler.Input != nil {
|
||||
for k, v := range routeConfig.Handler.Input {
|
||||
response[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Add request data if available
|
||||
if ctx.Request.Method() == "POST" || ctx.Request.Method() == "PUT" {
|
||||
var body map[string]interface{}
|
||||
if err := ctx.Request.BodyParser(&body); err == nil {
|
||||
response["request_data"] = body
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.Request.JSON(response)
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handleAPI(ctx *ExecutionContext, routeConfig RouteConfig) error {
|
||||
// Generic API handler - can execute functions, workflows, or return configured data
|
||||
target := routeConfig.Handler.Target
|
||||
|
||||
// Check if target is a function
|
||||
if function, exists := e.functions[target]; exists {
|
||||
result, err := e.executeFunction(ctx, function, routeConfig.Handler.Input)
|
||||
if err != nil {
|
||||
return ctx.Request.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return ctx.Request.JSON(result)
|
||||
}
|
||||
|
||||
// Check if target is a workflow
|
||||
if workflow, exists := e.workflows[target]; exists {
|
||||
result, err := e.executeWorkflow(ctx, workflow, routeConfig.Handler.Input)
|
||||
if err != nil {
|
||||
return ctx.Request.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return ctx.Request.JSON(result)
|
||||
}
|
||||
|
||||
// Return configured output or input as fallback
|
||||
response := make(map[string]interface{})
|
||||
if routeConfig.Handler.Output != nil {
|
||||
response = routeConfig.Handler.Output
|
||||
} else if routeConfig.Handler.Input != nil {
|
||||
response = routeConfig.Handler.Input
|
||||
} else {
|
||||
response["message"] = "API endpoint: " + target
|
||||
response["method"] = ctx.Request.Method()
|
||||
response["path"] = ctx.Request.Path()
|
||||
}
|
||||
|
||||
return ctx.Request.JSON(response)
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handleGeneric(ctx *ExecutionContext, routeConfig RouteConfig) error {
|
||||
// Generic handler for unknown types - maximum flexibility
|
||||
log.Printf("Using generic handler for type: %s", routeConfig.Handler.Type)
|
||||
|
||||
response := map[string]interface{}{
|
||||
"handler_type": routeConfig.Handler.Type,
|
||||
"target": routeConfig.Handler.Target,
|
||||
"method": ctx.Request.Method(),
|
||||
"path": ctx.Request.Path(),
|
||||
"timestamp": time.Now().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
// Add handler output if available
|
||||
if routeConfig.Handler.Output != nil {
|
||||
response["output"] = routeConfig.Handler.Output
|
||||
}
|
||||
|
||||
// Add handler input if available
|
||||
if routeConfig.Handler.Input != nil {
|
||||
response["input"] = routeConfig.Handler.Input
|
||||
}
|
||||
|
||||
// Add request body for POST/PUT requests
|
||||
if ctx.Request.Method() == "POST" || ctx.Request.Method() == "PUT" {
|
||||
var body map[string]interface{}
|
||||
if err := ctx.Request.BodyParser(&body); err == nil {
|
||||
response["request_body"] = body
|
||||
}
|
||||
}
|
||||
|
||||
// Add query parameters
|
||||
if len(ctx.Request.Queries()) > 0 {
|
||||
response["query_params"] = ctx.Request.Queries()
|
||||
}
|
||||
|
||||
// Check if there's a function with this handler type as ID
|
||||
if function, exists := e.functions[routeConfig.Handler.Type]; exists {
|
||||
result, err := e.executeFunction(ctx, function, response)
|
||||
if err != nil {
|
||||
response["function_error"] = err.Error()
|
||||
} else {
|
||||
response["function_result"] = result
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.Request.JSON(response)
|
||||
}
|
||||
|
||||
// Middleware creators
|
||||
func (e *JSONEngine) createAuthMiddleware(config MiddlewareConfig) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
@@ -998,3 +1401,325 @@ func (e *JSONEngine) executeFunction(ctx *ExecutionContext, function *Function,
|
||||
return nil, fmt.Errorf("unknown function handler type")
|
||||
}
|
||||
}
|
||||
|
||||
// createBuiltinFunction creates handlers for built-in functions
|
||||
func (e *JSONEngine) createBuiltinFunction(config FunctionConfig) interface{} {
|
||||
return func(ctx *ExecutionContext, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
switch config.Handler {
|
||||
case "authenticate":
|
||||
// Handle user authentication
|
||||
username, _ := input["username"].(string)
|
||||
password, _ := input["password"].(string)
|
||||
|
||||
if username == "" || password == "" {
|
||||
return map[string]interface{}{
|
||||
"success": false,
|
||||
"error": "Username and password required",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Generic authentication using user data from configuration
|
||||
// Look for users in multiple possible data keys for flexibility
|
||||
var users []interface{}
|
||||
|
||||
if demoUsers, ok := e.data["demo_users"].([]interface{}); ok {
|
||||
users = demoUsers
|
||||
} else if configUsers, ok := e.data["users"].([]interface{}); ok {
|
||||
users = configUsers
|
||||
} else if authUsers, ok := e.data["auth_users"].([]interface{}); ok {
|
||||
users = authUsers
|
||||
} else {
|
||||
return map[string]interface{}{
|
||||
"success": false,
|
||||
"error": "User authentication data not configured",
|
||||
}, nil
|
||||
}
|
||||
|
||||
for _, userInterface := range users {
|
||||
user, ok := userInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
userUsername, _ := user["username"].(string)
|
||||
userPassword, _ := user["password"].(string)
|
||||
role, _ := user["role"].(string)
|
||||
|
||||
if userUsername == username && userPassword == password {
|
||||
// Generate simple token (in production, use JWT)
|
||||
token := fmt.Sprintf("token_%s_%d", username, time.Now().Unix())
|
||||
|
||||
return map[string]interface{}{
|
||||
"success": true,
|
||||
"token": token,
|
||||
"user": map[string]interface{}{
|
||||
"username": username,
|
||||
"role": role,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"success": false,
|
||||
"error": "Invalid credentials",
|
||||
}, nil
|
||||
|
||||
case "echo":
|
||||
return input, nil
|
||||
case "log":
|
||||
log.Printf("Builtin log: %+v", input)
|
||||
return map[string]interface{}{"logged": true}, nil
|
||||
case "validate":
|
||||
// Simple validation example
|
||||
return map[string]interface{}{"valid": true}, nil
|
||||
case "transform":
|
||||
// Simple data transformation
|
||||
if data, exists := input["data"]; exists {
|
||||
return map[string]interface{}{"transformed": data}, nil
|
||||
}
|
||||
return input, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown builtin function: %s", config.Handler)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createCustomFunction creates handlers for custom user-defined functions
|
||||
func (e *JSONEngine) createCustomFunction(config FunctionConfig) interface{} {
|
||||
return func(ctx *ExecutionContext, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
// Execute custom code from config.Code
|
||||
if config.Code != "" {
|
||||
// For now, just return the configured response or echo input
|
||||
if config.Response != nil {
|
||||
return config.Response, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Handle CRUD operations based on config
|
||||
if config.Config != nil {
|
||||
action, _ := config.Config["action"].(string)
|
||||
entity, _ := config.Config["entity"].(string)
|
||||
|
||||
switch action {
|
||||
case "create":
|
||||
return e.handleCreateEntity(ctx, entity, input)
|
||||
case "update":
|
||||
return e.handleUpdateEntity(ctx, entity, input)
|
||||
case "delete":
|
||||
return e.handleDeleteEntity(ctx, entity, input)
|
||||
case "get":
|
||||
return e.handleGetEntity(ctx, entity, input)
|
||||
case "list":
|
||||
return e.handleListEntity(ctx, entity, input)
|
||||
case "send_campaign":
|
||||
return e.handleSendCampaign(ctx, input)
|
||||
case "publish":
|
||||
return e.handlePublishEntity(ctx, entity, input)
|
||||
}
|
||||
}
|
||||
|
||||
// Simple key-value transformation based on config
|
||||
result := make(map[string]interface{})
|
||||
for k, v := range input {
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
// Apply any transformations from config
|
||||
if config.Config != nil {
|
||||
for key, value := range config.Config {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CRUD operation handlers
|
||||
func (e *JSONEngine) handleCreateEntity(ctx *ExecutionContext, entity string, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
switch entity {
|
||||
case "employee":
|
||||
// Create new employee
|
||||
return map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "Employee created successfully",
|
||||
"id": time.Now().Unix(), // Simple ID generation
|
||||
"data": input,
|
||||
}, nil
|
||||
case "post":
|
||||
// Create new blog post
|
||||
return map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "Blog post created successfully",
|
||||
"id": time.Now().Unix(),
|
||||
"data": input,
|
||||
}, nil
|
||||
case "email":
|
||||
// Create email campaign
|
||||
return map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "Email campaign created successfully",
|
||||
"id": time.Now().Unix(),
|
||||
"data": input,
|
||||
}, nil
|
||||
default:
|
||||
return map[string]interface{}{
|
||||
"success": true,
|
||||
"message": fmt.Sprintf("%s created successfully", entity),
|
||||
"id": time.Now().Unix(),
|
||||
"data": input,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handleUpdateEntity(ctx *ExecutionContext, entity string, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
id, _ := input["id"].(string)
|
||||
return map[string]interface{}{
|
||||
"success": true,
|
||||
"message": fmt.Sprintf("%s updated successfully", entity),
|
||||
"id": id,
|
||||
"data": input,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handleDeleteEntity(ctx *ExecutionContext, entity string, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
id, _ := input["id"].(string)
|
||||
return map[string]interface{}{
|
||||
"success": true,
|
||||
"message": fmt.Sprintf("%s deleted successfully", entity),
|
||||
"id": id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handleGetEntity(ctx *ExecutionContext, entity string, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
// Get entity ID from input
|
||||
var entityID string
|
||||
if idVal, ok := input["id"]; ok {
|
||||
entityID = fmt.Sprintf("%v", idVal)
|
||||
}
|
||||
|
||||
if entityID == "" {
|
||||
return map[string]interface{}{
|
||||
"success": false,
|
||||
"error": entity + " ID is required",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Look up entity data from configuration
|
||||
entityDataKey := entity + "s" // Assume plural form (employees, posts, etc.)
|
||||
if entityData, ok := e.data[entityDataKey]; ok {
|
||||
if entityList, ok := entityData.([]interface{}); ok {
|
||||
for _, item := range entityList {
|
||||
if itemMap, ok := item.(map[string]interface{}); ok {
|
||||
if itemIDVal, ok := itemMap["id"]; ok {
|
||||
itemIDStr := fmt.Sprintf("%v", itemIDVal)
|
||||
if itemIDStr == entityID {
|
||||
// Found the entity, return it with all required data
|
||||
result := make(map[string]interface{})
|
||||
|
||||
// Add the entity with singular name
|
||||
result[entity] = itemMap
|
||||
|
||||
// Add other required data for the template
|
||||
if appTitle, ok := e.data["app_title"]; ok {
|
||||
result["app_title"] = appTitle
|
||||
}
|
||||
|
||||
// Add any other data that might be needed by templates
|
||||
for key, value := range e.data {
|
||||
if key != entityDataKey { // Don't duplicate the main entity data
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"success": false,
|
||||
"error": entity + " not found",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handleListEntity(ctx *ExecutionContext, entity string, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
// Look up entity data from configuration using plural form
|
||||
entityDataKey := entity + "s" // Assume plural form (employees, posts, etc.)
|
||||
if entityData, ok := e.data[entityDataKey]; ok {
|
||||
result := map[string]interface{}{
|
||||
"success": true,
|
||||
}
|
||||
result[entityDataKey] = entityData
|
||||
|
||||
// Add other data that might be needed
|
||||
for key, value := range e.data {
|
||||
if key != entityDataKey {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// If no data found, return empty result
|
||||
return map[string]interface{}{
|
||||
"success": true,
|
||||
entity + "s": []interface{}{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handleSendCampaign(ctx *ExecutionContext, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
return map[string]interface{}{
|
||||
"success": true,
|
||||
"campaign_id": fmt.Sprintf("campaign_%d", time.Now().Unix()),
|
||||
"emails_sent": 10, // Mock value
|
||||
"status": "sent",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *JSONEngine) handlePublishEntity(ctx *ExecutionContext, entity string, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
id, _ := input["id"].(string)
|
||||
return map[string]interface{}{
|
||||
"success": true,
|
||||
"message": fmt.Sprintf("%s published successfully", entity),
|
||||
"id": id,
|
||||
"status": "published",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// createGenericFunction creates a generic function handler for unknown types
|
||||
func (e *JSONEngine) createGenericFunction(config FunctionConfig) interface{} {
|
||||
return func(ctx *ExecutionContext, input map[string]interface{}) (map[string]interface{}, error) {
|
||||
log.Printf("Executing generic function: %s with type: %s", config.ID, config.Type)
|
||||
|
||||
// For unknown function types, we create a flexible handler that:
|
||||
// 1. Returns configured response if available
|
||||
if config.Response != nil {
|
||||
return config.Response, nil
|
||||
}
|
||||
|
||||
// 2. Applies any transformations from config
|
||||
result := make(map[string]interface{})
|
||||
for k, v := range input {
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
if config.Config != nil {
|
||||
for key, value := range config.Config {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Add metadata about the function execution
|
||||
result["_function_id"] = config.ID
|
||||
result["_function_type"] = config.Type
|
||||
result["_executed_at"] = time.Now().Format(time.RFC3339)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func loadConfiguration(configPath string) (*AppConfiguration, error) {
|
||||
data, err := ioutil.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read config file: %v", err)
|
||||
}
|
||||
|
||||
var config AppConfiguration
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse JSON config: %v", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Parse command line flags
|
||||
configPath := flag.String("config", "sms-app.json", "Path to JSON configuration file")
|
||||
@@ -16,14 +33,15 @@ func main() {
|
||||
*configPath = os.Args[1]
|
||||
}
|
||||
|
||||
// Create JSON engine
|
||||
engine := NewJSONEngine()
|
||||
|
||||
// Load configuration
|
||||
if err := engine.LoadConfiguration(*configPath); err != nil {
|
||||
// Load configuration first
|
||||
config, err := loadConfiguration(*configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load configuration: %v", err)
|
||||
}
|
||||
|
||||
// Create JSON engine with configuration
|
||||
engine := NewJSONEngine(config)
|
||||
|
||||
// Compile configuration
|
||||
if err := engine.Compile(); err != nil {
|
||||
log.Fatalf("Failed to compile configuration: %v", err)
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
// AppConfiguration represents the complete JSON configuration for an application
|
||||
type AppConfiguration struct {
|
||||
App AppMetadata `json:"app"`
|
||||
WorkflowEngine *WorkflowEngineConfig `json:"workflow_engine,omitempty"`
|
||||
Routes []RouteConfig `json:"routes"`
|
||||
Middleware []MiddlewareConfig `json:"middleware"`
|
||||
Templates map[string]TemplateConfig `json:"templates"`
|
||||
@@ -26,6 +27,30 @@ type AppMetadata struct {
|
||||
Host string `json:"host"`
|
||||
}
|
||||
|
||||
// WorkflowEngineConfig contains workflow engine configuration
|
||||
type WorkflowEngineConfig struct {
|
||||
MaxWorkers int `json:"max_workers,omitempty"`
|
||||
ExecutionTimeout string `json:"execution_timeout,omitempty"`
|
||||
EnableMetrics bool `json:"enable_metrics,omitempty"`
|
||||
EnableAudit bool `json:"enable_audit,omitempty"`
|
||||
EnableTracing bool `json:"enable_tracing,omitempty"`
|
||||
LogLevel string `json:"log_level,omitempty"`
|
||||
Storage StorageConfig `json:"storage,omitempty"`
|
||||
Security SecurityConfig `json:"security,omitempty"`
|
||||
}
|
||||
|
||||
// StorageConfig contains storage configuration
|
||||
type StorageConfig struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
MaxConnections int `json:"max_connections,omitempty"`
|
||||
}
|
||||
|
||||
// SecurityConfig contains security configuration
|
||||
type SecurityConfig struct {
|
||||
EnableAuth bool `json:"enable_auth,omitempty"`
|
||||
AllowedOrigins []string `json:"allowed_origins,omitempty"`
|
||||
}
|
||||
|
||||
// RouteConfig defines HTTP routes
|
||||
type RouteConfig struct {
|
||||
Path string `json:"path"`
|
||||
@@ -48,6 +73,7 @@ type ResponseConfig struct {
|
||||
type HandlerConfig struct {
|
||||
Type string `json:"type"` // "workflow", "template", "function", "redirect"
|
||||
Target string `json:"target"`
|
||||
Template string `json:"template,omitempty"`
|
||||
Input map[string]interface{} `json:"input,omitempty"`
|
||||
Output map[string]interface{} `json:"output,omitempty"`
|
||||
ErrorHandling *ErrorHandlingConfig `json:"error_handling,omitempty"`
|
||||
@@ -177,47 +203,45 @@ type JSONSchemaConfig struct {
|
||||
Output map[string]interface{} `json:"output,omitempty"`
|
||||
}
|
||||
|
||||
// FunctionConfig defines custom functions
|
||||
// FunctionConfig defines custom functions with complete flexibility
|
||||
type FunctionConfig struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Type string `json:"type"` // "builtin", "custom", "external", "http"
|
||||
Type string `json:"type"` // "http", "expression", "template", "js", "builtin"
|
||||
Handler string `json:"handler,omitempty"`
|
||||
Method string `json:"method,omitempty"` // For HTTP functions
|
||||
URL string `json:"url,omitempty"` // For HTTP functions
|
||||
Headers map[string]string `json:"headers,omitempty"` // For HTTP functions
|
||||
Headers map[string]interface{} `json:"headers,omitempty"` // For HTTP functions
|
||||
Body string `json:"body,omitempty"` // For HTTP request body template
|
||||
Code string `json:"code,omitempty"` // For custom code functions
|
||||
Parameters []ParameterConfig `json:"parameters,omitempty"`
|
||||
Returns []ParameterConfig `json:"returns,omitempty"`
|
||||
Template string `json:"template,omitempty"` // For template functions
|
||||
Expression string `json:"expression,omitempty"` // For expression functions
|
||||
Parameters map[string]interface{} `json:"parameters,omitempty"` // Generic parameters
|
||||
Returns map[string]interface{} `json:"returns,omitempty"` // Generic return definition
|
||||
Response map[string]interface{} `json:"response,omitempty"` // Response structure
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
Async bool `json:"async"`
|
||||
Timeout string `json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
// ParameterConfig defines function parameters
|
||||
type ParameterConfig struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Required bool `json:"required"`
|
||||
Default interface{} `json:"default,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Validation []string `json:"validation,omitempty"`
|
||||
}
|
||||
// Note: ParameterConfig removed - using generic map[string]interface{} for parameters
|
||||
|
||||
// ValidatorConfig defines validation rules
|
||||
// ValidatorConfig defines validation rules with complete flexibility
|
||||
type ValidatorConfig struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"` // "jsonschema", "custom", "regex"
|
||||
Type string `json:"type"` // "jsonschema", "custom", "regex", "builtin"
|
||||
Field string `json:"field,omitempty"`
|
||||
Schema interface{} `json:"schema,omitempty"`
|
||||
Rules []ValidationRule `json:"rules,omitempty"`
|
||||
Rules map[string]interface{} `json:"rules,omitempty"` // Generic rules
|
||||
Messages map[string]string `json:"messages,omitempty"`
|
||||
Expression string `json:"expression,omitempty"` // For expression-based validation
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
StrictMode bool `json:"strict_mode"`
|
||||
AllowEmpty bool `json:"allow_empty"`
|
||||
}
|
||||
|
||||
// ValidationRule defines individual validation rules
|
||||
// ValidationRule defines individual validation rules with flexibility
|
||||
type ValidationRule struct {
|
||||
Field string `json:"field"`
|
||||
Type string `json:"type"`
|
||||
@@ -225,46 +249,18 @@ type ValidationRule struct {
|
||||
Min interface{} `json:"min,omitempty"`
|
||||
Max interface{} `json:"max,omitempty"`
|
||||
Pattern string `json:"pattern,omitempty"`
|
||||
Expression string `json:"expression,omitempty"` // For custom expressions
|
||||
CustomRule string `json:"custom_rule,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Config map[string]interface{} `json:"config,omitempty"`
|
||||
Conditions []ConditionConfig `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// Provider configuration for SMS/communication services
|
||||
type ProviderConfig struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"` // "sms", "email", "push"
|
||||
Enabled bool `json:"enabled"`
|
||||
Priority int `json:"priority"`
|
||||
Config map[string]interface{} `json:"config"`
|
||||
Countries []string `json:"countries,omitempty"`
|
||||
RateLimit *RateLimitConfig `json:"rate_limit,omitempty"`
|
||||
Costs map[string]float64 `json:"costs,omitempty"`
|
||||
Features []string `json:"features,omitempty"`
|
||||
Reliability float64 `json:"reliability"`
|
||||
}
|
||||
|
||||
// RateLimitConfig defines rate limiting
|
||||
type RateLimitConfig struct {
|
||||
RequestsPerSecond int `json:"requests_per_second"`
|
||||
BurstSize int `json:"burst_size"`
|
||||
WindowSize int `json:"window_size"`
|
||||
}
|
||||
|
||||
// Country configuration for routing
|
||||
type CountryConfig struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Providers []string `json:"providers"`
|
||||
DefaultRate float64 `json:"default_rate"`
|
||||
Regulations map[string]string `json:"regulations,omitempty"`
|
||||
}
|
||||
|
||||
// Runtime types for the JSON engine
|
||||
// Generic runtime types for the JSON engine
|
||||
type JSONEngine struct {
|
||||
app *fiber.App
|
||||
workflowEngine *workflow.WorkflowEngine
|
||||
workflowEngineConfig *WorkflowEngineConfig
|
||||
config *AppConfiguration
|
||||
templates map[string]*Template
|
||||
workflows map[string]*Workflow
|
||||
@@ -272,6 +268,7 @@ type JSONEngine struct {
|
||||
validators map[string]*Validator
|
||||
middleware map[string]*Middleware
|
||||
data map[string]interface{}
|
||||
genericData map[string]interface{} // For any custom data structures
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
@@ -303,16 +300,20 @@ type Edge struct {
|
||||
To *Node
|
||||
}
|
||||
|
||||
// Function represents a compiled function with generic handler
|
||||
type Function struct {
|
||||
ID string
|
||||
Config FunctionConfig
|
||||
Handler interface{}
|
||||
Handler interface{} // Can be any type of handler
|
||||
Runtime map[string]interface{} // Runtime state/context
|
||||
}
|
||||
|
||||
// Validator represents a compiled validator with generic rules
|
||||
type Validator struct {
|
||||
ID string
|
||||
Config ValidatorConfig
|
||||
Rules []ValidationRule
|
||||
Rules map[string]interface{} // Generic rules instead of typed array
|
||||
Runtime map[string]interface{} // Runtime context
|
||||
}
|
||||
|
||||
type Middleware struct {
|
||||
@@ -328,7 +329,7 @@ type WorkflowRuntime struct {
|
||||
Error error
|
||||
}
|
||||
|
||||
// Execution context for runtime
|
||||
// ExecutionContext for runtime with complete flexibility
|
||||
type ExecutionContext struct {
|
||||
Request *fiber.Ctx
|
||||
Data map[string]interface{}
|
||||
@@ -339,4 +340,7 @@ type ExecutionContext struct {
|
||||
Node *Node
|
||||
Functions map[string]*Function
|
||||
Validators map[string]*Validator
|
||||
Config *AppConfiguration // Access to full config
|
||||
Runtime map[string]interface{} // Runtime state
|
||||
Context map[string]interface{} // Additional context data
|
||||
}
|
||||
|
Binary file not shown.
Reference in New Issue
Block a user