feat(inbound): SESInboundParser (AWS SES via SNS HTTP subscription)#38
Closed
mpge wants to merge 1 commit intofeat/attachment-downloaderfrom
Closed
feat(inbound): SESInboundParser (AWS SES via SNS HTTP subscription)#38mpge wants to merge 1 commit intofeat/attachment-downloaderfrom
mpge wants to merge 1 commit intofeat/attachment-downloaderfrom
Conversation
Ports escalated-go#36 + escalated-dotnet#30 + escalated-spring#33
+ escalated-phoenix#42 to Symfony. AWS SES receipt rules publish
to an SNS topic; host apps subscribe via HTTP and SNS POSTs the
envelope to the unified /escalated/webhook/email/inbound?adapter=ses
webhook.
SESInboundParser handles:
1. Type=SubscriptionConfirmation — throws
SESSubscriptionConfirmationException carrying topicArn +
subscribeUrl + token. Host apps GET subscribeUrl out-of-band.
2. Type=Notification — parses the JSON-encoded Message field
for mail.commonHeaders (from/to/subject) and the mail.headers
array (Message-ID / In-Reply-To / References). Falls back to
mail.headers when commonHeaders doesn't surface a threading
field.
3. Best-effort MIME body extraction from the base64 content
field. Hand-rolled splitter (no external MIME dep) handles
single-part text/plain, text/html, multipart/alternative, and
quoted-printable transfer encoding.
10 PHPUnit cases cover all branches including subscription
confirmation detection, threading metadata extraction, plain +
multipart body decoding, missing-content fallback, unknown
envelope type, missing/malformed Message, and headers-array
threading fallback. All 10 green locally.
This was referenced Apr 24, 2026
Member
Author
|
Superseded by #39 which merged the full inbound stack including SES parser. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Completes the SES parser rollout across all 5 greenfield frameworks. Ports escalated-go#36 + escalated-dotnet#30 + escalated-spring#33 + escalated-phoenix#42 to Symfony.
What's handled
`Type=SubscriptionConfirmation` — throws `SESSubscriptionConfirmationException` carrying `topicArn` + `subscribeUrl` + `token`. Host apps GET `subscribeUrl` out-of-band to activate the subscription.
`Type=Notification` — parses the JSON-encoded `Message` field for `mail.commonHeaders` (from/to/subject) and the `mail.headers` array (Message-ID / In-Reply-To / References threading). Falls back to `mail.headers` when `commonHeaders` doesn't surface a threading field.
Best-effort MIME body extraction from the base64 `content` field when SES is configured with `action.type=SNS` / `encoding=BASE64`. Hand-rolled splitter (no external MIME dep) handles single-part `text/plain`, `text/html`, `multipart/alternative`, and `quoted-printable` transfer encoding.
Tests
10 PHPUnit cases — all 10 green locally (`vendor/bin/phpunit tests/Mail/Inbound/SESInboundParserTest.php` → `OK (10 tests, 26 assertions)`).
Stacked PR
Based on `feat/attachment-downloader` (#37). Merge order: #30 → #31 → #32 → #33 → #36 → #37 → this PR.