feat: add Amazon SES email adapter#123
Conversation
Add a production AWS SES adapter built for high-volume bulk sending (the 500K-recipient newsletter path), using the SES API v2 directly with no AWS SDK dependency. Primary path (no attachments) uses SendBulkEmail, which is template-based: a deterministic template name is derived from a hash of subject+body+isHtml, created on demand the first time SES reports it missing, and reused across every batch of the same send. Each recipient maps to one BulkEmailEntry and its BulkEmailEntryResults[].Status is mapped to a per-recipient result. Fallback path (attachments present) uses SendEmail with Content.Raw, one request per recipient, with the MIME assembled via PHPMailer (matching the SMTP adapter). SES templates cannot carry attachments, so this mirrors how the Resend adapter falls back to individual sends. Authentication is hand-rolled AWS Signature Version 4 (service "ses"), supporting temporary credentials via an optional session token. The signing core is covered by a deterministic unit test asserting it reproduces AWS's published aws-sig-v4-test-suite "get-vanilla" signature, plus request-routing unit tests (bulk endpoint, template lifecycle, result parsing, 50-recipient limit, Raw attachment fallback) and env-gated live tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Greptile SummaryThis PR introduces a new Amazon SES email adapter (
Confidence Score: 5/5The adapter is well-scoped and all previously raised concerns have been addressed in this revision. The SigV4 signing is verified against a published AWS test vector. The bulk and raw send paths correctly propagate CC/BCC, handle per-recipient result mapping, guard against oversized attachments (both pre-MIME and post-build), and fall back gracefully on template-missing errors. The Email class auto-normalizes recipients and ensures replyToEmail/replyToName are always non-empty, so the unconditional addReplyTo call in buildMime is safe. No correctness issues were identified in this revision. No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "fix(SES): address review feedback on bul..." | Re-trigger Greptile |
There was a problem hiding this comment.
Pull request overview
Adds an Amazon SES (API v2) email adapter to the messaging library, including deterministic AWS SigV4 signing, template-backed bulk sending for high-volume newsletters, and a raw-MIME per-recipient fallback for attachments. This fits into the existing adapter architecture alongside SendGrid/Mailgun/Resend/SMTP.
Changes:
- Introduce
SESadapter with SigV4 signing, bulkSendBulkEmailpath, and rawSendEmailfallback for attachments. - Add deterministic unit tests for signing and request routing, plus env-gated live SES integration tests.
- Update README usage examples and provider checklist to include SES.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Utopia/Messaging/Adapter/Email/SES.php | New SES adapter implementing bulk + raw send paths and hand-rolled SigV4 signing. |
| tests/Messaging/Adapter/Email/SESRoutingTest.php | Network-free tests covering endpoint routing, template lifecycle, per-recipient result mapping, limits, and session-token signing. |
| tests/Messaging/Adapter/Email/SESSigningTest.php | Deterministic SigV4 test against AWS “get-vanilla” vector. |
| tests/Messaging/Adapter/Email/SESTest.php | Env-gated live integration tests hitting real SES API. |
| README.md | Document SES adapter usage and mark SES as supported. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Resolves the review comments on the SES adapter: - Bulk path now sets CcAddresses/BccAddresses on each BulkEmailEntry Destination; previously CC/BCC recipients on attachment-free sends were silently dropped. - formatAddress() wraps display names containing RFC 5322 specials in a quoted-string, so a name like "Acme, Inc." no longer produces a malformed address that SES rejects with a 400. - Template names are truncated to SES's 64-character limit; the prefix plus a full SHA-256 hex was 71 chars, which failed every CreateEmailTemplate. - A 2xx SendBulkEmail response without parseable per-recipient results is now reported as a failure for all recipients instead of false-positive successes. - MAX_ATTACHMENT_BYTES is overridden to SES's real 10MB message limit, and the assembled MIME size is validated so oversized sends (including base64/MIME overhead) fail fast with a clear exception. - Documents that content-hashed templates are never deleted and accumulate toward the per-account template quota. Adds routing-test coverage for CC/BCC forwarding, address quoting, the template-name length cap, the unparseable-success failure path, and the MIME size limit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a production AWS SES adapter built for high-volume bulk sending (the 500K-recipient newsletter path), using the SES API v2 directly with no AWS SDK dependency.
Primary path (no attachments) uses SendBulkEmail, which is template-based: a deterministic template name is derived from a hash of subject+body+isHtml, created on demand the first time SES reports it missing, and reused across every batch of the same send. Each recipient maps to one BulkEmailEntry and its BulkEmailEntryResults[].Status is mapped to a per-recipient result.
Fallback path (attachments present) uses SendEmail with Content.Raw, one request per recipient, with the MIME assembled via PHPMailer (matching the SMTP adapter). SES templates cannot carry attachments, so this mirrors how the Resend adapter falls back to individual sends.
Authentication is hand-rolled AWS Signature Version 4 (service "ses"), supporting temporary credentials via an optional session token. The signing core is covered by a deterministic unit test asserting it reproduces AWS's published aws-sig-v4-test-suite "get-vanilla" signature, plus request-routing unit tests (bulk endpoint, template lifecycle, result parsing, 50-recipient limit, Raw attachment fallback) and env-gated live tests.
What does this PR do?
(Provide a description of what this PR does.)
Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)
Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
Have you read the Contributing Guidelines on issues?
(Write your answer here.)