Skip to content

feat(email): wire MessageIdUtil into EmailService outbound headers#25

Open
mpge wants to merge 1 commit intofeat/email-message-idfrom
feat/email-service-wireup
Open

feat(email): wire MessageIdUtil into EmailService outbound headers#25
mpge wants to merge 1 commit intofeat/email-message-idfrom
feat/email-service-wireup

Conversation

@mpge
Copy link
Copy Markdown
Member

@mpge mpge commented Apr 24, 2026

Summary

Wires the MessageIdUtil helpers (added in #24) into EmailService so outbound ticket notifications carry canonical RFC 5322 Message-ID headers plus signed Reply-To.

Headers set

Scenario Message-ID In-Reply-To References Reply-To
Initial ticket notification <ticket-{id}@{domain}> reply+{id}.{hmac8}@{domain}
Reply notification <ticket-{id}-reply-{replyId}@{domain}> ticket root ticket root signed
Satisfaction survey signed

The signed Reply-To routes inbound replies back to the right ticket even when the mail client strips our Message-ID / In-Reply-To chain.

Config

New EscalatedProperties.Email:

  • escalated.email.domain (default: localhost) — right-hand side of Message-IDs + Reply-To
  • escalated.email.inbound-secret (default: empty — Reply-To is omitted when unset, so basic threading still works without activating the inbound pipeline)

Cleanup

Removes the previous reliance on ticket.getEmailMessageId() / reply.getEmailMessageId() — these fields were never populated and yielded null Message-IDs.

Dependencies

Test plan

  • 5 unit tests: initial notification headers, reply-in-thread headers, inbound-secret-disabled path, satisfaction survey. Uses real MimeMessage + a mocked JavaMailSender so assertions are on actual header values.
  • CI green (won't trigger against stacked base until rebased)

Uses MessageIdUtil (added in #24) to stamp canonical RFC 5322
Message-ID headers plus signed Reply-To on every outbound ticket
email:

- Initial ticket notification: <ticket-{id}@{domain}>
- Reply notification: <ticket-{id}-reply-{replyId}@{domain}> plus
  In-Reply-To + References pointing to the ticket-root Message-ID
- Reply-To: reply+{id}.{hmac8}@{domain} on all outbound mail so the
  inbound provider webhook can verify ticket identity independently
  of the mail client's threading headers.

Adds EscalatedProperties.Email config block with:
  escalated.email.domain        (default: localhost)
  escalated.email.inbound-secret (default: empty — Reply-To skipped)

When inbound-secret is blank, Reply-To is omitted but Message-ID +
In-Reply-To still land so basic threading works.

Removes the previous reliance on ticket.getEmailMessageId() /
reply.getEmailMessageId() — these were never populated and yielded
null Message-IDs.

5 new unit tests cover the three notification types + the
inbound-secret-disabled path, using real MimeMessage + a mocked
JavaMailSender so we can assert on actual header values.
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