Skip to content

feat: add AWS SES payload adapter #65

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 20 commits into from
Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from 8 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
46 changes: 46 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,51 @@ 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');

// `fromEnv` grabs your credentials from environment variables (I use this method for testing and `fromInstanceMetadata` for production)
const {
fromInstanceMetadata, // gets 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 credential provider depending on environment
let awsCredsProvider = process.env.NODE_ENV === 'production' ? await fromInstanceMetadata() : await fromEnv();

// `awsCredsProvider` returns a promise, once resolved, you get your credentials
const credentials = await awsCredsProvider(); // This assumes top level await.

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 && 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 && 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;