Mailglass.Outbound.Delivery (Mailglass v0.1.0)

Copy Markdown View Source

One row per (Message, recipient, provider) tuple. Mutable: projection columns are updated by Mailglass.Outbound.Projector (Plan 06).

Field order per CONTEXT.md "Claude's Discretion": id → tenant_id → foreign keys → state → metadata/flags → timestamps.

@primary_key is UUIDv7 via the Mailglass.Schema macro.

Atom Sets

  • :stream:transactional | :operational | :bulk (D-10)

  • :last_event_type — full Anymail event taxonomy + mailglass internal :dispatched / :suppressed (D-14 project-level)

Projection columns (D-13)

dispatched_at, delivered_at, bounced_at, complained_at, suppressed_at, terminal, last_event_type, last_event_at are the only Elixir-modifiable facts. Mailglass.Outbound.Projector (Plan 06) owns writes to these columns; metadata is a free-form jsonb bag for adopter-supplied non-PII extras.

Optimistic locking (D-18)

:lock_version defaults to 1. Consumers chain Ecto.Changeset.optimistic_lock(:lock_version) onto the changeset when updating — concurrent dispatch attempts raise Ecto.StaleEntryError on the loser.

Summary

Functions

Closed event-type atom set. Tested against api_stability.md (Phase 6 check).

Closed stream atom set.

Builds a changeset for a new %Delivery{} from an attr map.

Types

t()

@type t() :: %Mailglass.Outbound.Delivery{
  __meta__: term(),
  bounced_at: DateTime.t() | nil,
  complained_at: DateTime.t() | nil,
  delivered_at: DateTime.t() | nil,
  dispatched_at: DateTime.t() | nil,
  id: Ecto.UUID.t() | nil,
  idempotency_key: String.t() | nil,
  inserted_at: DateTime.t() | nil,
  last_error: map() | nil,
  last_event_at: DateTime.t() | nil,
  last_event_type: atom() | nil,
  lock_version: integer() | nil,
  mailable: String.t() | nil,
  metadata: map(),
  provider: String.t() | nil,
  provider_message_id: String.t() | nil,
  recipient: String.t() | nil,
  recipient_domain: String.t() | nil,
  status: :queued | :sent | :dispatched | :failed | :suppressed | nil,
  stream: :transactional | :operational | :bulk | nil,
  suppressed_at: DateTime.t() | nil,
  tenant_id: String.t() | nil,
  terminal: boolean() | nil,
  updated_at: DateTime.t() | nil
}

Functions

__event_types__()

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

Closed event-type atom set. Tested against api_stability.md (Phase 6 check).

__streams__()

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

Closed stream atom set.

changeset(attrs)

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

Builds a changeset for a new %Delivery{} from an attr map.

Auto-populates :recipient_domain from :recipient (denormalization per D-13) — a cheap cast-time computation that saves a SPLIT_PART() at query time for rate-limit and analytics reads.

changeset(delivery, attrs)

@spec changeset(t(), map()) :: Ecto.Changeset.t()