package main import ( "context" "fmt" "regexp" "strings" "time" "github.com/oarkflow/json" "github.com/oarkflow/mq/dag" "github.com/oarkflow/mq/utils" "github.com/oarkflow/jet" "github.com/oarkflow/mq" "github.com/oarkflow/mq/consts" ) func main() { flow := dag.NewDAG("Email Notification System", "email-notification", func(taskID string, result mq.Result) { fmt.Printf("Email notification workflow completed for task %s: %s\n", taskID, string(utils.RemoveRecursiveFromJSON(result.Payload, "html_content"))) }) // Add workflow nodes // Note: Page nodes have no timeout by default, allowing users unlimited time for form input flow.AddNode(dag.Page, "Contact Form", "ContactForm", &ContactFormNode{}, true) flow.AddNode(dag.Function, "Validate Contact Data", "ValidateContact", &ValidateContactNode{}) flow.AddNode(dag.Function, "Check User Type", "CheckUserType", &CheckUserTypeNode{}) flow.AddNode(dag.Function, "Send Welcome Email", "SendWelcomeEmail", &SendWelcomeEmailNode{}) flow.AddNode(dag.Function, "Send Premium Email", "SendPremiumEmail", &SendPremiumEmailNode{}) flow.AddNode(dag.Function, "Send Standard Email", "SendStandardEmail", &SendStandardEmailNode{}) flow.AddNode(dag.Page, "Success Page", "SuccessPage", &SuccessPageNode{}) flow.AddNode(dag.Page, "Error Page", "ErrorPage", &EmailErrorPageNode{}) // Define conditional flow flow.AddEdge(dag.Simple, "Form to Validation", "ContactForm", "ValidateContact") flow.AddCondition("ValidateContact", map[string]string{ "valid": "CheckUserType", "invalid": "ErrorPage", }) flow.AddCondition("CheckUserType", map[string]string{ "new_user": "SendWelcomeEmail", "premium_user": "SendPremiumEmail", "standard_user": "SendStandardEmail", }) flow.AddCondition("SendWelcomeEmail", map[string]string{ "sent": "SuccessPage", "failed": "ErrorPage", }) flow.AddCondition("SendPremiumEmail", map[string]string{ "sent": "SuccessPage", "failed": "ErrorPage", }) flow.AddCondition("SendStandardEmail", map[string]string{ "sent": "SuccessPage", "failed": "ErrorPage", }) // Start the flow if flow.Error != nil { panic(flow.Error) } fmt.Println("Starting Email Notification DAG server on http://0.0.0.0:8084") fmt.Println("Navigate to the URL to access the contact form") flow.Start(context.Background(), "0.0.0.0:8084") } // ContactFormNode - Contact form with validation type ContactFormNode struct { dag.Operation } func (c *ContactFormNode) ProcessTask(ctx context.Context, task *mq.Task) mq.Result { // Check if this is a form submission var inputData map[string]any if task.Payload != nil && len(task.Payload) > 0 { if err := json.Unmarshal(task.Payload, &inputData); err == nil { // If we have valid input data, pass it through for validation return mq.Result{Payload: task.Payload, Ctx: ctx} } } // Otherwise, show the form htmlTemplate := ` Contact Us - Email Notification System

📧 Contact Us

Advanced Email Notification System with DAG Workflow

🔄 Smart Routing: Our system automatically routes your message based on your user type and preferences.

📱 Instant Notifications
Real-time email delivery
🎯 Smart Targeting
User-specific content
🔒 Secure Processing
Enterprise-grade security
` parser := jet.NewWithMemory(jet.WithDelims("{{", "}}")) rs, err := parser.ParseTemplate(htmlTemplate, map[string]any{ "task_id": ctx.Value("task_id"), }) if err != nil { return mq.Result{Error: err, Ctx: ctx} } ctx = context.WithValue(ctx, consts.ContentType, consts.TypeHtml) data := map[string]any{ "html_content": rs, "step": "form", } bt, _ := json.Marshal(data) return mq.Result{Payload: bt, Ctx: ctx} } // ValidateContactNode - Validates contact form data type ValidateContactNode struct { dag.Operation } func (v *ValidateContactNode) ProcessTask(ctx context.Context, task *mq.Task) mq.Result { var inputData map[string]any if err := json.Unmarshal(task.Payload, &inputData); err != nil { return mq.Result{ Error: fmt.Errorf("invalid input data: %v", err), Ctx: ctx, } } // Extract form data firstName, _ := inputData["first_name"].(string) lastName, _ := inputData["last_name"].(string) email, _ := inputData["email"].(string) userType, _ := inputData["user_type"].(string) priority, _ := inputData["priority"].(string) subject, _ := inputData["subject"].(string) message, _ := inputData["message"].(string) // Validate required fields if firstName == "" { inputData["validation_error"] = "First name is required" inputData["error_field"] = "first_name" bt, _ := json.Marshal(inputData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "invalid"} } if lastName == "" { inputData["validation_error"] = "Last name is required" inputData["error_field"] = "last_name" bt, _ := json.Marshal(inputData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "invalid"} } if email == "" { inputData["validation_error"] = "Email address is required" inputData["error_field"] = "email" bt, _ := json.Marshal(inputData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "invalid"} } // Validate email format emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`) if !emailRegex.MatchString(email) { inputData["validation_error"] = "Please enter a valid email address" inputData["error_field"] = "email" bt, _ := json.Marshal(inputData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "invalid"} } if userType == "" { inputData["validation_error"] = "Please select your user type" inputData["error_field"] = "user_type" bt, _ := json.Marshal(inputData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "invalid"} } if priority == "" { inputData["validation_error"] = "Please select a priority level" inputData["error_field"] = "priority" bt, _ := json.Marshal(inputData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "invalid"} } if subject == "" { inputData["validation_error"] = "Subject is required" inputData["error_field"] = "subject" bt, _ := json.Marshal(inputData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "invalid"} } if message == "" { inputData["validation_error"] = "Message is required" inputData["error_field"] = "message" bt, _ := json.Marshal(inputData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "invalid"} } // Check for spam patterns spamPatterns := []string{"click here", "free money", "act now", "limited time"} messageLower := strings.ToLower(message) subjectLower := strings.ToLower(subject) for _, pattern := range spamPatterns { if strings.Contains(messageLower, pattern) || strings.Contains(subjectLower, pattern) { inputData["validation_error"] = "Message contains prohibited content" inputData["error_field"] = "message" bt, _ := json.Marshal(inputData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "invalid"} } } // All validations passed validatedData := map[string]any{ "first_name": firstName, "last_name": lastName, "full_name": fmt.Sprintf("%s %s", firstName, lastName), "email": email, "user_type": userType, "priority": priority, "subject": subject, "message": message, "validated_at": time.Now().Format("2006-01-02 15:04:05"), "validation_status": "success", "message_length": len(message), } bt, _ := json.Marshal(validatedData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "valid"} } // CheckUserTypeNode - Determines routing based on user type type CheckUserTypeNode struct { dag.Operation } func (c *CheckUserTypeNode) ProcessTask(ctx context.Context, task *mq.Task) mq.Result { var inputData map[string]any if err := json.Unmarshal(task.Payload, &inputData); err != nil { return mq.Result{Error: err, Ctx: ctx} } userType, _ := inputData["user_type"].(string) // Add timestamp and additional metadata inputData["processed_at"] = time.Now().Format("2006-01-02 15:04:05") inputData["routing_decision"] = userType var conditionStatus string switch userType { case "new": conditionStatus = "new_user" inputData["email_template"] = "welcome" case "premium": conditionStatus = "premium_user" inputData["email_template"] = "premium" case "standard": conditionStatus = "standard_user" inputData["email_template"] = "standard" default: conditionStatus = "standard_user" inputData["email_template"] = "standard" } fmt.Printf("🔀 Routing decision: %s -> %s\n", userType, conditionStatus) bt, _ := json.Marshal(inputData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: conditionStatus} } // Email sending nodes type SendWelcomeEmailNode struct { dag.Operation } func (s *SendWelcomeEmailNode) ProcessTask(ctx context.Context, task *mq.Task) mq.Result { return s.sendEmail(ctx, task, "Welcome to our platform! 🎉") } type SendPremiumEmailNode struct { dag.Operation } func (s *SendPremiumEmailNode) ProcessTask(ctx context.Context, task *mq.Task) mq.Result { return s.sendEmail(ctx, task, "Premium Support Response 💎") } type SendStandardEmailNode struct { dag.Operation } func (s *SendStandardEmailNode) ProcessTask(ctx context.Context, task *mq.Task) mq.Result { return s.sendEmail(ctx, task, "Thank you for contacting us ⭐") } // Helper method for email sending func (s *SendWelcomeEmailNode) sendEmail(ctx context.Context, task *mq.Task, emailType string) mq.Result { var inputData map[string]any if err := json.Unmarshal(task.Payload, &inputData); err != nil { return mq.Result{Error: err, Ctx: ctx} } email, _ := inputData["email"].(string) // Simulate email sending delay time.Sleep(300 * time.Millisecond) // Simulate occasional failures for demo purposes timestamp := time.Now() success := timestamp.Second()%15 != 0 // 93% success rate if !success { errorData := inputData errorData["email_status"] = "failed" errorData["error_message"] = "Email gateway temporarily unavailable. Please try again." errorData["sent_at"] = timestamp.Format("2006-01-02 15:04:05") errorData["retry_suggested"] = true bt, _ := json.Marshal(errorData) return mq.Result{ Payload: bt, Ctx: ctx, ConditionStatus: "failed", } } // Generate mock email ID and response emailID := fmt.Sprintf("EMAIL_%d_%s", timestamp.Unix(), email[0:3]) resultData := inputData resultData["email_status"] = "sent" resultData["email_id"] = emailID resultData["email_type"] = emailType resultData["sent_at"] = timestamp.Format("2006-01-02 15:04:05") resultData["delivery_estimate"] = "Instant" resultData["gateway"] = "MockEmail Gateway" fmt.Printf("📧 Email sent successfully! Type: %s, ID: %s, To: %s\n", emailType, emailID, email) bt, _ := json.Marshal(resultData) return mq.Result{Payload: bt, Ctx: ctx, ConditionStatus: "sent"} } // Helper methods for other email nodes func (s *SendPremiumEmailNode) sendEmail(ctx context.Context, task *mq.Task, emailType string) mq.Result { node := &SendWelcomeEmailNode{} return node.sendEmail(ctx, task, emailType) } func (s *SendStandardEmailNode) sendEmail(ctx context.Context, task *mq.Task, emailType string) mq.Result { node := &SendWelcomeEmailNode{} return node.sendEmail(ctx, task, emailType) } // SuccessPageNode - Shows successful email result type SuccessPageNode struct { dag.Operation } func (s *SuccessPageNode) ProcessTask(ctx context.Context, task *mq.Task) mq.Result { var inputData map[string]any if err := json.Unmarshal(task.Payload, &inputData); err != nil { return mq.Result{Error: err, Ctx: ctx} } htmlTemplate := ` Message Sent Successfully

