Skip to content

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

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

feat(inbound): SESInboundParser (AWS SES via SNS HTTP subscription)#30
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

Ports escalated-go#36 to .NET. AWS SES receipt rules publish to an SNS topic; host apps subscribe via HTTP and SNS POSTs the envelope to the unified `/support/webhook/email/inbound?adapter=ses` webhook.

What's handled

  1. `Type=SubscriptionConfirmation` — throws `SESSubscriptionConfirmationException` carrying `TopicArn` + `SubscribeUrl` + `Token`. Host apps GET the `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`. Includes an inline `MimeMessageParser` that handles single-part `text/plain`, `text/html`, `multipart/alternative`, and `quoted-printable` transfer encoding. No external MIME lib dep — pure stdlib (`System.Net.Mail`, `System.Net.Mime`, `System.Text`).

Tests

10 xUnit cases:

  • `Name` = `"ses"`.
  • Subscription confirmation → `SESSubscriptionConfirmationException` with populated `SubscribeUrl`.
  • Full threading metadata extraction (from / to / subject / Message-ID / In-Reply-To / References / headers dict).
  • Plain single-part body decoding.
  • Multipart body decoding (text + html parts).
  • Missing content → empty body but metadata populated.
  • Unknown envelope type → `InvalidOperationException`.
  • Missing Message field → `InvalidOperationException`.
  • Malformed Message JSON → `InvalidOperationException`.
  • Headers-array fallback when commonHeaders lacks threading fields.

Stacked PR

Based on `feat/attachment-downloader` (#29). Merge order: #23#24#25#26#29 → this PR.

Ports escalated-go#36 to .NET. AWS SES receipt rules publish to
an SNS topic; host apps subscribe via HTTP and SNS POSTs the
envelope to the unified /support/webhook/email/inbound?adapter=ses
webhook.

SESInboundParser handles:

  1. Type=SubscriptionConfirmation — throws
     SESSubscriptionConfirmationException carrying TopicArn +
     SubscribeUrl. Host apps GET the 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. Includes an inline MimeMessageParser that
     handles single-part text/plain, text/html,
     multipart/alternative, and quoted-printable transfer encoding.
     No external MIME lib dep.

10 xUnit tests cover: Name check, subscription confirmation path,
full threading metadata extraction, plain body decode, multipart
body decode, missing content → empty body fallback, unknown
envelope type, missing/malformed Message, headers-array
fallback for threading fields.
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