Files
mesh-catalog/modules/mesh-mail-reader/cmd/mesh-mail-reader/hear.go
T

65 lines
2.7 KiB
Go

package main
// Hearing mesh-mail's added events (novox/hq ADR 0459 §3, §12, issue 1518): mesh-mail-reader consumes
// mesh-mail.mail.*.message.added under its own runs-as account, exactly as mesh-mail-notifier does (main.go
// AddedPattern, watchWith), but records each one as a waiting mail (store.go, pgstore.go) BEFORE the handler
// returns success — so a mail heard is never lost, even across a restart: the controller composes a durable
// consumer (MaxDeliver 5, AckWait 30s) that redelivers an event five times before it is dead-lettered, and a
// store failure here must make that redelivery happen, never swallow the event.
import (
"context"
"encoding/json"
"time"
)
// EventBody is what mesh-mail's added event says of a message (metadata only; mailevent.go names where it is
// said). Unknown fields are ignored: the reader reads only what it needs, never a body.
type EventBody struct {
Account string `json:"account"`
Folder string `json:"folder"`
UID uint32 `json:"uid"`
UIDValidity uint32 `json:"uidvalidity"`
MessageID string `json:"message-id"`
Date string `json:"date"`
From []Person `json:"from"`
Subject string `json:"subject"`
Size int64 `json:"size"`
Flags []string `json:"flags"`
}
// Hear records one added event as a waiting mail (1518/AC2): the store is written to before this returns
// nil, so the caller (the bus subscription's handler) only then acknowledges the event. A store failure is
// returned unchanged, so the event is redelivered rather than lost (1518/AC2). The same event heard twice
// (1518/AC3) is recorded once: AddWaiting itself is idempotent on the mail's own identity.
func Hear(ctx context.Context, store Store, raw json.RawMessage, now func() time.Time) error {
var body EventBody
if err := json.Unmarshal(raw, &body); err != nil {
// An event this reader cannot even read is taken and dropped, exactly as mesh-mail-notifier does
// (main.go watchWith): a malformed event would otherwise be redelivered forever, never succeeding.
return nil
}
date, err := time.Parse(time.RFC3339, body.Date)
if err != nil {
date = now()
}
w := WaitingMail{
Account: body.Account,
Folder: body.Folder,
MessageID: body.MessageID,
UIDValidity: body.UIDValidity,
UID: body.UID,
Date: date,
From: body.From,
Subject: body.Subject,
Flags: body.Flags,
Size: body.Size,
Heard: now(),
}
return store.AddWaiting(ctx, w)
}
// AddedPattern is the mesh-mail stream's event this module consumes (its own `consumes` entry, novox/hq ADR
// 0459 §3): a new message's `added` only.
const AddedPattern = "mesh-mail.mail.*.message.added"