Skip to content
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

feat: sendgrid support attachments #21

Merged
merged 4 commits into from
Aug 21, 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
2 changes: 1 addition & 1 deletion .github/actions/functional-test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ runs:
shell: bash
run: |
npm run docker npm ci
docker-compose up -d
docker compose up -d
bash tests/wait-kuzzle.sh
npm run test
9 changes: 9 additions & 0 deletions doc/1/controllers/sendgrid/sendEmail/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ Method: POST
"subject": "<email subject>",
"html": "<email body>",
"from": "<sender email>", // optional
"attachments": [ // optional
{
"content": "<base64 encoded attachment content>",
"contentType": "<attachment content type>",
"filename": "<attachment file name>",
"contentDisposition": "attachment" | "inline",
"cid": "<content ID if inline attachment>" // optional
}
]
}
}
```
Expand Down
9 changes: 9 additions & 0 deletions doc/1/controllers/sendgrid/sendTemplatedEmail/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ Method: POST
},
"templateId": "<template ID>",
"from": "<sender email>", // optional
"attachments": [ // optional
{
"content": "<base64 encoded attachment content>",
"contentType": "<attachment content type>",
"filename": "<attachment file name>",
"contentDisposition": "attachment" | "inline",
"cid": "<content ID if inline attachment>" // optional
}
]
}
}
```
Expand Down
3 changes: 3 additions & 0 deletions lib/HermesMessengerPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,12 @@ export class HermesMessengerPlugin extends Plugin {
properties: {
content: { type: "keyword" },
contentType: { type: "keyword" },
type: { type: "keyword" },
filename: { type: "keyword" },
contentDisposition: { type: "keyword" },
disposition: { type: "keyword" },
cid: { type: "keyword" },
content_id: { type: "keyword" },
},
},

Expand Down
31 changes: 26 additions & 5 deletions lib/controllers/SendgridController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import {
PluginContext,
ControllerDefinition,
} from "kuzzle";

import { SendgridClient } from "../messenger-clients";
import { Attachment, SendgridAttachment } from "lib/types";

