Skip to content

feat(inbound): SESInboundParser (AWS SES via SNS HTTP subscription)#38

Closed
mpge wants to merge 1 commit intofeat/attachment-downloaderfrom
feat/ses-parser
Closed

feat(inbound): SESInboundParser (AWS SES via SNS HTTP subscription)#38
mpge wants to merge 1 commit intofeat/attachment-downloaderfrom
feat/ses-parser

Conversation

@mpge
Copy link
Copy Markdown
Member

@mpge mpge commented Apr 24, 2026

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

  1. `Type=SubscriptionConfirmation` — throws `SESSubscriptionConfirmationException` carrying `topicArn` + `subscribeUrl` + `token`. Host apps GET `subscribeUrl` out-of-band to activate the subscription.

  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 threading). Falls back to `mail.headers` when `commonHeaders` doesn't surface a threading field.

  3. 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.

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.
@mpge
Copy link
Copy Markdown
Member Author

mpge commented Apr 27, 2026

Superseded by #39 which merged the full inbound stack including SES parser.

@mpge mpge deleted the branch feat/attachment-downloader April 27, 2026 02:38
@mpge mpge closed this Apr 27, 2026
@mpge mpge deleted the feat/ses-parser branch April 27, 2026 02:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant