From f211b99a2b53fa476d48991f6dcf9e5bcd9fcc90 Mon Sep 17 00:00:00 2001 From: Ashish Naik Date: Sat, 19 Oct 2024 18:56:29 +0530 Subject: [PATCH] feat: Add ZeptoMail support (#98) --- README.md | 32 +++++++++++++++++ spec/ApiMailAdapter.spec.js | 48 ++++++++++++++++++++++++++ src/ApiPayloadConverter.js | 68 +++++++++++++++++++++++++++++++++---- 3 files changed, 142 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7358666..e3a02ae 100644 --- a/README.md +++ b/README.md @@ -323,6 +323,38 @@ const server = new ParseServer({ }); ``` +### Example for ZeptoMail Service + +This is an example for the ZeptoMail Service client using the ZeptoMail JavaScript SDK. +Provide comma separated email adddresses in recepient parameter to send to multiple. + +```js +// Configure mail client +var { SendMailClient } = require('zeptomail'); + +const url = process.env.ZEPTOMAIL_URL; +const token = process.env.ZEPTOMAIL_TOKEN; +const zeptoMaiClient = new SendMailClient({ url, token }); + + +// Configure Parse Server +const server = new ParseServer({ + ...otherServerOptions, + + emailAdapter: { + module: 'parse-server-api-mail-adapter', + options: { + ... otherAdapterOptions, + + apiCallback: async ({ payload, locale }) => { + const zeptoMailPayload = ApiPayloadConverter.zeptomail({ api: '1.1', payload }); + await zeptoMaiClient.sendMail(zeptoMailPayload); + }, + } + } +}); +``` + ## Custom API This is an example of how the API payload can be adapted in the adapter configuration `apiCallback` according to a custom email provider's API specification. diff --git a/spec/ApiMailAdapter.spec.js b/spec/ApiMailAdapter.spec.js index b10e4eb..5bd827a 100644 --- a/spec/ApiMailAdapter.spec.js +++ b/spec/ApiMailAdapter.spec.js @@ -402,6 +402,54 @@ describe('ApiMailAdapter', () => { expect(payload.Message.Body.Text.Data).toBe(examplePayload.text); expect(payload.Message.Body.Html.Data).toBe(examplePayload.html); }); + + describe('convert ZeptoMail API v1.1 payload', () => { + it('converts single recipient payload', () => { + const payload = converter.zeptomail({ api: '1.1', payload: examplePayload}); + expect(payload.from.address).toEqual(examplePayload.from); + expect(payload.to).toBeInstanceOf(Array); + expect(payload.to.length).toBe(1); + expect(payload.to[0].email_address.address).toEqual(examplePayload.to); + expect(payload.reply_to).toBeInstanceOf(Array); + expect(payload.reply_to.length).toBe(1); + expect(payload.reply_to[0].address).toEqual(examplePayload.replyTo); + expect(payload.subject).toBe(examplePayload.subject); + expect(payload.textbody).toBe(examplePayload.text); + expect(payload.htmlbody).toBe(examplePayload.html); + }); + + it('converts multiple recipients payload', () => { + const examplePayload = { + from: "from@example.com", + to: "to@example.com,toanother@example.com", + replyTo: "replyto@example.com, replytoanother@example.com", + subject: "ExampleSubject", + text: "ExampleText", + html: "ExampleHtml" + } + const payload = converter.zeptomail({ api: '1.1', payload: examplePayload}); + expect(payload.from.address).toEqual(examplePayload.from); + expect(payload.to).toBeInstanceOf(Array); + const exmplePayloadToAddresses = examplePayload.to.split(','); + const toAddresses = payload.to.map(entry => entry.email_address.address); + exmplePayloadToAddresses.forEach((address, index) => { + expect(address).toBe(toAddresses[index]); + }); + expect(payload.reply_to).toBeInstanceOf(Array); + const exmpleReplyToAddresses = examplePayload.replyTo.split(',').map(addr => addr.trim()); + const replyToAddresses = payload.reply_to[0].address.split(',').map(addr => addr.trim()); + exmpleReplyToAddresses.forEach((exampleAddress, index) => { + expect(replyToAddresses[index]).toBe(exampleAddress); + }); + expect(payload.subject).toBe(examplePayload.subject); + expect(payload.textbody).toBe(examplePayload.text); + expect(payload.htmlbody).toBe(examplePayload.html); + }); + + it('throws if unsupported version', () => { + expect(() => converter.zeptomail({ api: 'invalidVersion', payload: examplePayload})).toThrowError(/invalidVersion/); + }); + }); }); describe('invoke _sendMail', function () { diff --git a/src/ApiPayloadConverter.js b/src/ApiPayloadConverter.js index 801738e..85f950f 100644 --- a/src/ApiPayloadConverter.js +++ b/src/ApiPayloadConverter.js @@ -6,13 +6,13 @@ class ApiPayloadConverter { /** * @description Converts the mail payload for the official Mailgun client. - * @param {Object} originalPayload The original payload (provider agnostic). + * @param {Object} data The original payload (provider agnostic). * @returns {Object} The payload according to Mailgun client specification. */ - static mailgun(originalPayload) { + static mailgun(data) { // Clone payload - const payload = Object.assign({}, originalPayload); + const payload = Object.assign({}, data); // Transform reply-to if (payload.replyTo) { @@ -25,13 +25,13 @@ class ApiPayloadConverter { /** * @description Converts the mail payload for the AWS Simple Mail Service (AWS JavaScript SDK v3). - * @param {Object} originalPayload The original payload (provider agnostic). + * @param {Object} data The original payload (provider agnostic). * @returns {Object} The payload according to AWS SDK specification. */ - static awsSes(originalPayload) { + static awsSes(data) { // Clone payload - const payload = Object.assign({}, originalPayload); + const payload = Object.assign({}, data); // Transform sender payload.Source = [payload.from]; @@ -92,6 +92,62 @@ class ApiPayloadConverter { return payload; } + + /** + * Converts the mail payload for the ZeptoMail. + * @param {Object} data The original payload + * @param {String} data.api The provider API version. + * @param {Object} data.payload The payload to convert to be compatible with the provider API. + * @returns {Object} The payload according to ZeptoMail SDK specification. + */ + static zeptomail(data) { + + // Clone payload + const payload = Object.assign({}, data.payload); + switch (data.api) { + case '1.1': { + + // Transform sender + payload.from = { + address: payload.from + } + const emailString = payload.to; + const emailAddresses = emailString.split(',').map(email => email.trim()); + const formattedEmails = emailAddresses.map((address) => ({ + email_address: { + address: address.trim() + } + })); + payload.to = formattedEmails; + if (payload.replyTo) { + payload.reply_to = [{ + address: payload.replyTo + } + ]; + delete payload.replyTo; + } + + // If message has content + if (payload.subject || payload.textbody || payload.htmlbody) { + if (payload.text || payload.html) { + payload.textbody = {}; + if (payload.text) { + payload.textbody = payload.text; + delete payload.text; + } + if (payload.html) { + payload.htmlbody = payload.html; + delete payload.html; + } + } + } + break; + } + default: + throw new Error(`Unsupported ZeptoMail API version '${ data.api }'.`); + } + return payload; + } } module.exports = ApiPayloadConverter;