ultisuite-backend/internal/mail/rules/simulate.go
R3D347HR4Y 1b9a3394e2 Implement rule simulation and webhook enhancements
- Added a new endpoint for simulating rules based on sample messages, allowing users to test rule conditions and actions.
- Enhanced webhook management with versioning, preview capabilities, and improved validation for webhook requests.
- Updated service interfaces to support new functionalities, including max retries for webhooks and signing secrets.
- Implemented observability metrics for webhook retries and dead-letter tracking, improving error handling and monitoring.
- Enhanced unit tests to cover new simulation and webhook features, ensuring robust functionality and validation.
2026-05-22 17:53:51 +02:00

72 lines
2.1 KiB
Go

package rules
import (
"context"
"fmt"
"github.com/ultisuite/ulti-backend/internal/mail/webhooks"
)
type SimulatedActionResult struct {
ActionResult
SimulatedPayload string `json:"simulated_payload,omitempty"`
}
type SimulationResult struct {
Matched bool `json:"matched"`
Actions []SimulatedActionResult `json:"actions,omitempty"`
}
func (e *Engine) SimulateRule(ctx context.Context, conditions []Condition, actions []Action, msg *Message) SimulationResult {
if !matchesAll(conditions, msg) {
return SimulationResult{Matched: false}
}
return SimulationResult{
Matched: true,
Actions: e.simulateActions(ctx, actions, msg),
}
}
func (e *Engine) simulateActions(ctx context.Context, actions []Action, msg *Message) []SimulatedActionResult {
results := make([]SimulatedActionResult, 0, len(actions))
for _, action := range actions {
results = append(results, e.simulateAction(ctx, action, msg))
}
return results
}
func (e *Engine) simulateAction(ctx context.Context, action Action, msg *Message) SimulatedActionResult {
switch action.Type {
case "label", "move", "archive", "delete", "mark_read":
return SimulatedActionResult{
ActionResult: ActionResult{Type: action.Type, Value: action.Value, OK: true},
}
case "webhook":
if e.db == nil {
return SimulatedActionResult{
ActionResult: actionResultFrom(action, fmt.Errorf("webhook simulation unavailable")),
}
}
var bodyTemplate string
err := e.db.QueryRow(ctx, `
SELECT body_template
FROM webhook_templates
WHERE id = $1 AND is_active = true
`, action.Value).Scan(&bodyTemplate)
if err != nil {
return SimulatedActionResult{
ActionResult: actionResultFrom(action, fmt.Errorf("query template: %w", err)),
}
}
payload := webhooks.RenderBodyTemplate(bodyTemplate, messageToWebhookContext(msg))
return SimulatedActionResult{
ActionResult: ActionResult{Type: action.Type, Value: action.Value, OK: true},
SimulatedPayload: payload,
}
default:
return SimulatedActionResult{
ActionResult: actionResultFrom(action, fmt.Errorf("unknown action type: %s", action.Type)),
}
}
}