Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ This adapter supports any REST API by adapting the API payload in the adapter co
For convenience, support for common APIs is already built into this adapter and available via the `ApiPayloadConverter`. The following is a list of currently supported API providers:

- [Mailgun](https://www.mailgun.com)
- [AWS Simple Email Service (AWS JavaScript SDK v3)](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-ses/index.html)

If the provider you are using is not already supported, please feel free to open a PR.

Expand Down Expand Up @@ -277,6 +278,48 @@ const server = new ParseServer({
});
```

### Example for AWS Simple Email Service

This is an example for the AWS Simple Email Service client using the AWS JavaScript SDK v3:

```js
// Configure mail client
const { SES, SendEmailCommand } = require('@aws-sdk/client-ses');

const {
fromInstanceMetadata, // Get credentials via IMDS from the AWS instance (when deployed on AWS instance)
fromEnv, // Get AWS credentials from environment variables (when testing locally)
} = require('@aws-sdk/credential-providers');

// Get AWS credentials depending on environment
const credentialProvider= process.env.NODE_ENV == 'production' ? fromInstanceMetadata() : fromEnv();
const credentials = await credentialProvider();

const sesClient = new SES({
credentials,
region: 'eu-west-1',
apiVersion: '2010-12-01'
});

// Configure Parse Server
const server = new ParseServer({
...otherServerOptions,

emailAdapter: {
module: 'parse-server-api-mail-adapter',
options: {
... otherAdapterOptions,

apiCallback: async ({ payload, locale }) => {
const awsSesPayload = ApiPayloadConverter.awsSes(payload);
const command = new SendEmailCommand(awsSesPayload);
await sesClient.send(command);
}
}
}
});
```

## 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
12 changes: 11 additions & 1 deletion spec/ApiMailAdapter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,17 @@ describe('ApiMailAdapter', () => {
expect(payload.subject).toBe(examplePayload.subject);
expect(payload.text).toBe(examplePayload.text);
expect(payload.html).toBe(examplePayload.html);
})
});

it('converts payload for AWS SES (SDK v3)', () => {
const payload = converter.awsSes(examplePayload);
expect(payload.Source).toEqual([examplePayload.from]);
expect(payload.Destination.ToAddresses).toEqual([examplePayload.to]);
expect(payload.ReplyToAddresses).toEqual([examplePayload.replyTo]);
expect(payload.Message.Subject.Data).toBe(examplePayload.subject);
expect(payload.Message.Body.Text.Data).toBe(examplePayload.text);
expect(payload.Message.Body.Html.Data).toBe(examplePayload.html);
});
});

describe('invoke _sendMail', function () {
Expand Down
77 changes: 77 additions & 0 deletions src/ApiPayloadConverter.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,83 @@ class ApiPayloadConverter {

return payload;
}

/**
* @description Converts the mail payload for the AWS Simple Mail Service (AWS JavaScript SDK v3).
* @param {Object} originalPayload The original payload (provider agnostic).
* @returns {Object} The payload according to AWS SDK specification.
*/
static awsSes(originalPayload) {

// Clone payload
const payload = Object.assign({}, originalPayload);

// Substitute keys
if (payload.to) {
payload.Destination = {
ToAddresses: [payload.to]
};
delete payload.to;
}

if (payload.from) {
payload.Source = [payload.from];
delete payload.from;
}

if (payload.replyTo) {
payload.ReplyToAddresses = [payload.replyTo];
delete payload.replyTo;
}

if (payload.subject) {
if (!payload.Message) {
payload.Message = {}
}

if (!payload.Message.Subject) {
payload.Message.Subject = {}
}

payload.Message.Subject.Data = payload.subject;
payload.Message.Subject.Charset = 'UTF-8';
delete payload.subject;
}

if (payload.html) {
if (!payload?.Message) {
payload.Message = {}
}

if (!payload.Message.Body) {
payload.Message.Body = {}
}

payload.Message.Body.Html = {
Charset: 'UTF-8',
Data: payload.html
};
delete payload.html;
}

if (payload.text) {
if (!payload?.Message) {
payload.Message = {}
}

if (!payload.Message.Body) {
payload.Message.Body = {}
}

payload.Message.Body.Text = {
Charset: 'UTF-8',
Data: payload.text
};
delete payload.text;
}

return payload;
}
}

module.exports = ApiPayloadConverter;