Accrue.Workers.Mailer (accrue v0.3.0)

Copy Markdown View Source

Oban worker that delivers a transactional email asynchronously.

Flow

  1. Accrue.Mailer.Default.deliver/2 enqueues a job with string-keyed %{"type" => "...", "assigns" => %{...}} args (Oban-safe scalars only).
  2. perform/1 rehydrates the assigns (locale/timezone/customer hydration via enrich/2), resolves the template module (honoring :email_overrides rung 2 MFA and rung 3 atom), builds a %Swoosh.Email{}, and delivers via Accrue.Mailer.Swoosh.

Queue

Host applications MUST configure an Oban queue named :accrue_mailers. Recommended concurrency: 20.

Pitfall 7 defense

unique: [period: 60, fields: [:args, :worker]] prevents double-dispatch when both a Billing action AND a webhook reducer try to enqueue the same email within 60s. DO NOT TOUCH this option — it's the only guard against the action+webhook duplication pitfall.

Summary

Functions

Enriches raw Oban-arg assigns with locale, timezone, and (optionally) the hydrated Accrue.Billing.Customer struct (D6-03 precedence ladder).

Resolves the template module for an email type. Honors :email_overrides (Pay-style ladder D-23)

Public accessor for the default template module for a given email type. Used by mix accrue.mail.preview (D6-08) and by tests. Honors the full 13-type catalogue plus the :payment_succeeded legacy alias.

Functions

enrich(type, assigns)

@spec enrich(atom(), map()) :: map()

Enriches raw Oban-arg assigns with locale, timezone, and (optionally) the hydrated Accrue.Billing.Customer struct (D6-03 precedence ladder).

Precedence for locale:

  1. assigns[:locale] / assigns["locale"]
  2. customer.preferred_locale (hydrated via assigns[:customer_id])
  3. Accrue.Config.default_locale/0
  4. Hardcoded "en" fallback

Same ladder for timezone (swap preferred_timezone + Accrue.Config.default_timezone/0 + "Etc/UTC").

Unknown locales/zones emit [:accrue, :email, :locale_fallback] / [:accrue, :email, :timezone_fallback] telemetry with %{requested: value} metadata (no PII) and fall back to "en" / "Etc/UTC". enrich/2 NEVER raises — pitfall 5 defense.

resolve_template(type)

@spec resolve_template(atom()) :: module()

Resolves the template module for an email type. Honors :email_overrides (Pay-style ladder D-23):

  • Rung 2 (MFA): {Mod, :fun, args} calls Mod.fun(type, *args) at runtime, letting hosts pick a template dynamically based on request- time context. The type atom is prepended to args as the first argument so MFA callbacks receive it.
  • Rung 3 (atom): YourModule replaces the default template module.
  • No override: falls through to default_template/1.

template_for(type)

@spec template_for(atom()) :: module()

Public accessor for the default template module for a given email type. Used by mix accrue.mail.preview (D6-08) and by tests. Honors the full 13-type catalogue plus the :payment_succeeded legacy alias.