diff --git a/integration-tests/modules/__tests__/notification/admin/notification.spec.ts b/integration-tests/modules/__tests__/notification/admin/notification.spec.ts index a2f1975f29..a379d3cc2b 100644 --- a/integration-tests/modules/__tests__/notification/admin/notification.spec.ts +++ b/integration-tests/modules/__tests__/notification/admin/notification.spec.ts @@ -38,6 +38,10 @@ medusaIntegrationTestRunner({ trigger_type: "order-created", resource_id: "order-id", resource_type: "order", + content: { + subject: "We received you order", + html: "

Thank you

", + }, } as CreateNotificationDTO const result = await service.createNotifications(notification) @@ -67,6 +71,8 @@ medusaIntegrationTestRunner({ }) ) + expect(result).toHaveProperty("content") + expect(fromDB).toEqual( expect.objectContaining({ to: "test@medusajs.com", @@ -83,6 +89,8 @@ medusaIntegrationTestRunner({ }) ) + expect(fromDB).not.toHaveProperty("content") + expect(logSpy).toHaveBeenCalledWith( `Attempting to send a notification to: 'test@medusajs.com' on the channel: 'email' with template: 'order-created' and data: '{\"username\":\"john-doe\"}'` ) diff --git a/packages/core/types/src/notification/common.ts b/packages/core/types/src/notification/common.ts index 963eb85347..0c975d9d62 100644 --- a/packages/core/types/src/notification/common.ts +++ b/packages/core/types/src/notification/common.ts @@ -176,3 +176,23 @@ export interface FilterableNotificationProps */ created_at?: OperatorMap } + +/** + * @interface + * + * The structure for content passed to the notification provider. + */ +export interface NotificationContent { + /** + * the subject of the notification + */ + subject?: string + /** + * the text content of the notification + */ + text?: string + /** + * the html content of the notification + */ + html?: string +} diff --git a/packages/core/types/src/notification/mutations.ts b/packages/core/types/src/notification/mutations.ts index 7501f941cf..004fdc4f53 100644 --- a/packages/core/types/src/notification/mutations.ts +++ b/packages/core/types/src/notification/mutations.ts @@ -1,3 +1,5 @@ +import { NotificationContent } from "./common" + /** * @interface * @@ -21,6 +23,10 @@ export interface CreateNotificationDTO { * The data that gets passed over to the provider for rendering the notification. */ data?: Record | null + /** + * The content that gets passed over to the provider. + */ + content?: NotificationContent | null /** * The event name, the workflow, or anything else that can help to identify what triggered the notification. */ diff --git a/packages/core/types/src/notification/provider.ts b/packages/core/types/src/notification/provider.ts index 1ba8207c39..ff98475376 100644 --- a/packages/core/types/src/notification/provider.ts +++ b/packages/core/types/src/notification/provider.ts @@ -1,4 +1,4 @@ -import { Attachment } from "./common" +import { Attachment, NotificationContent } from "./common" /** * @interface @@ -30,6 +30,10 @@ export type ProviderSendNotificationDTO = { * The data that gets passed over to the provider for rendering the notification. */ data?: Record | null + /** + * The content that gets passed to the provider. + */ + content?: NotificationContent | null } /** diff --git a/packages/modules/notification/integration-tests/__tests__/notification-module-service/index.spec.ts b/packages/modules/notification/integration-tests/__tests__/notification-module-service/index.spec.ts index d459436a2d..7865e45559 100644 --- a/packages/modules/notification/integration-tests/__tests__/notification-module-service/index.spec.ts +++ b/packages/modules/notification/integration-tests/__tests__/notification-module-service/index.spec.ts @@ -85,6 +85,30 @@ moduleIntegrationTestRunner({ ) }) + it("should send a notification and don't store the content in the database", async () => { + const notification = { + to: "admin@medusa.com", + template: "signup-template", + channel: "email", + data: {}, + content: { + html: "

Welcome to medusa

", + }, + } + + const result = await service.createNotifications(notification) + const dbEntry = await service.retrieveNotification(result.id) + + expect(dbEntry).toEqual( + expect.objectContaining({ + provider_id: "test-provider", + external_id: "external_id", + status: NotificationStatus.SUCCESS, + }) + ) + expect(dbEntry).not.toHaveProperty("content") + }) + it("should emit an event when a notification is created", async () => { const notification = { to: "admin@medusa.com", diff --git a/packages/modules/providers/notification-sendgrid/integration-tests/__tests__/services.spec.ts b/packages/modules/providers/notification-sendgrid/integration-tests/__tests__/services.spec.ts index e312be4447..40ee377b0b 100644 --- a/packages/modules/providers/notification-sendgrid/integration-tests/__tests__/services.spec.ts +++ b/packages/modules/providers/notification-sendgrid/integration-tests/__tests__/services.spec.ts @@ -6,6 +6,7 @@ jest.setTimeout(100000) describe.skip("Sendgrid notification provider", () => { let sendgridService: SendgridNotificationService let emailTemplate = "" + let emailContent = "" let to = "" beforeAll(() => { sendgridService = new SendgridNotificationService( @@ -19,6 +20,7 @@ describe.skip("Sendgrid notification provider", () => { ) emailTemplate = process.env.SENDGRID_TEST_TEMPLATE ?? "" + emailContent = `Welcome to medusa` to = process.env.SENDGRID_TEST_TO ?? "" }) @@ -35,6 +37,41 @@ describe.skip("Sendgrid notification provider", () => { expect(resp).toEqual({}) }) + it("sends an email with the specified email body", async () => { + const resp = await sendgridService.send({ + to, + channel: "email", + template: "signup-template", + content: { + subject: "It's a test", + html: emailContent, + }, + data: { + username: "john-doe", + }, + }) + + expect(resp).toEqual({}) + }) + + it("throws an exception if the subject is not present for html content", async () => { + const error = await sendgridService + .send({ + to, + template: "signup-template", + channel: "email", + content: { html: emailContent }, + data: { + username: "john-doe", + }, + }) + .catch((e) => e) + + expect(error.message).toEqual( + "Failed to send email: 400 - The subject is required. You can get around this requirement if you use a template with a subject defined or if every personalization has a subject defined." + ) + }) + it("throws an exception if the template does not exist", async () => { const error = await sendgridService .send({ diff --git a/packages/modules/providers/notification-sendgrid/src/services/sendgrid.ts b/packages/modules/providers/notification-sendgrid/src/services/sendgrid.ts index 987977c89f..23b80d2994 100644 --- a/packages/modules/providers/notification-sendgrid/src/services/sendgrid.ts +++ b/packages/modules/providers/notification-sendgrid/src/services/sendgrid.ts @@ -13,6 +13,8 @@ type InjectedDependencies = { logger: Logger } +type MailContent = Required> + interface SendgridServiceConfig { apiKey: string from: string @@ -59,14 +61,32 @@ export class SendgridNotificationService extends AbstractNotificationProviderSer const from = notification.from?.trim() || this.config_.from + let mailContent: + | MailContent + | { + templateId: string + } + + if ("content" in notification && !!notification.content) { + mailContent = { + subject: notification.content?.subject, + html: notification.content?.html, + } as MailContent + } else { + // we can't mix html and templates for sendgrid + mailContent = { + templateId: notification.template, + } + } + const message: sendgrid.MailDataRequired = { to: notification.to, from: from, - templateId: notification.template, dynamicTemplateData: notification.data as | { [key: string]: any } | undefined, attachments: attachments, + ...mailContent, } try {