Mailglass.Events.Event (Mailglass v0.1.0)

Copy Markdown View Source

Append-only row in mailglass_events.

Exposes changeset/1 for INSERTS only. There is NO update/delete helper — the mailglass_events_immutable_trigger (Plan 02) raises SQLSTATE 45A01 on any UPDATE/DELETE; Mailglass.Repo.transact/1 (Plan 01) translates to Mailglass.EventLedgerImmutableError.

Absence of an update helper also prevents code that looks like it could work but blows up in production.

Atom Sets

  • :type — full Anymail taxonomy + mailglass internal :dispatched and :suppressed (D-14 project-level).
  • :reject_reason:invalid | :bounced | :timed_out | :blocked | :spam | :unsubscribed | :other (nullable; D-14).

Relationships

delivery_id is a logical :binary_id reference — NO FK to mailglass_deliveries (ARCHITECTURE §4.3; Pitfall 4 in RESEARCH). Orphan webhooks insert with delivery_id: nil and are linked later by Mailglass.Events.Reconciler (Plan 05).

Idempotency

:idempotency_key is a nullable string backed by a partial UNIQUE index (mailglass_events_idempotency_key_idx WHERE idempotency_key IS NOT NULL). Plan 05's Events.append/1 will pass on_conflict: :nothing, conflict_target: {:unsafe_fragment, "(idempotency_key) WHERE idempotency_key IS NOT NULL"} — replays are no-ops.

Summary

Functions

Closed reject_reason atom set.

Closed type atom set (Anymail taxonomy + internal). Cross-checked in api_stability.md.

Builds an INSERT-only changeset for a new %Event{}.

Types

t()

@type t() :: %Mailglass.Events.Event{
  __meta__: term(),
  delivery_id: Ecto.UUID.t() | nil,
  id: Ecto.UUID.t() | nil,
  idempotency_key: String.t() | nil,
  inserted_at: DateTime.t() | nil,
  metadata: map(),
  needs_reconciliation: boolean() | nil,
  normalized_payload: map(),
  occurred_at: DateTime.t() | nil,
  reject_reason: atom() | nil,
  tenant_id: String.t() | nil,
  trace_id: String.t() | nil,
  type: atom() | nil
}

Functions

__reject_reasons__()

(since 0.1.0)
@spec __reject_reasons__() :: [atom()]

Closed reject_reason atom set.

__types__()

(since 0.1.0)
@spec __types__() :: [atom()]

Closed type atom set (Anymail taxonomy + internal). Cross-checked in api_stability.md.

changeset(attrs)

(since 0.1.0)
@spec changeset(map()) :: Ecto.Changeset.t()

Builds an INSERT-only changeset for a new %Event{}.

Intentionally the only public mutation point. No update or delete helper is exposed — UPDATE and DELETE at the Repo layer raise Mailglass.EventLedgerImmutableError via the DB trigger, and the Elixir surface deliberately offers no path that looks like it could work.