Skip to content

Add Mailer — pluggable email delivery (#166)#180

Merged
gregwinn merged 1 commit into
developfrom
feature/mailer
Jun 13, 2026
Merged

Add Mailer — pluggable email delivery (#166)#180
gregwinn merged 1 commit into
developfrom
feature/mailer

Conversation

@gregwinn

Copy link
Copy Markdown
Owner

Summary

New Mailer module — Mailer.send/3,4 — with a config-selected transport. Sub-issue E (#166) of the auth epic (#161) and the prerequisite for account recovery (#167). Branched off develop (no stacking).

Config.put(:mailer, :transport, :http)
Config.put(:mailer, :api_key, System.get_env("SENDGRID_API_KEY"))
Config.put(:mailer, :from, "no-reply@myapp.com")

Mailer.send("user@example.com", "Welcome!", "Thanks for signing up.")
Mailer.send(email, "Reset your password", html_body, %{html: true})

Transports

Transport Behavior
:http POSTs to SendGrid's v3 API. Uses hackney directly (since winn_http doesn't expose request headers, needed for Authorization: Bearer) — no new deps.
:test Captures messages in-process (Mailer.captured/0 / Mailer.clear/0) so flows are assertable without sending mail.

SMTP is intentionally deferred — it would pull in a new dependency (e.g. gen_smtp). Noted in the module and docs; use :http for now. (Satisfies the issue's "SMTP implemented or explicitly deferred with a note".)

Testing

9 tests: capture, opts (from/html), ordering, clear, no-transport / unknown-transport errors, http-without-api-key error, SendGrid payload shape (text + html), and an e2e codegen-resolution test (Mailer.send through the compiler). Full suite 757, only the pre-existing winn_sqlite_tests esqlite-NIF failure.

Docs

docs/stdlib.md gains a Mailer section (transports table, send, test helpers); CHANGELOG updated.

Part of #161. Closes #166 once merged. Unblocks #167 (email verification + password reset).

🤖 Generated with Claude Code

Phase 3 of the auth epic (#161); prerequisite for account recovery (#167).
New winn_mailer module (the `Mailer` Winn module) with config-selected transports.

- Mailer.send/3,4 -> {ok, transport} | {error, reason}; opts: from, reply_to, html.
- :http transport posts to SendGrid v3 via hackney directly (winn_http doesn't
  expose request headers) — no new deps. Reads mailer.api_key / mailer.from /
  optional mailer.endpoint from Config.
- :test transport captures messages in-process (captured/0, clear/0) so flows are
  assertable without sending mail.
- SMTP deferred (would add a dependency) — noted in module + docs.
- Mailer -> winn_mailer in winn_codegen_resolve.
- 9 tests (capture, opts, ordering, clear, unknown/no transport, no-api-key,
  SendGrid payload shape text+html, e2e codegen resolution). Full suite 757
  (only the pre-existing esqlite-NIF failure). docs/stdlib.md + CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gregwinn gregwinn added this to the v1.0.0 — The Winn Platform milestone Jun 13, 2026
@gregwinn gregwinn added enhancement New feature or request area/stdlib Standard library modules (logger, crypto, json, etc.) labels Jun 13, 2026
@gregwinn gregwinn merged commit 687b6c2 into develop Jun 13, 2026
2 checks passed
@gregwinn gregwinn deleted the feature/mailer branch June 13, 2026 04:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/stdlib Standard library modules (logger, crypto, json, etc.) enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant