[PB-1966]: refactor/reply a message - #96
Conversation
📝 WalkthroughWalkthroughEmail reply support now includes a dedicated controller route, reply DTOs, subject/thread resolution, Message-ID generation and reuse, JMAP threading updates, provider error handling, and expanded tests for reply and mapping behavior. ChangesEmail reply threading and delivery
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant EmailController
participant EmailService
participant JmapMailProvider
participant JmapMapper
EmailController->>EmailService: replyEmail(userEmail, parentId, dto, deliveryMode)
EmailService->>JmapMailProvider: getThreadingHeaders(parentId)
JmapMailProvider-->>EmailService: threading headers and parent subject
EmailService->>JmapMailProvider: send reply
JmapMailProvider->>JmapMapper: map message with Message-ID and reply headers
JmapMapper-->>JmapMailProvider: JMAP email create payload
JmapMailProvider-->>EmailService: delivery result
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/modules/email/email.service.ts (1)
209-263: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
dispatchExternalreturns the SMTP Message-ID as the responseid, not the created email's JMAP id.
saveToSent's result is awaited but discarded (Line 251), and the method returns{ id: messageId }— the RFC5322Message-IDheader value from SMTP — instead of the JMAP entity idsaveToSentactually created for the Sent copy. JMAP'sEmail.idand theMessage-IDheader are different identifier spaces (a JMAP id is what other endpoints such asGET /:id,PATCH /:id,DELETE /:id,GET threads/:id, and attachment download expect);EmailCreatedResponseDto.idis documented with a JMAP-shaped example ('Ma1f09b…').Since
dispatchExternalis now also the delivery path for the newPOST :id/reply(EXTERNAL) endpoint, this contract mismatch affects the new reply feature too: a client replying externally gets back an unusable "id" for any follow-up call on that message.🐛 Proposed fix
- await this.mail.saveToSent( + const sentCopy = await this.mail.saveToSent( userEmail, { ...dto, textBody: dto.encryption ? packEnvelope(dto.encryption) : dto.textBody, htmlBody: undefined, }, threading, messageId, ); - return { id: messageId }; + return { id: sentCopy.id };Note: this will also require updating
email.service.spec.tsassertions that currently expect the SMTPmessageIdas the returnedid(e.g. the "plain external email"/"encrypted attachments" tests).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/modules/email/email.service.ts` around lines 209 - 263, Update dispatchExternal to capture the result returned by mail.saveToSent and return its created JMAP email id instead of the SMTP messageId. Preserve messageId as the value passed to saveToSent for threading and Sent-copy persistence, and update the affected email.service.spec.ts expectations to assert the JMAP id.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/modules/email/email.service.ts`:
- Around line 209-263: Update dispatchExternal to capture the result returned by
mail.saveToSent and return its created JMAP email id instead of the SMTP
messageId. Preserve messageId as the value passed to saveToSent for threading
and Sent-copy persistence, and update the affected email.service.spec.ts
expectations to assert the JMAP id.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c470f2bc-bb70-425f-a56a-c05463e0457a
📒 Files selected for processing (13)
src/modules/email/email.controller.spec.tssrc/modules/email/email.controller.tssrc/modules/email/email.dto.tssrc/modules/email/email.service.spec.tssrc/modules/email/email.service.tssrc/modules/email/email.types.tssrc/modules/email/mail-provider.port.tssrc/modules/email/threading.spec.tssrc/modules/email/threading.tssrc/modules/infrastructure/jmap/jmap-mail.mapper.spec.tssrc/modules/infrastructure/jmap/jmap-mail.mapper.tssrc/modules/infrastructure/jmap/jmap-mail.provider.spec.tssrc/modules/infrastructure/jmap/jmap-mail.provider.ts



Replies to an email were showing up as separate, standalone items in the inbox instead of being grouped under the original conversation. We refactored the flow to reply an email. What we did:
Summary by CodeRabbit
New Features
Re:subject when no subject is provided.Bug Fixes