// Package malformedintent defines the operator-visible record used for // malformed notification intents. package malformedintent import ( "encoding/json" "fmt" "strings" "time" ) // FailureCode identifies one stable malformed-intent rejection reason. type FailureCode string const ( // FailureCodeInvalidIntent reports malformed top-level intent fields or an // invalid normalized envelope. FailureCodeInvalidIntent FailureCode = "invalid_intent" // FailureCodeInvalidPayload reports malformed or schema-invalid // `payload_json`. FailureCodeInvalidPayload FailureCode = "invalid_payload" // FailureCodeIdempotencyConflict reports a duplicate idempotency scope that // conflicts with already accepted normalized content. FailureCodeIdempotencyConflict FailureCode = "idempotency_conflict" // FailureCodeRecipientNotFound reports that a user-targeted recipient user // id could not be resolved through User Service. FailureCodeRecipientNotFound FailureCode = "recipient_not_found" ) // Entry stores one operator-visible malformed notification-intent record. type Entry struct { // StreamEntryID stores the Redis Stream entry identifier of the rejected // intent. StreamEntryID string // NotificationType stores the optional raw notification type extracted from // the rejected entry. NotificationType string // Producer stores the optional raw producer value extracted from the // rejected entry. Producer string // IdempotencyKey stores the optional raw idempotency key extracted from the // rejected entry. IdempotencyKey string // FailureCode stores the stable rejection classification. FailureCode FailureCode // FailureMessage stores the detailed validation or decode failure. FailureMessage string // RawFields stores the raw top-level stream fields captured for operator // inspection. RawFields map[string]any // RecordedAt stores when the malformed intent was durably recorded. RecordedAt time.Time } // IsKnown reports whether code belongs to the frozen malformed-intent // rejection surface. func (code FailureCode) IsKnown() bool { switch code { case FailureCodeInvalidIntent, FailureCodeInvalidPayload, FailureCodeIdempotencyConflict, FailureCodeRecipientNotFound: return true default: return false } } // Validate reports whether entry contains a complete malformed-intent record. func (entry Entry) Validate() error { if strings.TrimSpace(entry.StreamEntryID) == "" { return fmt.Errorf("malformed intent stream entry id must not be empty") } if !entry.FailureCode.IsKnown() { return fmt.Errorf("malformed intent failure code %q is unsupported", entry.FailureCode) } if strings.TrimSpace(entry.FailureMessage) == "" { return fmt.Errorf("malformed intent failure message must not be empty") } if strings.TrimSpace(entry.FailureMessage) != entry.FailureMessage { return fmt.Errorf("malformed intent failure message must not contain surrounding whitespace") } if entry.RawFields == nil { return fmt.Errorf("malformed intent raw fields must not be nil") } if err := validateJSONObject("malformed intent raw fields", entry.RawFields); err != nil { return err } if err := validateTimestamp("malformed intent recorded at", entry.RecordedAt); err != nil { return err } return nil } func validateJSONObject(name string, value map[string]any) error { 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 } func validateTimestamp(name string, value time.Time) error { if value.IsZero() { return fmt.Errorf("%s must not be zero", name) } if !value.Equal(value.UTC()) { return fmt.Errorf("%s must be UTC", name) } if !value.Equal(value.Truncate(time.Millisecond)) { return fmt.Errorf("%s must use millisecond precision", name) } return nil }