This PR includes documentation that preps for v2 docs (but doesn't introduce new docs). _Note: The number of file changes in the PR is due to find-and-replace within the `references` which is unavoidable. Let me know if I should move it to another PR._ ## Changes - Change Medusa version in base OAS used for v2. - Fix to docblock generator related to not catching all path parameters. - Added typedoc plugin that generates ER Diagrams, which will be used specifically for data model references in commerce modules. - Changed OAS tool to output references in `www/apps/api-reference/specs-v2` directory when the `--v2` option is used. - Added a version switcher to the API reference to switch between V1 and V2. This switcher is enabled by an environment variable, so it won't be visible/usable at the moment. - Upgraded docusaurus to v3.0.1 - Added new Vale rules to ensure correct spelling of Medusa Admin and module names. - Added new components to the `docs-ui` package that will be used in future documentation changes.
289 lines
14 KiB
Plaintext
289 lines
14 KiB
Plaintext
---
|
||
displayed_sidebar: core
|
||
slug: /development/notification/create-notification-provider
|
||
---
|
||
|
||
import TypeList from "@site/src/components/TypeList"
|
||
|
||
# How to Create a Notification Provider
|
||
|
||
In this document, you’ll learn how to create a notification provider in the Medusa backend and the methods you must implement in it. Learn more about the notification architecture in [this documentation](https://docs.medusajs.com/development/notification/overview)
|
||
|
||
## Overview
|
||
|
||
:::note[Prerequisites]
|
||
|
||
Before creating a Notification Provider, [install an event bus module](https://docs.medusajs.com/development/events/modules/redis).
|
||
|
||
:::
|
||
|
||
A Notification Provider is a provider that handles sending and resending of notifications.
|
||
|
||
To create a Notification Provider, create a TypeScript or JavaScript file in `src/services`. The name of the file is the name of the provider
|
||
(for example, `sendgrid.ts`). The file must export a class that extends the `AbstractNotificationService` class imported from `@medusajs/medusa`.
|
||
|
||
For example, create the file `src/services/email-sender.ts` with the following content:
|
||
|
||
```ts title="src/services/email-sender.ts"
|
||
import { AbstractNotificationService } from "@medusajs/medusa"
|
||
import { EntityManager } from "typeorm"
|
||
|
||
class EmailSenderService extends AbstractNotificationService {
|
||
protected manager_: EntityManager
|
||
protected transactionManager_: EntityManager
|
||
|
||
sendNotification(
|
||
event: string,
|
||
data: unknown,
|
||
attachmentGenerator: unknown
|
||
): Promise<{
|
||
to: string;
|
||
status: string;
|
||
data: Record<string, unknown>;
|
||
}> {
|
||
throw new Error("Method not implemented.")
|
||
}
|
||
resendNotification(
|
||
notification: unknown,
|
||
config: unknown,
|
||
attachmentGenerator: unknown
|
||
): Promise<{
|
||
to: string;
|
||
status: string;
|
||
data: Record<string, unknown>;
|
||
}> {
|
||
throw new Error("Method not implemented.")
|
||
}
|
||
|
||
}
|
||
|
||
export default EmailSenderService
|
||
```
|
||
|
||
---
|
||
|
||
## Identifier Property
|
||
|
||
The `NotificationProvider` entity has 2 properties: `identifier` and `is_installed`. The value of the `identifier` property in the notification provider
|
||
class is used when the Notification Provider is created in the database.
|
||
|
||
The value of this property is also used later when you want to subscribe the Notification Provider to events in a [Loader](https://docs.medusajs.com/development/loaders/overview).
|
||
|
||
For example:
|
||
|
||
```ts
|
||
class EmailSenderService extends AbstractNotificationService {
|
||
static identifier = "email-sender"
|
||
// ...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## constructor
|
||
|
||
You can use the `constructor` of your notification provider to access the different services in Medusa through dependency injection.
|
||
|
||
You can also use the constructor to initialize your integration with the third-party provider. For example, if you use a client to connect to the third-party provider’s APIs,
|
||
you can initialize it in the constructor and use it in other methods in the service.
|
||
|
||
Additionally, if you’re creating your notification provider as an external plugin to be installed on any Medusa backend and you want to access the options
|
||
added for the plugin, you can access it in the constructor.
|
||
|
||
### Example
|
||
|
||
```ts
|
||
// ...
|
||
import { AbstractNotificationService, OrderService } from "@medusajs/medusa"
|
||
import { EntityManager } from "typeorm"
|
||
|
||
class EmailSenderService extends AbstractNotificationService {
|
||
// ...
|
||
protected orderService: OrderService
|
||
|
||
constructor(container, options) {
|
||
super(container)
|
||
// you can access options here in case you're
|
||
// using a plugin
|
||
|
||
this.orderService = container.orderService
|
||
|
||
// you can also initialize a client that
|
||
// communicates with a third-party service.
|
||
this.client = new Client(options)
|
||
}
|
||
|
||
// ...
|
||
}
|
||
|
||
export default EmailSenderService
|
||
```
|
||
|
||
### Parameters
|
||
|
||
<TypeList types={[{"name":"container","type":"`Record<string, unknown>`","description":"An instance of `MedusaContainer` that allows you to access other resources, such as services, in your Medusa backend.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"config","type":"`Record<string, unknown>`","description":"If this notification provider is created in a plugin, the plugin's options are passed in this parameter.","optional":true,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="new AbstractNotificationService"/>
|
||
|
||
___
|
||
|
||
## Methods
|
||
|
||
### sendNotification
|
||
|
||
When an event is triggered that your Notification Provider is registered as a handler for, the [`NotificationService`](https://docs.medusajs.com/references/services/classes/services.NotificationService)
|
||
in the Medusa backend executes this method of your Notification Provider.
|
||
|
||
In this method, you can perform the necessary operation to send the Notification. For example, you can send an email to the customer when they place an order.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class EmailSenderService extends AbstractNotificationService {
|
||
// ...
|
||
async sendNotification(
|
||
event: string,
|
||
data: any,
|
||
attachmentGenerator: unknown
|
||
): Promise<{
|
||
to: string;
|
||
status: string;
|
||
data: Record<string, unknown>;
|
||
}> {
|
||
if (event === "order.placed") {
|
||
// retrieve order
|
||
const order = await this.orderService.retrieve(data.id)
|
||
// TODO send email
|
||
|
||
console.log("Notification sent")
|
||
return {
|
||
to: order.email,
|
||
status: "done",
|
||
data: {
|
||
// any data necessary to send the email
|
||
// for example:
|
||
subject: "You placed a new order!",
|
||
items: order.items,
|
||
},
|
||
}
|
||
}
|
||
}
|
||
// ...
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"event","type":"`string`","description":"The name of the event that was triggered. For example, `order.placed`.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"data","type":"`unknown`","description":"The data payload of the event that was triggered. For example, if the `order.placed` event is triggered,\nthe `eventData` object contains the property `id` which is the ID of the order that was placed. You can refer to the\n[Events reference](https://docs.medusajs.com/development/events/events-list) for information on all events and their payloads.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"attachmentGenerator","type":"`unknown`","description":"If you’ve previously register an attachment generator to the `NotificationService` using the\n[`registerAttachmentGenerator`](https://docs.medusajs.com/references/services/classes/services.NotificationService#registerattachmentgenerator) method,\nyou have access to it here. You can use the `attachmentGenerator` to generate on-demand invoices or other documents. The default value of this parameter is `null`.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="sendNotification"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<[ReturnedData](../interfaces/notification.ReturnedData.mdx)>","optional":false,"defaultValue":"","description":"The sending details.","expandable":false,"children":[{"name":"ReturnedData","type":"[ReturnedData](../interfaces/notification.ReturnedData.mdx)","optional":false,"defaultValue":"","description":"The details of a sent or resent notification.","expandable":false,"children":[{"name":"to","type":"`string`","description":"The receiver of the Notification. For example, if you sent an email to the customer then `to` is the email address of the customer.\nIn other cases, it might be a phone number or a username.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"status","type":"`string`","description":"The status of the sent notification. There are no restriction on the returned status.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"data","type":"`Record<string, unknown>`","description":"The data used to send the Notification. For example, if you sent an order confirmation email to the customer, then the `data` object\nmight include the order items or the subject of the email. This `data` is necessary if the notification is resent later as you can use the same data.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]}]} sectionTitle="sendNotification"/>
|
||
|
||
### resendNotification
|
||
|
||
This method is used to resend notifications, which is typically triggered by the
|
||
[Resend Notification API Route](https://docs.medusajs.com/api/admin#notifications\_postnotificationsnotificationresend).
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class EmailSenderService extends AbstractNotificationService {
|
||
// ...
|
||
async resendNotification(
|
||
notification: any,
|
||
config: any,
|
||
attachmentGenerator: unknown
|
||
): Promise<{
|
||
to: string;
|
||
status: string;
|
||
data: Record<string, unknown>;
|
||
}> {
|
||
// check if the receiver should be changed
|
||
const to: string = config.to || notification.to
|
||
|
||
// TODO resend the notification using the same data
|
||
// that is saved under notification.data
|
||
|
||
console.log("Notification resent")
|
||
return {
|
||
to,
|
||
status: "done",
|
||
data: notification.data, // make changes to the data
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"notification","type":"`unknown`","description":"The original [Notification record](https://docs.medusajs.com/references/entities/classes/Notification) that was created after you sent the\nnotification with `sendNotification`. It includes the `to` and `data` attributes which are populated originally using the `to` and `data` properties of\nthe object you return in [sendNotification](notification.AbstractNotificationService.mdx#sendnotification).","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"config","type":"`unknown`","description":"The new configuration used to resend the notification. The [Resend Notification API Route](https://docs.medusajs.com/api/admin#notifications\\_postnotificationsnotificationresend),\nallows you to pass a new `to` field. If specified, it will be available in this config object.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"attachmentGenerator","type":"`unknown`","description":"f you’ve previously register an attachment generator to the `NotificationService` using the\n[`registerAttachmentGenerator`](https://docs.medusajs.com/references/services/classes/services.NotificationService#registerattachmentgenerator) method,\nyou have access to it here. You can use the `attachmentGenerator` to generate on-demand invoices or other documents. The default value of this parameter is `null`.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} sectionTitle="resendNotification"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<[ReturnedData](../interfaces/notification.ReturnedData.mdx)>","optional":false,"defaultValue":"","description":"The resend details.","expandable":false,"children":[{"name":"ReturnedData","type":"[ReturnedData](../interfaces/notification.ReturnedData.mdx)","optional":false,"defaultValue":"","description":"The details of a sent or resent notification.","expandable":false,"children":[{"name":"to","type":"`string`","description":"The receiver of the Notification. For example, if you sent an email to the customer then `to` is the email address of the customer.\nIn other cases, it might be a phone number or a username.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"status","type":"`string`","description":"The status of the sent notification. There are no restriction on the returned status.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"data","type":"`Record<string, unknown>`","description":"The data used to send the Notification. For example, if you sent an order confirmation email to the customer, then the `data` object\nmight include the order items or the subject of the email. This `data` is necessary if the notification is resent later as you can use the same data.","optional":false,"defaultValue":"","expandable":false,"children":[]}]}]}]} sectionTitle="resendNotification"/>
|
||
|
||
---
|
||
|
||
## Subscribe with Loaders
|
||
|
||
After creating your Notification Provider Service, you must create a [Loader](https://docs.medusajs.com/development/loaders/overview) that registers this Service as a notification handler of events.
|
||
|
||
For example, to register the `email-sender` Notification Provider as a handler for the `order.placed` event, create the file `src/loaders/notification.ts` with the following content:
|
||
|
||
```ts title="src/loaders/notification.ts"
|
||
import {
|
||
MedusaContainer,
|
||
NotificationService,
|
||
} from "@medusajs/medusa"
|
||
|
||
export default async (
|
||
container: MedusaContainer
|
||
): Promise<void> => {
|
||
const notificationService = container.resolve<
|
||
NotificationService
|
||
>("notificationService")
|
||
|
||
notificationService.subscribe(
|
||
"order.placed",
|
||
"email-sender"
|
||
)
|
||
}
|
||
```
|
||
|
||
This loader accesses the `notificationService` through the [MedusaContainer](https://docs.medusajs.com/development/fundamentals/dependency-injection). The `notificationService` has a `subscribe` method that accepts 2 parameters. The first one is the name of the event to subscribe to, and the second is the identifier of the Notification Provider that's subscribing to that event.
|
||
|
||
---
|
||
|
||
## Test Sending a Notification
|
||
|
||
Make sure you have an event bus module configured in your Medusa backend. You can learn more on how to do that in the [Configurations guide](https://docs.medusajs.com/development/backend/configurations#modules).
|
||
|
||
Then:
|
||
|
||
1\. Run the `build` command in the root directory of your Medusa backend:
|
||
|
||
```bash npm2yarn
|
||
npm run build
|
||
```
|
||
|
||
2\. Start your Medusa backend:
|
||
|
||
```bash npm2yarn
|
||
npx medusa develop
|
||
```
|
||
|
||
3\. Place an order either using the [REST APIs](https://docs.medusajs.com/api/store) or using the [storefront](https://docs.medusajs.com/starters/nextjs-medusa-starter).
|
||
|
||
4\. After placing an order, you can see in your console the message “Notification Sent”. If you added your own notification sending logic, you should receive an email or alternatively the type of notification you’ve set up.
|
||
|
||
---
|
||
|
||
## Test Resending a Notification
|
||
|
||
To test resending a notification:
|
||
|
||
1. Retrieve the ID of the notification you just sent using the [List Notifications API Route](https://docs.medusajs.com/api/admin#notifications_getnotifications). You can pass as a body parameter the `to` or `event_name` parameters to filter out the notification you just sent.
|
||
|
||
2. Send a request to the [Resend Notification API Route](https://docs.medusajs.com/api/admin#notifications_postnotificationsnotificationresend) using the ID retrieved from the previous request. You can pass the `to` parameter in the body to change the receiver of the notification.
|
||
|
||
3. You should see the message “Notification Resent” in your console.
|