Thin facade over the host-configured Ecto.Repo.
Mailglass does not own a Repo — the host application does. Every context
module that needs Postgres routes through this facade, which resolves the
real Repo via Application.get_env(:mailglass, :repo) at call time.
Runtime resolution is deliberate: tests inject a test repo through
config/test.exs, host apps inject their Repo through
config :mailglass, repo: MyApp.Repo, and neither path requires
recompiling mailglass.
This module re-exports only what mailglass itself uses. Callers that need lower-level operations call the host Repo directly.
SQLSTATE 45A01 translation (D-06)
Every write that touches mailglass_events can raise the immutability
trigger (BEFORE UPDATE OR DELETE raises SQLSTATE 45A01). The facade
rescues %Postgrex.Error{} at every call site and reraises as
Mailglass.EventLedgerImmutableError so callers pattern-match a
mailglass-owned error, never the raw Postgrex struct. The translation
is centralized in translate_postgrex_error/2 — adding new write
functions means wiring the same rescue clause.
Summary
Functions
Delegates to the host Repo's all/2.
Delegates to the host Repo's delete/2, translating event-ledger immutability errors.
Delegates to the host Repo's delete_all/2. Used by
Mailglass.Webhook.Pruner (Phase 4 D-16) for retention-policy
DELETEs against mailglass_webhook_events.
Delegates to the host Repo's get/3.
Delegates to the host Repo's insert/2, translating event-ledger immutability errors.
Executes an Ecto.Multi against the host-configured repo and returns
the canonical {:ok, changes} / {:error, step, reason, changes} shape.
Delegates to the host Repo's one/2.
Delegates to the host Repo's query!/2. Raw passthrough — no SQLSTATE
translation.
Delegates to Ecto.Repo.transact/2 on the host-configured Repo.
Delegates to the host Repo's update/2, translating event-ledger immutability errors.
Functions
@spec all( Ecto.Queryable.t(), keyword() ) :: [struct()]
Delegates to the host Repo's all/2.
@spec delete( struct() | Ecto.Changeset.t(), keyword() ) :: {:ok, struct()} | {:error, Ecto.Changeset.t()}
Delegates to the host Repo's delete/2, translating event-ledger immutability errors.
@spec delete_all( Ecto.Queryable.t(), keyword() ) :: {non_neg_integer(), nil | [term()]}
Delegates to the host Repo's delete_all/2. Used by
Mailglass.Webhook.Pruner (Phase 4 D-16) for retention-policy
DELETEs against mailglass_webhook_events.
Does NOT translate SQLSTATE 45A01 — that trigger fires only on
mailglass_events UPDATE/DELETE, not mailglass_webhook_events
(which is intentionally mutable + prunable per CONTEXT D-15 split).
@spec get(Ecto.Queryable.t(), term(), keyword()) :: struct() | nil
Delegates to the host Repo's get/3.
@spec insert( Ecto.Changeset.t() | struct(), keyword() ) :: {:ok, struct()} | {:error, Ecto.Changeset.t()}
Delegates to the host Repo's insert/2, translating event-ledger immutability errors.
Executes an Ecto.Multi against the host-configured repo and returns
the canonical {:ok, changes} / {:error, step, reason, changes} shape.
Added in Phase 3 Plan 01 so Mailglass.Outbound (Plan 05) can compose
two Multis (D-20) via a public function — repo/0 is deliberately
private to keep the facade narrow.
Raises Mailglass.ConfigError{type: :missing} when :repo is not
configured (same path as transact/2). Event-ledger-immutability
errors (SQLSTATE 45A01) are translated via the same rescue as the
other write helpers.
@spec one( Ecto.Queryable.t(), keyword() ) :: struct() | nil
Delegates to the host Repo's one/2.
@spec query!(String.t(), [term()]) :: %Postgrex.Result{ columns: term(), command: term(), connection_id: term(), messages: term(), num_rows: term(), rows: term() }
Delegates to the host Repo's query!/2. Raw passthrough — no SQLSTATE
translation.
Intentionally does NOT rescue %Postgrex.Error{}: the Phase 4 webhook
ingest Multi (Plan 06) calls query!/2 from inside Repo.transact/1
to run SET LOCAL statement_timeout = '2s' + SET LOCAL lock_timeout = '500ms' (D-29). Those SET LOCAL statements never produce SQLSTATE
45A01 — the immutability trigger fires only on UPDATE/DELETE against
mailglass_events rows — so translation would add latency for no gain
and muddle the semantics. Callers that need the trigger's translated
error use insert/2, update/2, delete/2, transact/1, or
multi/1 instead.
Delegates to Ecto.Repo.transact/2 on the host-configured Repo.
Preferred over Ecto.Repo.transaction/2 because Ecto 3.13+ transact/2
accepts a zero-arity function that returns {:ok, result} or
{:error, reason} and rolls the transaction back on :error without
requiring Ecto.Repo.rollback/1.
Raises Mailglass.ConfigError of type :missing when :repo is not
configured. Raises Mailglass.EventLedgerImmutableError when the
mailglass_events immutability trigger fires (SQLSTATE 45A01).
Examples
Mailglass.Repo.transact(fn ->
{:ok, inserted} = Mailglass.Events.append(multi)
{:ok, inserted}
end)
@spec update( Ecto.Changeset.t(), keyword() ) :: {:ok, struct()} | {:error, Ecto.Changeset.t()}
Delegates to the host Repo's update/2, translating event-ledger immutability errors.