Files
warmbly/internal/models/event_variants.go
T
Matthew Meszaros f106c8541d feat: make the bus envelopes Avro-encodable (#535)
* feat: make both bus envelopes Avro-encodable by deriving each one's schema from a declared registry of body types, with a union branch per body and our own struct walk that skips unexported fields and honours avro:"-" before descending, so the schema describes exactly what encoding/json already puts on the wire, and narrow the two sync cursors on the wire DTOs to int64 because Avro has no unsigned 64-bit type

* feat: stop the instance health check, the config registry and the docs all claiming Avro cannot serialize a worker envelope, which stopped being true once the envelopes carried a declared union, and check the one thing that is still a real misconfiguration instead: avro selected with no SCHEMA_REGISTRY_URL to resolve against

* feat: emit a reference the second time a record appears in an envelope schema instead of defining it again, because Avro names a record once and a document that defines warmbly.events.Token three times is rejected outright, and keep the Schema Registry round-trip as a skip-by-default test since only a registry judges the document rather than the objects it was built from

* feat: carry uint64 as Avro fixed(8) rather than long, which lets the sync cursors keep their unsigned type instead of being narrowed, name every event field after its json tag so the schema and the JSON wire agree, and populate every field in the round-trip test because zero values are why a uint64 mapped to long passed in the first place

* feat: frame Avro in Confluent's wire format and encode through hamba's default API instead of going through avrov2, whose private avro.API holds a type resolver avro.Register cannot reach, so a union body failed there with unable to resolve type while encoding cleanly against the same schema, and keep the registry round-trip as a skip-by-default test
2026-09-15 10:50:30 -07:00

61 lines
3.1 KiB
Go

package models
// The two bus envelopes are tagged unions: a discriminator plus a body whose
// Go type depends on it. Nothing in the type system connects the two, so this
// is where that connection is written down, once, for every reader that needs
// it.
//
// It exists because a schema-carrying codec has to know the shape of a body
// before it sees one. JSON could stay ignorant and decode into a map; Avro
// resolves a union branch, which means every body type has to be declared up
// front. Keeping the declaration here rather than inside the codec means the
// list cannot be built from what a codec happens to have been asked to encode.
//
// A new event type is not carried until it is added here. The round-trip test
// over this map is what turns that from a rule into a failure.
// WorkerEventBodies maps every worker command to the type its body carries.
// Pointer-ness matters and is deliberate: it matches what the publisher sends
// and what the worker's handler expects, so a decoded body satisfies the
// handler's type assertion instead of taking a re-marshalling detour.
var WorkerEventBodies = map[WorkerEventType]any{
WorkerEventTypeSendEmail: (*SendEmail)(nil),
WorkerEventTypeAddEmail: (*AddWorkerEmail)(nil),
WorkerEventTypeRemoveEmail: (*RemoveWorkerEmail)(nil),
WorkerEventTypeEmailValidation: EventWorkerEmailValidation{},
WorkerEventTypeWarmupAction: (*WarmupEmailAction)(nil),
WorkerEventTypeMessageSeen: (*MessageSeenAction)(nil),
WorkerEventTypeMailboxIdentity: EventWorkerMailboxIdentity{},
}
// JobEventBodies is the same for everything a worker reports back.
//
// Several types share a body, which is correct rather than a mistake to tidy
// up: the four error events differ in what they mean and not in what they
// carry, and a send is reported the same way whether it worked. The
// discriminator separates them, so the union only needs each distinct type
// once.
var JobEventBodies = map[JobEventType]any{
JobEventTypeNewEmail: (*JobEventNewEmail)(nil),
JobEventTypeInboundBounce: (*JobEventInboundBounce)(nil),
JobEventTypeInboundComplaint: (*JobEventInboundComplaint)(nil),
JobEventTypeEmailUpdate: (*JobEventEmailUpdate)(nil),
JobEventTypeRemoveEmail: (*JobEventRemoveEmail)(nil),
JobEventTypeFlagsAdd: (*JobEventFlags)(nil),
JobEventTypeFlagsRemove: (*JobEventFlags)(nil),
JobEventTypeMailboxUpdate: (*JobEventMailboxUpdate)(nil),
JobEventTypeMailboxDelete: (*JobEventMailboxDelete)(nil),
JobEventTypeMailboxRename: (*JobEventMailboxRename)(nil),
JobEventTypeHistoryIDUpdate: (*JobEventHistoryIDUpdate)(nil),
JobEventTypeGraphDeltaUpdate: (*JobEventGraphDeltaUpdate)(nil),
JobEventTypeSyncState: (*JobEventSyncState)(nil),
JobEventTypeTokenUpdate: (*JobEventTokenUpdate)(nil),
JobEventTypeEmailSent: SendEmailResult{},
JobEventTypeEmailFailed: SendEmailResult{},
JobEventTypeEmailAuthError: EmailErrorEvent{},
JobEventTypeEmailDisabled: EmailErrorEvent{},
JobEventTypeEmailRateLimited: EmailErrorEvent{},
JobEventTypeEmailServerError: EmailErrorEvent{},
JobEventTypeWorkerHealth: WorkerHealthSample{},
}