Message Sent Successfully!

{{email_status}}
👤 Recipient
{{full_name}}
📧 Email Address
{{email}}
🆔 Email ID
{{email_id}}
⏰ Sent At
{{sent_at}}
📨 Email Type
{{email_type}}
👥 User Type
{{user_type}}
🚨 Priority
{{priority}}
🚚 Delivery
{{delivery_estimate}}
📋 Subject:
{{subject}}
💬 Message ({{message_length}} chars):
"{{message}}"
📧 Send Another Message 📊 View Metrics
🔄 Workflow Details:
Gateway: {{gateway}} | Template: {{email_template}} | Processed: {{processed_at}}
This message was processed through our advanced DAG workflow system with conditional routing.
` parser := jet.NewWithMemory(jet.WithDelims("{{", "}}")) rs, err := parser.ParseTemplate(htmlTemplate, inputData) if err != nil { return mq.Result{Error: err, Ctx: ctx} } ctx = context.WithValue(ctx, consts.ContentType, consts.TypeHtml) finalData := map[string]any{ "html_content": rs, "result": inputData, "step": "success", } bt, _ := json.Marshal(finalData) return mq.Result{Payload: bt, Ctx: ctx} } // EmailErrorPageNode - Shows validation or sending errors type EmailErrorPageNode struct { dag.Operation } func (e *EmailErrorPageNode) ProcessTask(ctx context.Context, task *mq.Task) mq.Result { var inputData map[string]any if err := json.Unmarshal(task.Payload, &inputData); err != nil { return mq.Result{Error: err, Ctx: ctx} } // Determine error type and message errorMessage, _ := inputData["validation_error"].(string) errorField, _ := inputData["error_field"].(string) emailError, _ := inputData["error_message"].(string) if errorMessage == "" && emailError != "" { errorMessage = emailError errorField = "email_sending" } if errorMessage == "" { errorMessage = "An unknown error occurred" } htmlTemplate := ` Email Error

Email Processing Error

{{error_message}}
{{if error_field}}
🎯 Error Field: {{error_field}}
⚡ Action Required: Please correct the highlighted field and try again.
💡 Tip: Make sure all required fields are properly filled out.
{{end}} {{if retry_suggested}}
⚠️ Temporary Issue: This appears to be a temporary system issue. Please try sending your message again in a few moments.
🔄 Auto-Retry: Our system will automatically retry failed deliveries.
{{end}}
🔄 Try Again 📊 Check Status
🔄 DAG Error Handler | Email Notification Workflow Failed
Our advanced routing system ensures reliable message delivery.
` parser := jet.NewWithMemory(jet.WithDelims("{{", "}}")) templateData := map[string]any{ "error_message": errorMessage, "error_field": errorField, "retry_suggested": inputData["retry_suggested"], } rs, err := parser.ParseTemplate(htmlTemplate, templateData) if err != nil { return mq.Result{Error: err, Ctx: ctx} } ctx = context.WithValue(ctx, consts.ContentType, consts.TypeHtml) finalData := map[string]any{ "html_content": rs, "error_data": inputData, "step": "error", } bt, _ := json.Marshal(finalData) return mq.Result{Payload: bt, Ctx: ctx} }