From 6950754a67472b4990d43ce7be7eabddf8d6a084 Mon Sep 17 00:00:00 2001 From: aolamide Date: Wed, 9 Apr 2025 22:51:54 +0100 Subject: [PATCH 1/7] types: Add reply_to field to CommonMail type --- src/types/mailtrap.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/mailtrap.ts b/src/types/mailtrap.ts index 2cca7b0..6731ebd 100644 --- a/src/types/mailtrap.ts +++ b/src/types/mailtrap.ts @@ -23,6 +23,7 @@ export type CommonMail = { attachments?: Attachment[]; headers?: MailtrapHeaders; custom_variables?: CustomVariables; + reply_to?: Address; }; export type TemplateVariables = Record; From 708a864343b48a2e6341858f9af13ba302a9ee79 Mon Sep 17 00:00:00 2001 From: aolamide Date: Wed, 9 Apr 2025 22:52:54 +0100 Subject: [PATCH 2/7] adapters: Add reply_to handling in adaptMail and implement adaptReplyToRecipient function --- src/adapters/mail.ts | 3 ++- src/adapters/recipients.ts | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/adapters/mail.ts b/src/adapters/mail.ts index 8139d29..26e7fbe 100644 --- a/src/adapters/mail.ts +++ b/src/adapters/mail.ts @@ -1,7 +1,7 @@ import adaptAttachment from "./attachement"; import adaptContent from "./content"; import adaptHeaders from "./headers"; -import adaptRecipients, { adaptSingleRecipient } from "./recipients"; +import adaptRecipients, { adaptSingleRecipient, adaptReplyToRecipient } from "./recipients"; import CONFIG from "../config"; @@ -27,6 +27,7 @@ export default function adaptMail(data: MailtrapMailOptions): Mail | SendError { to: adaptRecipients(data.to), cc: adaptRecipients(data.cc), bcc: adaptRecipients(data.bcc), + reply_to: adaptReplyToRecipient(data.replyTo), }; if (data.headers) { diff --git a/src/adapters/recipients.ts b/src/adapters/recipients.ts index befc895..412d980 100644 --- a/src/adapters/recipients.ts +++ b/src/adapters/recipients.ts @@ -38,3 +38,27 @@ export default function adaptRecipients( return recipients.map(adaptSingleRecipient); } + +/** + * If there is no recipient or empty array is passed, then return undefined since it is an optional field. + * If it's not array, then adapt recipient and returns it. + * Otherwise, if type is array as nodemailer allows, we pick the first recipient + * as Mailtrap doesn't support multiple reply-to recipients. + */ +export function adaptReplyToRecipient( + recipients: + | string + | NodemailerAddress + | Array + | undefined +): Address | undefined { + if(!recipients || (Array.isArray(recipients) && recipients.length === 0)) { + return undefined; + } + + if (!Array.isArray(recipients)) { + return adaptSingleRecipient(recipients); + } + + return adaptSingleRecipient(recipients[0]); +} \ No newline at end of file From 16325b97936d9b7d17d1544be99c51e506641964 Mon Sep 17 00:00:00 2001 From: aolamide Date: Wed, 9 Apr 2025 22:55:29 +0100 Subject: [PATCH 3/7] tests: Update adapter tests to account for various cases of replyTo --- src/__tests__/adapters/mail.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/__tests__/adapters/mail.test.ts b/src/__tests__/adapters/mail.test.ts index d5a1ebb..e449696 100644 --- a/src/__tests__/adapters/mail.test.ts +++ b/src/__tests__/adapters/mail.test.ts @@ -1,7 +1,7 @@ import adaptMail from "../../adapters/mail"; import config from "../../config"; -import { adaptSingleRecipient } from "../../adapters/recipients"; +import { adaptSingleRecipient, adaptReplyToRecipient } from "../../adapters/recipients"; const { ERRORS } = config; const { SUBJECT_REQUIRED, FROM_REQUIRED } = ERRORS; @@ -24,6 +24,7 @@ describe("adapters/mail: ", () => { mockKey: "mock-value", }, subject: "mock-subject", + replyTo: "mock-reply-to", }; const expectedResult = { @@ -33,6 +34,7 @@ describe("adapters/mail: ", () => { bcc: [], headers: data.headers, subject: data.subject, + reply_to: adaptReplyToRecipient(data.replyTo), }; const result = adaptMail(data); @@ -47,6 +49,7 @@ describe("adapters/mail: ", () => { mockKey: "mock-value", }, attachments: [{ filename: "mock-filename", content: "mock-content" }], + replyTo: [], }; const expectedResult = { @@ -57,6 +60,7 @@ describe("adapters/mail: ", () => { bcc: [], headers: data.headers, attachments: data.attachments, + reply_to: adaptReplyToRecipient(data.replyTo), }; const result = adaptMail(data); @@ -74,6 +78,7 @@ describe("adapters/mail: ", () => { customVariables: { user_id: "mock-user_id", }, + replyTo: ["mock-reply-to"], }; const expectedResult = { @@ -85,6 +90,7 @@ describe("adapters/mail: ", () => { headers: data.headers, attachments: data.attachments, custom_variables: data.customVariables, + reply_to: adaptReplyToRecipient(data.replyTo), }; const result = adaptMail(data); From 7e6376f211bfb439b9cbf86d0f0bbac2bf60d8fe Mon Sep 17 00:00:00 2001 From: aolamide Date: Wed, 9 Apr 2025 22:56:33 +0100 Subject: [PATCH 4/7] examples: Updated email example to show use of the reply_to field --- examples/sending/everything.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/sending/everything.ts b/examples/sending/everything.ts index b12e512..d4b9591 100644 --- a/examples/sending/everything.ts +++ b/examples/sending/everything.ts @@ -12,6 +12,7 @@ import { MailtrapClient } from "mailtrap" const TOKEN = ""; const SENDER_EMAIL = ""; const RECIPIENT_EMAIL = ""; +const REPLY_TO_EMAIL = ""; const client = new MailtrapClient({ token: TOKEN }); @@ -28,6 +29,7 @@ client from: { name: "Mailtrap Test", email: SENDER_EMAIL }, to: [{ email: RECIPIENT_EMAIL }], subject: "Hello from Mailtrap!", + reply_to: { email: REPLY_TO_EMAIL }, html: ` From 5ee1633900a3095693bf14032e096c0b398da5bf Mon Sep 17 00:00:00 2001 From: aolamide Date: Wed, 9 Apr 2025 23:22:43 +0100 Subject: [PATCH 5/7] tests: Add tests for adaptReplyToRecipient function to validate recipient handling --- src/__tests__/adapters/recipients.test.ts | 57 +++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/__tests__/adapters/recipients.test.ts b/src/__tests__/adapters/recipients.test.ts index 7968a09..b39aac1 100644 --- a/src/__tests__/adapters/recipients.test.ts +++ b/src/__tests__/adapters/recipients.test.ts @@ -1,5 +1,6 @@ import adaptRecipients, { adaptSingleRecipient, + adaptReplyToRecipient, } from "../../adapters/recipients"; describe("adapters/recipients: ", () => { @@ -85,4 +86,60 @@ describe("adapters/recipients: ", () => { expect(result).toEqual(expectedResult); }); }); + + describe("adaptReplyToRecipient(): ", () => { + it("returns undefined if recipients is invalid.", () => { + const recipients = undefined; + + const expectedResult = undefined; + const result = adaptReplyToRecipient(recipients); + + expect(result).toEqual(expectedResult); + }); + + it("returns undefined if recipients is empty array.", () => { + const recipients: any = []; + + const expectedResult = undefined; + const result = adaptReplyToRecipient(recipients); + + expect(result).toEqual(expectedResult); + }); + + it("returns adapted recipients if it's not an array.", () => { + const recipients = { + name: "mock-name", + address: "mock-email", + }; + + const expectedResult = { + name: recipients.name, + email: recipients.address, + }; + const result = adaptReplyToRecipient(recipients); + + expect(result).toEqual(expectedResult); + }); + + it("returns first adapted recipient if it's an array.", () => { + const recipients = [ + { + name: "mock-name-1", + address: "mock-email-1", + }, + { + name: "mock-name-2", + address: "mock-email-2", + }, + ]; + + const expectedResult = { + name: recipients[0].name, + email: recipients[0].address, + }; + const result = adaptReplyToRecipient(recipients); + + expect(result).toEqual(expectedResult); + }); + }); }); From 89a26f0dc61b4b0f9e217e23ade712402919ebe0 Mon Sep 17 00:00:00 2001 From: aolamide Date: Wed, 9 Apr 2025 23:26:20 +0100 Subject: [PATCH 6/7] style: Linting fixes --- src/__tests__/adapters/mail.test.ts | 5 ++++- src/adapters/mail.ts | 5 ++++- src/adapters/recipients.ts | 6 +++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/__tests__/adapters/mail.test.ts b/src/__tests__/adapters/mail.test.ts index e449696..7c118fa 100644 --- a/src/__tests__/adapters/mail.test.ts +++ b/src/__tests__/adapters/mail.test.ts @@ -1,7 +1,10 @@ import adaptMail from "../../adapters/mail"; import config from "../../config"; -import { adaptSingleRecipient, adaptReplyToRecipient } from "../../adapters/recipients"; +import { + adaptSingleRecipient, + adaptReplyToRecipient, +} from "../../adapters/recipients"; const { ERRORS } = config; const { SUBJECT_REQUIRED, FROM_REQUIRED } = ERRORS; diff --git a/src/adapters/mail.ts b/src/adapters/mail.ts index 26e7fbe..547001e 100644 --- a/src/adapters/mail.ts +++ b/src/adapters/mail.ts @@ -1,7 +1,10 @@ import adaptAttachment from "./attachement"; import adaptContent from "./content"; import adaptHeaders from "./headers"; -import adaptRecipients, { adaptSingleRecipient, adaptReplyToRecipient } from "./recipients"; +import adaptRecipients, { + adaptSingleRecipient, + adaptReplyToRecipient, +} from "./recipients"; import CONFIG from "../config"; diff --git a/src/adapters/recipients.ts b/src/adapters/recipients.ts index 412d980..458122e 100644 --- a/src/adapters/recipients.ts +++ b/src/adapters/recipients.ts @@ -42,7 +42,7 @@ export default function adaptRecipients( /** * If there is no recipient or empty array is passed, then return undefined since it is an optional field. * If it's not array, then adapt recipient and returns it. - * Otherwise, if type is array as nodemailer allows, we pick the first recipient + * Otherwise, if type is array as nodemailer allows, we pick the first recipient * as Mailtrap doesn't support multiple reply-to recipients. */ export function adaptReplyToRecipient( @@ -52,7 +52,7 @@ export function adaptReplyToRecipient( | Array | undefined ): Address | undefined { - if(!recipients || (Array.isArray(recipients) && recipients.length === 0)) { + if (!recipients || (Array.isArray(recipients) && recipients.length === 0)) { return undefined; } @@ -61,4 +61,4 @@ export function adaptReplyToRecipient( } return adaptSingleRecipient(recipients[0]); -} \ No newline at end of file +} From 089b7f5cc5b069fdf9f24a7d389a0890e03c10fa Mon Sep 17 00:00:00 2001 From: aolamide Date: Thu, 10 Apr 2025 08:54:05 +0100 Subject: [PATCH 7/7] examples: Add replyTo field to nodemailer transport example --- examples/sending/transport.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/sending/transport.ts b/examples/sending/transport.ts index 39cfb18..b6033f7 100644 --- a/examples/sending/transport.ts +++ b/examples/sending/transport.ts @@ -11,6 +11,7 @@ import { MailtrapTransport } from "mailtrap" const TOKEN = "" const SENDER_EMAIL = ""; const RECIPIENT_EMAIL = ""; +const REPLY_TO_EMAIL = ""; const transport = Nodemailer.createTransport(MailtrapTransport({ token: TOKEN, @@ -26,6 +27,7 @@ transport.sendMail({ address: SENDER_EMAIL, name: "Mailtrap Test" }, + replyTo: REPLY_TO_EMAIL, subject: "Hello from Mailtrap!", html: `