From 0a82d3fc4e948cd2d49a50b289b38e3578c3b2f4 Mon Sep 17 00:00:00 2001 From: ashishn Date: Sun, 13 Oct 2024 12:41:22 +0530 Subject: [PATCH 01/16] Added support for ZeptoMail. Added tests. --- spec/ApiMailAdapter.spec.js | 59 +++++++++++++++++++++++++++++++++++ src/ApiPayloadConverter.js | 62 +++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/spec/ApiMailAdapter.spec.js b/spec/ApiMailAdapter.spec.js index b10e4eb..e914bbf 100644 --- a/spec/ApiMailAdapter.spec.js +++ b/spec/ApiMailAdapter.spec.js @@ -402,6 +402,65 @@ describe('ApiMailAdapter', () => { expect(payload.Message.Body.Text.Data).toBe(examplePayload.text); expect(payload.Message.Body.Html.Data).toBe(examplePayload.html); }); + + it('converts payload for ZeptoMail for single recepient', () => { + const payload = converter.zeptomail(examplePayload); + expect(payload.from.address).toEqual(examplePayload.from); + // Check if 'to' is an array + expect(payload.to).toBeInstanceOf(Array); + // Check if the array has at least one element + expect(payload.to.length).toBe(1); + expect(payload.to[0].email_address.address).toEqual(examplePayload.to); + + // Check if 'to' is an array + expect(payload.reply_to).toBeInstanceOf(Array); + + // Check if the array has at least one element + 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 payload for ZeptoMail for multiple recepients', () => { + + const multipleRecepientExamplePayload = { + 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(multipleRecepientExamplePayload); + + expect(payload.from.address).toEqual(examplePayload.from); + + // Check if 'to' is an array + expect(payload.to).toBeInstanceOf(Array); + //test multiple to addresses + const toAddresses = payload.to.map(entry => entry.email_address.address); + payload.to.forEach((entry, index) => { + + expect(entry.email_address.address).toBe(toAddresses[index]); + + }); + + // Check if 'reply_to' is an array + expect(payload.reply_to).toBeInstanceOf(Array); + //test multiple to addresses + const replyToAddresses = payload.reply_to[0].address.split(',').map(addr => addr.trim()); + const [firstAddress, secondAddress] = replyToAddresses; + expect(replyToAddresses).toContain(firstAddress); + expect(replyToAddresses).toContain(secondAddress); + + expect(payload.subject).toBe(multipleRecepientExamplePayload.subject); + expect(payload.textbody).toBe(multipleRecepientExamplePayload.text); + expect(payload.htmlbody).toBe(multipleRecepientExamplePayload.html); + }); + }); describe('invoke _sendMail', function () { diff --git a/src/ApiPayloadConverter.js b/src/ApiPayloadConverter.js index 801738e..27ccd39 100644 --- a/src/ApiPayloadConverter.js +++ b/src/ApiPayloadConverter.js @@ -92,6 +92,68 @@ class ApiPayloadConverter { return payload; } + + /** + * @description Converts the mail payload for the ZeptoMail. + * @param {Object} originalPayload The original payload (provider agnostic). + * @returns {Object} The payload according to ZeptoMail SDK specification. + */ + static zeptomail(originalPayload) { + + // Clone payload + const payload = Object.assign({}, originalPayload); + + // Transform sender + payload.from = { + address: payload.from + } + + // Extract the string of email addresses, need to be comma separated if multiple + 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 + + // Transform reply-to + if (payload.replyTo) { + payload.reply_to = [{ + address: payload.replyTo + } + ]; + delete payload.replyTo; + } + + // If message has content + if (payload.subject || payload.textbody || payload.htmlbody) { + + // If message has body + + if (payload.text || payload.html) { + + // Set default body + payload.textbody = {}; + + // Transform plain-text + if (payload.text) { + payload.textbody = payload.text, + delete payload.text; + } + + // Transform HTML + if (payload.html) { + payload.htmlbody = payload.html + delete payload.html; + } + } + } + + return payload; + } + } module.exports = ApiPayloadConverter; From 892789cb7edf99c81a3635bcf06185d8d274df8d Mon Sep 17 00:00:00 2001 From: ashishn Date: Sun, 13 Oct 2024 13:45:43 +0530 Subject: [PATCH 02/16] Added example to README. --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 7358666..4766f6a 100644 --- a/README.md +++ b/README.md @@ -323,6 +323,39 @@ 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; +let 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(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. From f35372e869abf9cf0472ac412fd93b323b3e4c60 Mon Sep 17 00:00:00 2001 From: ashishn Date: Sun, 13 Oct 2024 19:37:22 +0530 Subject: [PATCH 03/16] Added API version support. --- spec/ApiMailAdapter.spec.js | 14 ++++++++------ src/ApiPayloadConverter.js | 15 ++++++++++----- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/spec/ApiMailAdapter.spec.js b/spec/ApiMailAdapter.spec.js index e914bbf..457d220 100644 --- a/spec/ApiMailAdapter.spec.js +++ b/spec/ApiMailAdapter.spec.js @@ -403,8 +403,10 @@ describe('ApiMailAdapter', () => { expect(payload.Message.Body.Html.Data).toBe(examplePayload.html); }); - it('converts payload for ZeptoMail for single recepient', () => { - const payload = converter.zeptomail(examplePayload); + fit('converts payload for ZeptoMail for single recepient', () => { + //test works for ZeptoMail v1.1 which is current version + + const payload = converter.zeptomail({api: '1.1',payload: examplePayload}); expect(payload.from.address).toEqual(examplePayload.from); // Check if 'to' is an array expect(payload.to).toBeInstanceOf(Array); @@ -423,8 +425,8 @@ describe('ApiMailAdapter', () => { expect(payload.htmlbody).toBe(examplePayload.html); }); - it('converts payload for ZeptoMail for multiple recepients', () => { - + fit('converts payload for ZeptoMail for multiple recepients', () => { + //test works for ZeptoMail v1.1 which is current version const multipleRecepientExamplePayload = { from: "from@example.com", to: "to@example.com,toanother@example.com", @@ -434,9 +436,9 @@ describe('ApiMailAdapter', () => { html: "ExampleHtml" } - const payload = converter.zeptomail(multipleRecepientExamplePayload); + const payload = converter.zeptomail({api: '1.1',payload: multipleRecepientExamplePayload}); - expect(payload.from.address).toEqual(examplePayload.from); + expect(payload.from.address).toEqual(multipleRecepientExamplePayload.from); // Check if 'to' is an array expect(payload.to).toBeInstanceOf(Array); diff --git a/src/ApiPayloadConverter.js b/src/ApiPayloadConverter.js index 27ccd39..6fa4036 100644 --- a/src/ApiPayloadConverter.js +++ b/src/ApiPayloadConverter.js @@ -95,19 +95,20 @@ class ApiPayloadConverter { /** * @description Converts the mail payload for the ZeptoMail. - * @param {Object} originalPayload The original payload (provider agnostic). + * @param {Object} originalPayload The original payload (provider agnostic). originalPayload has two components 1. api and 2. payload. * @returns {Object} The payload according to ZeptoMail SDK specification. */ static zeptomail(originalPayload) { // Clone payload - const payload = Object.assign({}, originalPayload); + const payload = Object.assign({}, originalPayload.payload); - // Transform sender - payload.from = { + if (originalPayload.api === '1.1') { + + // Transform sender + payload.from = { address: payload.from } - // Extract the string of email addresses, need to be comma separated if multiple const emailString = payload.to; const emailAddresses = emailString.split(',').map(email => email.trim()); @@ -152,6 +153,10 @@ class ApiPayloadConverter { } return payload; + } else { + + throw new Error('Unsupported ZeptoMail API version'); + } } } From c25871ed3a5ccd31b50a91cde27cae18adb67f5e Mon Sep 17 00:00:00 2001 From: ashishn Date: Sun, 13 Oct 2024 19:40:11 +0530 Subject: [PATCH 04/16] Corrected test. --- spec/ApiMailAdapter.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/ApiMailAdapter.spec.js b/spec/ApiMailAdapter.spec.js index 457d220..31dfcda 100644 --- a/spec/ApiMailAdapter.spec.js +++ b/spec/ApiMailAdapter.spec.js @@ -403,9 +403,9 @@ describe('ApiMailAdapter', () => { expect(payload.Message.Body.Html.Data).toBe(examplePayload.html); }); - fit('converts payload for ZeptoMail for single recepient', () => { + it('converts payload for ZeptoMail for single recepient', () => { //test works for ZeptoMail v1.1 which is current version - + const payload = converter.zeptomail({api: '1.1',payload: examplePayload}); expect(payload.from.address).toEqual(examplePayload.from); // Check if 'to' is an array @@ -425,7 +425,7 @@ describe('ApiMailAdapter', () => { expect(payload.htmlbody).toBe(examplePayload.html); }); - fit('converts payload for ZeptoMail for multiple recepients', () => { + it('converts payload for ZeptoMail for multiple recepients', () => { //test works for ZeptoMail v1.1 which is current version const multipleRecepientExamplePayload = { from: "from@example.com", From b9ef56294018d820b8ba7a6ac4917bb69ed4a9da Mon Sep 17 00:00:00 2001 From: ashishn Date: Sun, 13 Oct 2024 20:13:29 +0530 Subject: [PATCH 05/16] Updated README and temporarily removing "npm test" from package.json as install on local Mac fails. --- README.md | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4766f6a..180c7e9 100644 --- a/README.md +++ b/README.md @@ -346,8 +346,8 @@ const server = new ParseServer({ options: { ... otherAdapterOptions, - apiCallback: async ({ payload, locale }) => { - const zeptoMailPayload = ApiPayloadConverter.zeptomail(payload); + apiCallback: async ({ originalPayload, locale }) => { + const zeptoMailPayload = ApiPayloadConverter.zeptomail({api: '1.1', payload: originalPayload}); await zeptoMaiClient.sendMail(zeptoMailPayload); }, } diff --git a/package.json b/package.json index 51c5604..62c7db2 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "madge:circular": "npm run madge --arg=--circular", "test": "nyc --reporter=lcov jasmine", "posttest": "nyc report --reporter=json && codecov -f coverage/*.json", - "prepare": "npm run build && npm test", + "prepare": "npm run build", "demo": "node ./demo" } } From b559e88e303059aead3d6f11c103014897154a63 Mon Sep 17 00:00:00 2001 From: ashishn Date: Sun, 13 Oct 2024 20:30:57 +0530 Subject: [PATCH 06/16] Correcting payload parameter --- spec/ApiMailAdapter.spec.js | 4 ++-- src/ApiPayloadConverter.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/ApiMailAdapter.spec.js b/spec/ApiMailAdapter.spec.js index 31dfcda..ca539f9 100644 --- a/spec/ApiMailAdapter.spec.js +++ b/spec/ApiMailAdapter.spec.js @@ -406,7 +406,7 @@ describe('ApiMailAdapter', () => { it('converts payload for ZeptoMail for single recepient', () => { //test works for ZeptoMail v1.1 which is current version - const payload = converter.zeptomail({api: '1.1',payload: examplePayload}); + const payload = converter.zeptomail({api: '1.1',originalPayload: examplePayload}); expect(payload.from.address).toEqual(examplePayload.from); // Check if 'to' is an array expect(payload.to).toBeInstanceOf(Array); @@ -436,7 +436,7 @@ describe('ApiMailAdapter', () => { html: "ExampleHtml" } - const payload = converter.zeptomail({api: '1.1',payload: multipleRecepientExamplePayload}); + const payload = converter.zeptomail({api: '1.1',originalPayload: multipleRecepientExamplePayload}); expect(payload.from.address).toEqual(multipleRecepientExamplePayload.from); diff --git a/src/ApiPayloadConverter.js b/src/ApiPayloadConverter.js index 6fa4036..515f072 100644 --- a/src/ApiPayloadConverter.js +++ b/src/ApiPayloadConverter.js @@ -101,7 +101,7 @@ class ApiPayloadConverter { static zeptomail(originalPayload) { // Clone payload - const payload = Object.assign({}, originalPayload.payload); + const payload = Object.assign({}, originalPayload.originalPayload); if (originalPayload.api === '1.1') { From 590904a62d14988b5b0c6b8203d49f5ca3f695a4 Mon Sep 17 00:00:00 2001 From: ashishn Date: Mon, 14 Oct 2024 00:34:02 +0530 Subject: [PATCH 07/16] Updated README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 180c7e9..2a582cf 100644 --- a/README.md +++ b/README.md @@ -346,8 +346,8 @@ const server = new ParseServer({ options: { ... otherAdapterOptions, - apiCallback: async ({ originalPayload, locale }) => { - const zeptoMailPayload = ApiPayloadConverter.zeptomail({api: '1.1', payload: originalPayload}); + apiCallback: async ({ payload, locale }) => { + const zeptoMailPayload = ApiPayloadConverter.zeptomail({api: '1.1', originalPayload: payload}); await zeptoMaiClient.sendMail(zeptoMailPayload); }, } From 576f059866cc4936d1992a7afeb8f21af5a5533c Mon Sep 17 00:00:00 2001 From: ashishn Date: Tue, 15 Oct 2024 22:54:00 +0530 Subject: [PATCH 08/16] Resolving review comments - handling of v1.1 - tests for v1.1 - code cleanup --- package.json | 2 +- spec/ApiMailAdapter.spec.js | 96 +++++++++++++++---------------------- src/ApiPayloadConverter.js | 96 ++++++++++++++++--------------------- 3 files changed, 81 insertions(+), 113 deletions(-) diff --git a/package.json b/package.json index 62c7db2..b6d8078 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "madge:circular": "npm run madge --arg=--circular", "test": "nyc --reporter=lcov jasmine", "posttest": "nyc report --reporter=json && codecov -f coverage/*.json", - "prepare": "npm run build", + "prepare": "npm run build && nopm test", "demo": "node ./demo" } } diff --git a/spec/ApiMailAdapter.spec.js b/spec/ApiMailAdapter.spec.js index ca539f9..f56160d 100644 --- a/spec/ApiMailAdapter.spec.js +++ b/spec/ApiMailAdapter.spec.js @@ -403,65 +403,47 @@ describe('ApiMailAdapter', () => { expect(payload.Message.Body.Html.Data).toBe(examplePayload.html); }); - it('converts payload for ZeptoMail for single recepient', () => { - //test works for ZeptoMail v1.1 which is current version - - const payload = converter.zeptomail({api: '1.1',originalPayload: examplePayload}); - expect(payload.from.address).toEqual(examplePayload.from); - // Check if 'to' is an array - expect(payload.to).toBeInstanceOf(Array); - // Check if the array has at least one element - expect(payload.to.length).toBe(1); - expect(payload.to[0].email_address.address).toEqual(examplePayload.to); - - // Check if 'to' is an array - expect(payload.reply_to).toBeInstanceOf(Array); - - // Check if the array has at least one element - 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 payload for ZeptoMail for multiple recepients', () => { - //test works for ZeptoMail v1.1 which is current version - const multipleRecepientExamplePayload = { - 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',originalPayload: multipleRecepientExamplePayload}); - - expect(payload.from.address).toEqual(multipleRecepientExamplePayload.from); - - // Check if 'to' is an array - expect(payload.to).toBeInstanceOf(Array); - //test multiple to addresses - const toAddresses = payload.to.map(entry => entry.email_address.address); - payload.to.forEach((entry, index) => { - - expect(entry.email_address.address).toBe(toAddresses[index]); - + describe('convert ZeptoMail API v1.1 payload', () => { + it('converts payload for ZeptoMail for single recepient', () => { + 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); }); - // Check if 'reply_to' is an array - expect(payload.reply_to).toBeInstanceOf(Array); - //test multiple to addresses - const replyToAddresses = payload.reply_to[0].address.split(',').map(addr => addr.trim()); - const [firstAddress, secondAddress] = replyToAddresses; - expect(replyToAddresses).toContain(firstAddress); - expect(replyToAddresses).toContain(secondAddress); - - expect(payload.subject).toBe(multipleRecepientExamplePayload.subject); - expect(payload.textbody).toBe(multipleRecepientExamplePayload.text); - expect(payload.htmlbody).toBe(multipleRecepientExamplePayload.html); - }); + it('converts payload for ZeptoMail for multiple recepients', () => { + const multipleRecepientExamplePayload = { + 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: multipleRecepientExamplePayload}); + expect(payload.from.address).toEqual(multipleRecepientExamplePayload.from); + expect(payload.to).toBeInstanceOf(Array); + const toAddresses = payload.to.map(entry => entry.email_address.address); + payload.to.forEach((entry, index) => { + expect(entry.email_address.address).toBe(toAddresses[index]); + }); + expect(payload.reply_to).toBeInstanceOf(Array); + const replyToAddresses = payload.reply_to[0].address.split(',').map(addr => addr.trim()); + const [firstAddress, secondAddress] = replyToAddresses; + expect(replyToAddresses).toContain(firstAddress); + expect(replyToAddresses).toContain(secondAddress); + expect(payload.subject).toBe(multipleRecepientExamplePayload.subject); + expect(payload.textbody).toBe(multipleRecepientExamplePayload.text); + expect(payload.htmlbody).toBe(multipleRecepientExamplePayload.html); + }); + }); }); diff --git a/src/ApiPayloadConverter.js b/src/ApiPayloadConverter.js index 515f072..bcdb8e6 100644 --- a/src/ApiPayloadConverter.js +++ b/src/ApiPayloadConverter.js @@ -101,64 +101,50 @@ class ApiPayloadConverter { static zeptomail(originalPayload) { // Clone payload - const payload = Object.assign({}, originalPayload.originalPayload); - - if (originalPayload.api === '1.1') { - - // Transform sender - payload.from = { - address: payload.from - } - // Extract the string of email addresses, need to be comma separated if multiple - 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 - - // Transform reply-to - if (payload.replyTo) { - payload.reply_to = [{ - address: payload.replyTo - } - ]; - delete payload.replyTo; - } - - // If message has content - if (payload.subject || payload.textbody || payload.htmlbody) { - - // If message has body - - if (payload.text || payload.html) { - - // Set default body - payload.textbody = {}; - - // Transform plain-text - if (payload.text) { - payload.textbody = payload.text, - delete payload.text; - } - - // Transform HTML - if (payload.html) { - payload.htmlbody = payload.html - delete payload.html; - } - } + const payload = Object.assign({}, originalPayload.payload); + switch (originalPayload.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'); } - return payload; - } else { - - throw new Error('Unsupported ZeptoMail API version'); - } } - } module.exports = ApiPayloadConverter; From 230dc2ee47cd9e0d25d043d1bf967350a56480c8 Mon Sep 17 00:00:00 2001 From: ashishn Date: Wed, 16 Oct 2024 23:40:31 +0530 Subject: [PATCH 09/16] Resolved review comments --- README.md | 4 +-- package.json | 2 +- spec/ApiMailAdapter.spec.js | 42 ++++++++++++----------- src/ApiPayloadConverter.js | 67 +++++++++++++++++++------------------ 4 files changed, 59 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 2a582cf..ceb2417 100644 --- a/README.md +++ b/README.md @@ -347,9 +347,9 @@ const server = new ParseServer({ ... otherAdapterOptions, apiCallback: async ({ payload, locale }) => { - const zeptoMailPayload = ApiPayloadConverter.zeptomail({api: '1.1', originalPayload: payload}); + const zeptoMailPayload = ApiPayloadConverter.zeptomail({ api: '1.1', originalPayload: payload}); await zeptoMaiClient.sendMail(zeptoMailPayload); - }, + }, } } }); diff --git a/package.json b/package.json index b6d8078..51c5604 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "madge:circular": "npm run madge --arg=--circular", "test": "nyc --reporter=lcov jasmine", "posttest": "nyc report --reporter=json && codecov -f coverage/*.json", - "prepare": "npm run build && nopm test", + "prepare": "npm run build && npm test", "demo": "node ./demo" } } diff --git a/spec/ApiMailAdapter.spec.js b/spec/ApiMailAdapter.spec.js index f56160d..5bd3c3b 100644 --- a/spec/ApiMailAdapter.spec.js +++ b/spec/ApiMailAdapter.spec.js @@ -403,23 +403,24 @@ describe('ApiMailAdapter', () => { expect(payload.Message.Body.Html.Data).toBe(examplePayload.html); }); - describe('convert ZeptoMail API v1.1 payload', () => { - it('converts payload for ZeptoMail for single recepient', () => { - const payload = converter.zeptomail({api: '1.1',payload: examplePayload}); + describe('convert ZeptoMail API v1.1 payload', () => { + + it('converts single recepient payload for ZeptoMail', () => { + const payload = converter.zeptomail({ api: '1.1', payload: examplePayload}); expect(payload.from.address).toEqual(examplePayload.from); - expect(payload.to).toBeInstanceOf(Array); + 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.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 payload for ZeptoMail for multiple recepients', () => { - const multipleRecepientExamplePayload = { + it('converts multiple recepients payload for ZeptoMail', () => { + const examplePayload = { from: "from@example.com", to: "to@example.com,toanother@example.com", replyTo: "replyto@example.com, replytoanother@example.com", @@ -427,24 +428,25 @@ describe('ApiMailAdapter', () => { text: "ExampleText", html: "ExampleHtml" } - const payload = converter.zeptomail({api: '1.1',payload: multipleRecepientExamplePayload}); - expect(payload.from.address).toEqual(multipleRecepientExamplePayload.from); - expect(payload.to).toBeInstanceOf(Array); + 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); - payload.to.forEach((entry, index) => { - expect(entry.email_address.address).toBe(toAddresses[index]); + 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()); - const [firstAddress, secondAddress] = replyToAddresses; - expect(replyToAddresses).toContain(firstAddress); - expect(replyToAddresses).toContain(secondAddress); - expect(payload.subject).toBe(multipleRecepientExamplePayload.subject); - expect(payload.textbody).toBe(multipleRecepientExamplePayload.text); - expect(payload.htmlbody).toBe(multipleRecepientExamplePayload.html); + 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); }); - }); - + }); }); describe('invoke _sendMail', function () { diff --git a/src/ApiPayloadConverter.js b/src/ApiPayloadConverter.js index bcdb8e6..5e74c1a 100644 --- a/src/ApiPayloadConverter.js +++ b/src/ApiPayloadConverter.js @@ -95,7 +95,7 @@ class ApiPayloadConverter { /** * @description Converts the mail payload for the ZeptoMail. - * @param {Object} originalPayload The original payload (provider agnostic). originalPayload has two components 1. api and 2. payload. + * @param {Object} originalPayload The original payload (provider agnostic). originalPayload has two components 1. api and 2. payload. * @returns {Object} The payload according to ZeptoMail SDK specification. */ static zeptomail(originalPayload) { @@ -103,45 +103,46 @@ class ApiPayloadConverter { // Clone payload const payload = Object.assign({}, originalPayload.payload); switch (originalPayload.api) { - case '1.1': + case '1.1': { - // Transform sender - payload.from = { - address: payload.from + // 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() } - 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; + })); + 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; - } + // 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; + } + break; + } default: - throw new Error('Unsupported ZeptoMail API version'); + throw new Error(`Unsupported ZeptoMail API version '${originalPayload.api}'.`); } return payload; } From a2298ec80f0c3bcc53e48e5926968056429bf82f Mon Sep 17 00:00:00 2001 From: ashishn Date: Wed, 16 Oct 2024 23:44:22 +0530 Subject: [PATCH 10/16] Removed code formatting error --- src/ApiPayloadConverter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ApiPayloadConverter.js b/src/ApiPayloadConverter.js index 5e74c1a..02f5c71 100644 --- a/src/ApiPayloadConverter.js +++ b/src/ApiPayloadConverter.js @@ -101,7 +101,7 @@ class ApiPayloadConverter { static zeptomail(originalPayload) { // Clone payload - const payload = Object.assign({}, originalPayload.payload); + const payload = Object.assign({}, originalPayload.payload); switch (originalPayload.api) { case '1.1': { From 8474ed7bfa9ff69f518794ca22a6d4c7f3929c74 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Wed, 16 Oct 2024 20:33:50 +0200 Subject: [PATCH 11/16] jsdoc --- src/ApiPayloadConverter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ApiPayloadConverter.js b/src/ApiPayloadConverter.js index 02f5c71..e5b228d 100644 --- a/src/ApiPayloadConverter.js +++ b/src/ApiPayloadConverter.js @@ -95,7 +95,9 @@ class ApiPayloadConverter { /** * @description Converts the mail payload for the ZeptoMail. - * @param {Object} originalPayload The original payload (provider agnostic). originalPayload has two components 1. api and 2. payload. + * @param {Object} originalPayload The original payload + * @param {String} originalPayload.api The provider API version. + * @param {String} originalPayload.payload The payload to convert to be compatible with the provider API. * @returns {Object} The payload according to ZeptoMail SDK specification. */ static zeptomail(originalPayload) { From a5103079cb5e1794556d38bd37cc1786b5c390e9 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Wed, 16 Oct 2024 20:34:36 +0200 Subject: [PATCH 12/16] jsdoc --- src/ApiPayloadConverter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ApiPayloadConverter.js b/src/ApiPayloadConverter.js index e5b228d..7984c90 100644 --- a/src/ApiPayloadConverter.js +++ b/src/ApiPayloadConverter.js @@ -94,10 +94,10 @@ class ApiPayloadConverter { } /** - * @description Converts the mail payload for the ZeptoMail. + * Converts the mail payload for the ZeptoMail. * @param {Object} originalPayload The original payload * @param {String} originalPayload.api The provider API version. - * @param {String} originalPayload.payload The payload to convert to be compatible with the provider API. + * @param {Object} originalPayload.payload The payload to convert to be compatible with the provider API. * @returns {Object} The payload according to ZeptoMail SDK specification. */ static zeptomail(originalPayload) { From dfaad60bc7d2b5b0c09f376787445c1ffe7e3118 Mon Sep 17 00:00:00 2001 From: ashishn Date: Fri, 18 Oct 2024 23:53:49 +0530 Subject: [PATCH 13/16] Resolved review comments --- README.md | 4 ++-- src/ApiPayloadConverter.js | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ceb2417..f6688f7 100644 --- a/README.md +++ b/README.md @@ -334,7 +334,7 @@ var { SendMailClient } = require('zeptomail'); const url = process.env.ZEPTOMAIL_URL; const token = process.env.ZEPTOMAIL_TOKEN; -let zeptoMaiClient = new SendMailClient({url, token}); +const zeptoMaiClient = new SendMailClient({ url, token }); // Configure Parse Server @@ -347,7 +347,7 @@ const server = new ParseServer({ ... otherAdapterOptions, apiCallback: async ({ payload, locale }) => { - const zeptoMailPayload = ApiPayloadConverter.zeptomail({ api: '1.1', originalPayload: payload}); + const zeptoMailPayload = ApiPayloadConverter.zeptomail({ api: '1.1', payload }); await zeptoMaiClient.sendMail(zeptoMailPayload); }, } diff --git a/src/ApiPayloadConverter.js b/src/ApiPayloadConverter.js index 7984c90..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]; @@ -95,16 +95,16 @@ class ApiPayloadConverter { /** * Converts the mail payload for the ZeptoMail. - * @param {Object} originalPayload The original payload - * @param {String} originalPayload.api The provider API version. - * @param {Object} originalPayload.payload The payload to convert to be compatible with the provider API. + * @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(originalPayload) { + static zeptomail(data) { // Clone payload - const payload = Object.assign({}, originalPayload.payload); - switch (originalPayload.api) { + const payload = Object.assign({}, data.payload); + switch (data.api) { case '1.1': { // Transform sender @@ -144,7 +144,7 @@ class ApiPayloadConverter { break; } default: - throw new Error(`Unsupported ZeptoMail API version '${originalPayload.api}'.`); + throw new Error(`Unsupported ZeptoMail API version '${ data.api }'.`); } return payload; } From 82aa4f88816b0ae34d3d12501376fbe4b76701f6 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Sat, 19 Oct 2024 01:04:54 +0200 Subject: [PATCH 14/16] clean-up --- README.md | 1 - spec/ApiMailAdapter.spec.js | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f6688f7..e3a02ae 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,6 @@ const server = new ParseServer({ }); ``` - ## 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 5bd3c3b..3c95769 100644 --- a/spec/ApiMailAdapter.spec.js +++ b/spec/ApiMailAdapter.spec.js @@ -404,8 +404,7 @@ describe('ApiMailAdapter', () => { }); describe('convert ZeptoMail API v1.1 payload', () => { - - it('converts single recepient payload for ZeptoMail', () => { + 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); @@ -419,7 +418,7 @@ describe('ApiMailAdapter', () => { expect(payload.htmlbody).toBe(examplePayload.html); }); - it('converts multiple recepients payload for ZeptoMail', () => { + it('converts multiple recipients payload', () => { const examplePayload = { from: "from@example.com", to: "to@example.com,toanother@example.com", From 54358f3741832222a1103c5c48af38637f9cae3b Mon Sep 17 00:00:00 2001 From: ashishn Date: Sat, 19 Oct 2024 12:48:29 +0530 Subject: [PATCH 15/16] Added test for ZeptoMail API version check --- spec/ApiMailAdapter.spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/ApiMailAdapter.spec.js b/spec/ApiMailAdapter.spec.js index 3c95769..25f3de2 100644 --- a/spec/ApiMailAdapter.spec.js +++ b/spec/ApiMailAdapter.spec.js @@ -445,6 +445,11 @@ describe('ApiMailAdapter', () => { expect(payload.textbody).toBe(examplePayload.text); expect(payload.htmlbody).toBe(examplePayload.html); }); + + it('throws if unsupported version', () => { + expect(() => converter.zeptomail({ api: '1.2', payload: examplePayload})).toThrow(); + expect(() => converter.zeptomail({ api: '1.1', payload: examplePayload})).not.toThrow(); + }); }); }); From ce014f850f3fd409629a7914ca4cf7f375b63668 Mon Sep 17 00:00:00 2001 From: Ashish Naik Date: Sat, 19 Oct 2024 18:51:18 +0530 Subject: [PATCH 16/16] correct version chrck test Co-authored-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- spec/ApiMailAdapter.spec.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/ApiMailAdapter.spec.js b/spec/ApiMailAdapter.spec.js index 25f3de2..5bd827a 100644 --- a/spec/ApiMailAdapter.spec.js +++ b/spec/ApiMailAdapter.spec.js @@ -447,8 +447,7 @@ describe('ApiMailAdapter', () => { }); it('throws if unsupported version', () => { - expect(() => converter.zeptomail({ api: '1.2', payload: examplePayload})).toThrow(); - expect(() => converter.zeptomail({ api: '1.1', payload: examplePayload})).not.toThrow(); + expect(() => converter.zeptomail({ api: 'invalidVersion', payload: examplePayload})).toThrowError(/invalidVersion/); }); }); });