Skip to content

feat: Add ZeptoMail support #98

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
48 changes: 48 additions & 0 deletions spec/ApiMailAdapter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: "[email protected]",
to: "[email protected],[email protected]",
replyTo: "[email protected], [email protected]",
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 () {
Expand Down
68 changes: 62 additions & 6 deletions src/ApiPayloadConverter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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];
Expand Down Expand Up @@ -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;
Loading