export class SendgridController {
private context: PluginContext;
private config: JSONObject;

private sendgridClient: SendgridClient;

definition: ControllerDefinition;

get sdk(): EmbeddedSDK {
Expand Down Expand Up @@ -64,7 +62,20 @@ export class SendgridController {

const from = fromEmail.length === 0 ? null : fromEmail;

await this.sendgridClient.sendEmail(account, to, subject, html, { from });
const attachments = request.getBodyArray("attachments", []).map(
(attachment: Attachment): SendgridAttachment => ({
content: attachment.content,
filename: attachment.filename,
type: attachment.contentType,
disposition: attachment.contentDisposition,
content_id: attachment.cid,
})
);

await this.sendgridClient.sendEmail(account, to, subject, html, {
from,
attachments,
});
}

async sendTemplatedEmail(request: KuzzleRequest) {
Expand All @@ -76,12 +87,22 @@ export class SendgridController {

const from = fromEmail.length === 0 ? null : fromEmail;

const attachments = request.getBodyArray("attachments", []).map(
(attachment: Attachment): SendgridAttachment => ({
content: attachment.content,
filename: attachment.filename,
type: attachment.contentType,
disposition: attachment.contentDisposition,
content_id: attachment.cid,
})
);

await this.sendgridClient.sendTemplatedEmail(
account,
to,
templateId,
templateData,
{ from }
{ from, attachments }
);
}

Expand Down
32 changes: 24 additions & 8 deletions lib/messenger-clients/SendgridClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { MailService } from "@sendgrid/mail";
import { ExternalServiceError, JSONObject } from "kuzzle";

import { BaseAccount, MessengerClient } from "./MessengerClient";
import { SendgridAttachment } from "lib/types";

export interface SendgridAccount extends BaseAccount<MailService> {
options: {
Expand All @@ -25,24 +26,34 @@ export class SendgridClient extends MessengerClient<SendgridAccount> {
* @param subject Email subject
* @param html Email content
* @param options.from Sender email
* @param options.attachments Attachments to be included in the email
*/
async sendEmail(
accountName: string,
to: string[],
subject: string,
html: string,
{ from }: { from?: string } = {}
{
from,
attachments,
}: { from?: string; attachments?: SendgridAttachment[] } = {}
) {
const account = this.getAccount(accountName);

const fromEmail = from || account.options.defaultSender;

const email = { from: fromEmail, to, subject, html };
const email = {
from: fromEmail,
to,
subject,
html,
attachments,
};

this.context.log.debug(
`EMAIL (${accountName}): FROM ${fromEmail} TO ${to.join(
", "
)} SUBJECT ${subject}`
)} SUBJECT ${subject} ATTACHMENTS ${attachments?.length || 0}`
);

try {
Expand All @@ -59,20 +70,24 @@ export class SendgridClient extends MessengerClient<SendgridAccount> {
}

/**
* Sends a tempated email using one of the registered accounts.
* Sends a templated email using one of the registered accounts.
*
* @param accountName Account name
* @param from Sender email
* @param to Recipient email(s)
* @param templateId Template ID
* @param templateData Template placeholders values
* @param options.from Sender email
* @param options.attachments Attachments to be included in the email
*/
async sendTemplatedEmail(
accountName: string,
to: string[],
templateId: string,
templateData: JSONObject,
{ from }: { from?: string } = {}
{
from,
attachments,
}: { from?: string; attachments?: SendgridAttachment[] } = {}
) {
const account = this.getAccount(accountName);

Expand All @@ -83,12 +98,13 @@ export class SendgridClient extends MessengerClient<SendgridAccount> {
to,
templateId,
dynamic_template_data: templateData,
attachments,
};

this.context.log.debug(
`EMAIL (${accountName}): FROM ${fromEmail} TO ${to.join(
", "
)} TEMPLATE ${templateId}`
)} TEMPLATE ${templateId} ATTACHMENTS ${attachments?.length || 0}`
);

try {
Expand Down Expand Up @@ -127,7 +143,7 @@ export class SendgridClient extends MessengerClient<SendgridAccount> {
};
}

private async sendMessage(account: SendgridAccount, email: any) {
private async sendMessage(account: SendgridAccount, email) {
if (await this.mockedAccount(account.name)) {
await this.sdk.document.createOrReplace(
this.config.adminIndex,
Expand Down
8 changes: 8 additions & 0 deletions lib/types/Attachment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ export interface Attachment {
contentDisposition: "attachment" | "inline";
cid?: string;
}

export interface SendgridAttachment {
content: string;
type: string;
filename: string;
disposition: "attachment" | "inline";
content_id?: string;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"docker": "docker-compose run kuzzle_node_1 ",
"docker": "docker compose run kuzzle_node_1 ",
"dev": "NODE_ENV=development ergol tests/application/app.ts -c ergol.config.json",
"prod": "node ./dist/tests/application/app.js",
"test": "jest --runInBand",
Expand Down
9 changes: 9 additions & 0 deletions tests/scenarios/sendgrid.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@ describe("Sendgrid", () => {
from: "support@kuzzle.io",
to: ["jobs@kuzzle.io"],
templateData: { foo: "bar" },
attachments: [
{
content: "base64filecontent",
filename: "dummyfile.pdf",
contentType: "pdf",
contentDisposition: "inline",
content_id: "dummyCid",
},
],
},
});

Expand Down
2 changes: 1 addition & 1 deletion tests/wait-kuzzle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ do
((tries=tries+1))

if [ $tries -eq $max_tries ]; then
docker-compose logs
docker compose logs
curl http://localhost:7512?pretty
echo "Cannot connect to Kuzzle after $tries tries. Aborting."
exit 1
Expand Down
Loading