// Package malformedcommand defines the operator-visible record used for // malformed asynchronous generic delivery commands. package malformedcommand import ( "encoding/json" "fmt" "strings" "time" "galaxy/mail/internal/domain/common" ) // FailureCode identifies the stable malformed-command rejection reason. type FailureCode string const ( // FailureCodeInvalidEnvelope reports that the command could not be accepted // because the recipient envelope was invalid. FailureCodeInvalidEnvelope FailureCode = "invalid_envelope" // FailureCodeInvalidPayload reports that the command payload could not be // decoded or validated. FailureCodeInvalidPayload FailureCode = "invalid_payload" // FailureCodeInvalidCommand reports that the top-level stream envelope was // malformed or unsupported. FailureCodeInvalidCommand FailureCode = "invalid_command" // FailureCodeIdempotencyConflict reports that the stream command reused an // existing idempotency scope with a different request fingerprint. FailureCodeIdempotencyConflict FailureCode = "idempotency_conflict" ) // IsKnown reports whether code belongs to the frozen malformed-command // rejection surface. func (code FailureCode) IsKnown() bool { switch code { case FailureCodeInvalidEnvelope, FailureCodeInvalidPayload, FailureCodeInvalidCommand, FailureCodeIdempotencyConflict: return true default: return false } } // Entry stores one operator-visible malformed asynchronous command record. type Entry struct { // StreamEntryID stores the Redis Stream entry identifier of the malformed // command. StreamEntryID string // DeliveryID stores the optional raw delivery identifier extracted from the // stream entry when available. DeliveryID string // Source stores the optional raw source value extracted from the stream // entry when available. Source string // IdempotencyKey stores the optional raw idempotency key extracted from the // stream entry when available. IdempotencyKey string // FailureCode stores the stable malformed-command rejection reason. FailureCode FailureCode // FailureMessage stores the detailed validation or decoding failure. FailureMessage string // RawFields stores the raw top-level stream fields captured for later // operator inspection. RawFields map[string]any // RecordedAt stores when the malformed command was durably recorded. RecordedAt time.Time } // Validate reports whether entry contains a complete malformed-command record. func (entry Entry) Validate() error { if strings.TrimSpace(entry.StreamEntryID) == "" { return fmt.Errorf("malformed command stream entry id must not be empty") } if !entry.FailureCode.IsKnown() { return fmt.Errorf("malformed command failure code %q is unsupported", entry.FailureCode) } if strings.TrimSpace(entry.FailureMessage) == "" { return fmt.Errorf("malformed command failure message must not be empty") } if strings.TrimSpace(entry.FailureMessage) != entry.FailureMessage { return fmt.Errorf("malformed command failure message must not contain surrounding whitespace") } if entry.RawFields == nil { return fmt.Errorf("malformed command raw fields must not be nil") } if err := validateJSONObject("malformed command raw fields", entry.RawFields); err != nil { return err } if err := common.ValidateTimestamp("malformed command recorded at", entry.RecordedAt); err != nil { return err } return nil } func validateJSONObject(name string, value map[string]any) error { if value == nil { return fmt.Errorf("%s must not be nil", name) } payload, err := json.Marshal(value) if err != nil { return fmt.Errorf("%s: %w", name, err) } if string(payload) == "null" { return fmt.Errorf("%s must encode as a JSON object", name) } var decoded map[string]any if err := json.Unmarshal(payload, &decoded); err != nil { return fmt.Errorf("%s: %w", name, err) } if decoded == nil { return fmt.Errorf("%s must encode as a JSON object", name) } return nil }