fix(medusa-plugin-sendgrid): Use correct SendGrid client (#4524)
* fix(medusa-plugin-sendgrid): Use correct SendGrid instance * Create loud-fans-own.md * Adds tests * Use logger
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"medusa-plugin-sendgrid": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(medusa-plugin-sendgrid): Use correct SendGrid client
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
import SendGridService from "../sendgrid"
|
||||||
|
import SendGrid from "@sendgrid/mail"
|
||||||
|
|
||||||
|
jest.genMockFromModule("@sendgrid/mail")
|
||||||
|
jest.mock("@sendgrid/mail")
|
||||||
|
|
||||||
|
const mockedSendGrid = SendGrid
|
||||||
|
mockedSendGrid.setApiKey.mockResolvedValue(mockedSendGrid)
|
||||||
|
mockedSendGrid.send.mockResolvedValue(Promise.resolve())
|
||||||
|
|
||||||
|
describe("SendGridService", () => {
|
||||||
|
let sendGridService
|
||||||
|
|
||||||
|
const totalsService = {
|
||||||
|
withTransaction: function () {
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
getCalculationContext: jest.fn().mockImplementation((order, lineItems) => {
|
||||||
|
return Promise.resolve({})
|
||||||
|
}),
|
||||||
|
getLineItemTotals: jest.fn().mockImplementation(() => {
|
||||||
|
return Promise.resolve({})
|
||||||
|
}),
|
||||||
|
getLineItemRefund: () => {},
|
||||||
|
getTotal: (o) => {
|
||||||
|
return o.total || 0
|
||||||
|
},
|
||||||
|
getGiftCardableAmount: (o) => {
|
||||||
|
return o.subtotal || 0
|
||||||
|
},
|
||||||
|
getRefundedTotal: (o) => {
|
||||||
|
return o.refunded_total || 0
|
||||||
|
},
|
||||||
|
getShippingTotal: (o) => {
|
||||||
|
return o.shipping_total || 0
|
||||||
|
},
|
||||||
|
getGiftCardTotal: (o) => {
|
||||||
|
return o.gift_card_total || 0
|
||||||
|
},
|
||||||
|
getDiscountTotal: (o) => {
|
||||||
|
return o.discount_total || 0
|
||||||
|
},
|
||||||
|
getTaxTotal: (o) => {
|
||||||
|
return o.tax_total || 0
|
||||||
|
},
|
||||||
|
getSubtotal: (o) => {
|
||||||
|
return o.subtotal || 0
|
||||||
|
},
|
||||||
|
getPaidTotal: (o) => {
|
||||||
|
return o.paid_total || 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should call SendGrid.send when template is configured and correct data is passed", async () => {
|
||||||
|
const orderServiceMock = {
|
||||||
|
retrieve: jest.fn().mockImplementation((data) => {
|
||||||
|
return Promise.resolve({
|
||||||
|
email: "test@test.com",
|
||||||
|
currency_code: "usd",
|
||||||
|
items: [],
|
||||||
|
discounts: [],
|
||||||
|
gift_cards: [],
|
||||||
|
created_at: new Date(),
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
sendGridService = new SendGridService(
|
||||||
|
{ orderService: orderServiceMock, totalsService },
|
||||||
|
{
|
||||||
|
api_key: "SG.test",
|
||||||
|
order_placed_template: "lol",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await sendGridService.sendNotification("order.placed", { id: "test" })
|
||||||
|
expect(mockedSendGrid.send).toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should failed to send an email when event does not exist", async () => {
|
||||||
|
expect.assertions(1)
|
||||||
|
const orderServiceMock = {
|
||||||
|
retrieve: jest.fn().mockImplementation((data) => {
|
||||||
|
return Promise.resolve({
|
||||||
|
email: "test@test.com",
|
||||||
|
currency_code: "usd",
|
||||||
|
items: [],
|
||||||
|
discounts: [],
|
||||||
|
gift_cards: [],
|
||||||
|
created_at: new Date(),
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
sendGridService = new SendGridService(
|
||||||
|
{ orderService: orderServiceMock, totalsService },
|
||||||
|
{
|
||||||
|
api_key: "SG.test",
|
||||||
|
order_placed_template: "lol",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendGridService.sendNotification("some.non-existing_event", {
|
||||||
|
id: "test",
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual(
|
||||||
|
"Sendgrid service: No template was set for event: some.non-existing_event"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should failed to send an email when template id is not configured", async () => {
|
||||||
|
expect.assertions(1)
|
||||||
|
const orderServiceMock = {
|
||||||
|
retrieve: jest.fn().mockImplementation((data) => {
|
||||||
|
return Promise.resolve({
|
||||||
|
email: "test@test.com",
|
||||||
|
currency_code: "usd",
|
||||||
|
items: [],
|
||||||
|
discounts: [],
|
||||||
|
gift_cards: [],
|
||||||
|
created_at: new Date(),
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
sendGridService = new SendGridService(
|
||||||
|
{ orderService: orderServiceMock, totalsService },
|
||||||
|
{
|
||||||
|
api_key: "SG.test",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendGridService.sendNotification("order.placed", {
|
||||||
|
id: "test",
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
expect(error.message).toEqual(
|
||||||
|
"Sendgrid service: No template was set for event: order.placed"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should use localized template to send an email", async () => {
|
||||||
|
const cartServiceMock = {
|
||||||
|
retrieve: jest.fn().mockImplementation((data) => {
|
||||||
|
return Promise.resolve({
|
||||||
|
context: {
|
||||||
|
locale: "de-DE",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
const orderServiceMock = {
|
||||||
|
retrieve: jest.fn().mockImplementation((data) => {
|
||||||
|
return Promise.resolve({
|
||||||
|
email: "test@test.com",
|
||||||
|
currency_code: "usd",
|
||||||
|
items: [],
|
||||||
|
discounts: [],
|
||||||
|
gift_cards: [],
|
||||||
|
created_at: new Date(),
|
||||||
|
cart_id: "test-id",
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
sendGridService = new SendGridService(
|
||||||
|
{
|
||||||
|
orderService: orderServiceMock,
|
||||||
|
totalsService,
|
||||||
|
cartService: cartServiceMock,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api_key: "SG.test",
|
||||||
|
localization: {
|
||||||
|
"de-DE": {
|
||||||
|
order_placed_template: "lol",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await sendGridService.sendNotification("order.placed", {
|
||||||
|
id: "test",
|
||||||
|
})
|
||||||
|
expect(mockedSendGrid.send).toBeCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -34,6 +34,7 @@ class SendGridService extends NotificationService {
|
|||||||
totalsService,
|
totalsService,
|
||||||
productVariantService,
|
productVariantService,
|
||||||
giftCardService,
|
giftCardService,
|
||||||
|
logger,
|
||||||
},
|
},
|
||||||
options
|
options
|
||||||
) {
|
) {
|
||||||
@@ -53,6 +54,7 @@ class SendGridService extends NotificationService {
|
|||||||
this.totalsService_ = totalsService
|
this.totalsService_ = totalsService
|
||||||
this.productVariantService_ = productVariantService
|
this.productVariantService_ = productVariantService
|
||||||
this.giftCardService_ = giftCardService
|
this.giftCardService_ = giftCardService
|
||||||
|
this.logger_ = logger
|
||||||
|
|
||||||
SendGrid.setApiKey(options.api_key)
|
SendGrid.setApiKey(options.api_key)
|
||||||
}
|
}
|
||||||
@@ -219,16 +221,20 @@ class SendGridService extends NotificationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async sendNotification(event, eventData, attachmentGenerator) {
|
async sendNotification(event, eventData, attachmentGenerator) {
|
||||||
|
const data = await this.fetchData(event, eventData, attachmentGenerator)
|
||||||
|
|
||||||
let templateId = this.getTemplateId(event)
|
let templateId = this.getTemplateId(event)
|
||||||
|
|
||||||
if (!templateId) {
|
if (data.locale) {
|
||||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, `Sendgrid service: No template was set for event: ${event}`)
|
templateId = this.getLocalizedTemplateId(event, data.locale) || templateId
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await this.fetchData(event, eventData, attachmentGenerator)
|
if (!templateId) {
|
||||||
if (!data) {
|
throw new MedusaError(
|
||||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, "Sendgrid service: Invalid event data was received")
|
MedusaError.Types.INVALID_DATA,
|
||||||
}
|
`Sendgrid service: No template was set for event: ${event}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const attachments = await this.fetchAttachments(
|
const attachments = await this.fetchAttachments(
|
||||||
event,
|
event,
|
||||||
@@ -236,10 +242,6 @@ class SendGridService extends NotificationService {
|
|||||||
attachmentGenerator
|
attachmentGenerator
|
||||||
)
|
)
|
||||||
|
|
||||||
if (data.locale) {
|
|
||||||
templateId = this.getLocalizedTemplateId(event, data.locale) || templateId
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendOptions = {
|
const sendOptions = {
|
||||||
template_id: templateId,
|
template_id: templateId,
|
||||||
from: this.options_.from,
|
from: this.options_.from,
|
||||||
@@ -261,10 +263,15 @@ class SendGridService extends NotificationService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let status
|
let status
|
||||||
await this.transporter_.sendMail(sendOptions)
|
await SendGrid.send(sendOptions)
|
||||||
.then(() => { status = "sent" })
|
.then(() => {
|
||||||
.catch((error) => { status = "failed"; console.log(error) })
|
status = "sent"
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
status = "failed"
|
||||||
|
this.logger_.error(error)
|
||||||
|
})
|
||||||
|
|
||||||
// We don't want heavy docs stored in DB
|
// We don't want heavy docs stored in DB
|
||||||
delete sendOptions.attachments
|
delete sendOptions.attachments
|
||||||
@@ -303,18 +310,11 @@ class SendGridService extends NotificationService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends an email using SendGrid.
|
* Sends an email using SendGrid.
|
||||||
* @param {string} templateId - id of template in SendGrid
|
* @param {Object} options - send options containing to, from, template, and more. Read more here: https://github.com/sendgrid/sendgrid-nodejs/tree/main/packages/mail
|
||||||
* @param {string} from - sender of email
|
|
||||||
* @param {string} to - receiver of email
|
|
||||||
* @param {Object} data - data to send in mail (match with template)
|
|
||||||
* @return {Promise} result of the send operation
|
* @return {Promise} result of the send operation
|
||||||
*/
|
*/
|
||||||
async sendEmail(options) {
|
async sendEmail(options) {
|
||||||
try {
|
return await SendGrid.send(options)
|
||||||
return SendGrid.send(options)
|
|
||||||
} catch (error) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async orderShipmentCreatedData({ id, fulfillment_id }, attachmentGenerator) {
|
async orderShipmentCreatedData({ id, fulfillment_id }, attachmentGenerator) {
|
||||||
|
|||||||
Reference in New Issue
Block a user