Files
medusa-store/docs/content/development/notification/create-notification-provider.md
Shahed Nasser 6f1b49af03 chore: merge docs from master to develop (#3650)
* Fix issue on fixed total amount discount when using includes tax (#3472)

The calculation of the fixed discount amount breaks when having includes_tax setting active, due to the line item totals are incorrect and returning everything as 0, thus the totalItemPercentage will be Infinitiy due to the division by a subtotal of 0

* chore: Add missing changeset for @medusajs/medusa

* feat(medusa): Improve performance of Products domain (#3417)

* feat(medusa): Improve product update performances

* fix tests and update

* update mock repo

* improve repo

* cleanup

* fix

* cleanup + bulk emit + unit test fix

* improvements

* improve

* fix unit tests

* fix export

* fix product update handler

* enhance mock repo

* fix import integration

* fix end point tests

* revert mock repo product variant

* fix unit

* cleanup

* cleanup

* address feedback

* fix quotes in tests

* address feedback

* Create new-tips-mate.md

* use types

* chore: Remove integration-tests from changeset

* chore(release): v1.7.14

* chore(docs): Generated Docs Announcement Bar (automated) (#3489)

Co-authored-by: olivermrbl <olivermrbl@users.noreply.github.com>

* fix(medusa): EventBusService.emit using Redis mock (#3491)

* Fix eventBusService.emit using redis mock

* revert gitignore

* enqueuer

* unit test add redis_url

* fix test

* chore(docs): Generated Services Reference (automated) (#3490)

Co-authored-by: olivermrbl <olivermrbl@users.noreply.github.com>

* docs: publish restructure (#3496)

* docs: added features and guides overview page

* added image

* added version 2

* added version 3

* added version 4

* docs: implemented new color scheme

* docs: redesigned sidebar (#3193)

* docs: redesigned navbar for restructure (#3199)

* docs: redesigned footer (#3209)

* docs: redesigned cards (#3230)

* docs: redesigned admonitions (#3231)

* docs: redesign announcement bar (#3236)

* docs: redesigned large cards (#3239)

* docs: redesigned code blocks (#3253)

* docs: redesigned search modal and page (#3264)

* docs: redesigned doc footer (#3268)

* docs: added new sidebars + refactored css and assets (#3279)

* docs: redesigned api reference sidebar

* docs: refactored css

* docs: added code tabs transition

* docs: added new sidebars

* removed unused assets

* remove unusued assets

* Fix deploy errors

* fix incorrect link

* docs: fixed code responsivity + missing icons (#3283)

* docs: changed icons (#3296)

* docs: design fixes to the sidebar (#3297)

* redesign fixes

* docs: small design fixes

* docs: several design fixes after restructure (#3299)

* docs: bordered icon fixes

* docs: desgin fixes

* fixes to code blocks and sidebar scroll

* design adjustments

* docs: restructured homepage (#3305)

* docs: restructured homepage

* design fixes

* fixed core concepts icon

* docs: added core concepts page (#3318)

* docs: restructured homepage

* design fixes

* docs: added core concepts page

* changed text of different components

* docs: added architecture link

* added missing prop for user guide

* docs: added regions overview page (#3327)

* docs: added regions overview

* moved region pages to new structure

* docs: fixed description of regions architecture page

* small changes

* small fix

* docs: added customers overview page (#3331)

* docs: added regions overview

* moved region pages to new structure

* docs: fixed description of regions architecture page

* small changes

* small fix

* docs: added customers overview page

* fix link

* resolve link issues

* docs: updated regions architecture image

* docs: second-iteration fixes (#3347)

* docs: redesigned document

* design fixes

* docs: added products overview page (#3354)

* docs: added carts overview page (#3363)

* docs: added orders overview (#3364)

* docs: added orders overview

* added links in overview

* docs: added vercel redirects

* docs: added soon badge for cards (#3389)

* docs: resolved feedback changes + organized troubleshooting pages (#3409)

* docs: resolved feedback changes

* added extra line

* docs: changed icons for restructure (#3421)

* docs: added taxes overview page (#3422)

* docs: added taxes overview page

* docs: fix sidebar label

* added link to taxes overview page

* fixed link

* docs: fixed sidebar scroll (#3429)

* docs: added discounts overview (#3432)

* docs: added discounts overview

* fixed links

* docs: added gift cards overview (#3433)

* docs: added price lists overview page (#3440)

* docs: added price lists overview page

* fixed links

* docs: added sales channels overview page (#3441)

* docs: added sales overview page

* fixed links

* docs: added users overview (#3443)

* docs: fixed sidebar border height (#3444)

* docs: fixed sidebar border height

* fixed svg markup

* docs: added possible solutions to feedback component (#3449)

* docs: added several overview pages + restructured files (#3463)

* docs: added several overview pages

* fixed links

* docs: added feature flags + PAK overview pages (#3464)

* docs: added feature flags + PAK overview pages

* fixed links

* fix link

* fix link

* fixed links colors

* docs: added strategies overview page (#3468)

* docs: automated upgrade guide (#3470)

* docs: automated upgrade guide

* fixed vercel redirect

* docs: restructured files in docs codebase (#3475)

* docs: restructured files

* docs: fixed eslint exception

* docs: finished restructure loose-ends (#3493)

* fixed uses of backend

* docs: finished loose ends

* eslint fixes

* fixed links

* merged master

* added update instructions for v1.7.12

* docs: fixed discount details (#3499)

* docs: fix trailing slash causing 404 (#3508)

* docs: fix error during navigation (#3509)

* docs: removed the gatsby storefront guide (#3527)

* docs: removed the gatsby storefront guide

* docs: fixed query value

* chore(docs): Removed Docs Announcement Bar (automated) (#3536)

Co-authored-by: shahednasser <shahednasser@users.noreply.github.com>

* fix(medusa): Variant update should include the id for the listeners to be able to identify the entity (#3539)

* fix(medusa): Variant update should include the id for the listeners to be able to identify the entity

* fix unit tests

* Create brave-seahorses-film.md

* docs: fix admin redirects (#3548)

* chore(release): v1.7.15

* chore(docs): Generated Docs Announcement Bar (automated) (#3550)

Co-authored-by: olivermrbl <olivermrbl@users.noreply.github.com>

* chore(docs): Generated Services Reference (automated) (#3551)

Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>

* chore: updated READMEs of plugins (#3546)

* chore: updated READMEs of plugins

* added notice to plugins

* docs: added a deploy guide for next.js storefront (#3558)

* docs: added a deploy next.js guide

* docs: fix image zoom

* docs: fixes to next.js deployment guide to vercel (#3562)

* chore(workflows): Enable manual workflow in pre-release mode (#3566)

* chore(docs): Removed Docs Announcement Bar (automated) (#3598)

Co-authored-by: shahednasser <shahednasser@users.noreply.github.com>

* fix(medusa): Rounding issues on line item adjustments (#3446)

* chores(medusa): Attempt to fix discount rounding issues

* add migration

* update entities

* apply multipler factor properly

* fix discount service

* WIP

* fix rounding issues in discounts

* fix some tests

* Exclude raw_discount_total from responses

* fix adjustments

* cleanup response

* fix

* fix draft order integration

* fix order integration

* fix order integration

* address feedback

* fix test

* Create .changeset/polite-llamas-sit.md

* remove comment

---------

Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>

* chore(workflows): Add release notification (#3629)

---------

Co-authored-by: pepijn-vanvlaanderen <pepijn@webbers.com>
Co-authored-by: olivermrbl <oliver@mrbltech.com>
Co-authored-by: Adrien de Peretti <adrien.deperetti@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: olivermrbl <olivermrbl@users.noreply.github.com>
Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
Co-authored-by: shahednasser <shahednasser@users.noreply.github.com>
Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
2023-03-31 09:34:38 +02:00

16 KiB
Raw Blame History

description, addHowToData
description addHowToData
Learn how to create a notification provider in Medusa. This guide explains the different methods available in a Notification provider. true

How to Create a Notification Provider

In this document, youll learn how to create a Notification Provider in Medusa.

:::note

If youre unfamiliar with the Notification architecture in Medusa, it is recommended to check out the architecture overview first.

:::

Prerequisites

Before you start creating a Notification Provider, you need to either install a Medusa backend, or create it in a plugin.

You also need to setup Redis and configure it with the Medusa backend to test out the Notification provider.


Create a Notification Provider

Creating a Notification Provider is as simple as creating a TypeScript or JavaScript file in src/services. The name of the file is the name of the provider (for example, sendgrid.ts). A Notification Provider is essentially a Service that extends the AbstractNotificationService from @medusajs/medusa.

For example, create the file src/services/email-sender.ts with the following content:

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

Where EmailSenderService is the name of your Notification Provider Service.

Notification Providers must extend NotificationService from medusa-interfaces.

:::info

Following the naming convention of Services, the name of the file should be the slug name of the Notification Provider, and the name of the class should be the camel case name of the Notification Provider suffixed with “Service”. In the example above, the name of the file should be email-sender.js. You can learn more in the service documentation.

:::

identifier

Notification Provider Services must have a static property identifier.

The NotificationProvider entity has 2 properties: identifier and is_installed. The value of the identifier property in the Service 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 Subscriber.

For example, in the class you created in the previous code snippet you can add the following property:

class EmailSenderService extends AbstractNotificationService {
  static identifier = "email-sender"
  // ...
}

constructor

You can use the constructor of your Notification Provider to have access to 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 providers APIs, you can initialize it in the constructor and use it in other methods in the Service.

Additionally, if youre 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. The options are passed as a second parameter.

:::info

You can learn more about plugins and how to create them in the Plugins documentation.

:::

Continuing on with the previous example, if you want to use the OrderService later when sending notifications, you can inject it into the constructor:

import { 
  AbstractNotificationService, 
  OrderService,
} from "@medusajs/medusa"

class EmailSenderService extends AbstractNotificationService {
  protected manager_: EntityManager
  protected transactionManager_: EntityManager
  static identifier = "email-sender"
  protected orderService: OrderService

  constructor(container, options) {
    super(container)
    // you can access options here in case you're
    // using a plugin

    this.orderService = container.orderService
  }

  // ...
}

sendNotification

When an event is triggered that your Notification Provider is registered as a handler for, the NotificationService in Medusas core will execute the sendNotification method of your Notification Provider.

In this method, you can perform the necessary operation to send the Notification. Following the example above, you can send an email to the customer when they place an order.

This method receives three parameters:

  1. eventName: This is the name of the event that was triggered. For example, order.placed.
  2. eventData: This is the data payload of the event that was triggered. For example, if the order.placed event is triggered, the eventData object contains the property id which is the ID of the order that was placed.
  3. attachmentGenerator: If youve previously attached a generator to the NotificationService using the registerAttachmentGenerator method, you 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.

:::info

You can learn more about what events are triggered in Medusa and their data payload in the Events List documentation.

:::

This method must return an object containing two properties:

  1. to: a string that represents the receiver of the Notification. For example, if you sent an email to the customer then to is the email address of the customer. In other cases, it might be a phone number or a username.
  2. data: an object that contains the data used to send the Notification. For example, if you sent an order confirmation email to the customer, then the data object might 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.

Continuing with the previous example you can have the following implementation of the sendNotification method:

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,
        },
      }
    }
  }
}

In this code snippet, you check first if the event is order.placed. This can be helpful if youre handling multiple events using the same Notification Provider.

You then retrieve the order using the ID and send the email. Here, the logic related to sending the email is not implemented as it is generally specific to your Notification Provider.

Finally, you return an object with the to property set to the customer email and the data property is an object that contains data necessary to send the email such as a subject or items.

:::note

The to and data properties are used in the NotificationService in Medusas core to create a new Notification record in the database. You can learn more about the Notification entity in the Architecture Overview documentation.

:::

resendNotification

Using the Resend Notification endpoint, an admin user can resend a Notification to the customer. The NotificationService in Medusas core then executes the resendNotification method in your Notification Provider.

This method receives three parameters:

  1. notification: This is the original Notification record that was created after you sent the notification with sendNotification. You can get an overview of the entity and its attributes in the architecture overview, but most notably it includes the to and data attributes which are populated originally using the to and data properties of the object you return in sendNotification.
  2. config: In the Resend Notification endpoint you may specify an alternative receiver of the notification using the to request body parameter. For example, you may want to resend the order confirmation email to a different email. If thats the case, you have access to it in the config parameter object. Otherwise, config will be an empty object.
  3. attachmentGenerator: If youve previously attached a generator to the Notification Service using the registerAttachmentGenerator method, you 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.

Similarly to the sendNotification method, this method must return an object containing two properties:

  1. to: a string that represents the receiver of the Notification. You can either return the same to available in the notification parameter or the updated one in the config parameter.
  2. data: an object that contains the data used to send the Notification. You can either return the same data in the notification parameter or make any necessary updates to it.

Continuing with the previous example you can have the following implementation of the resendNotification method:

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 ? 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
    }
  }
}

In the above snippet, you check if the to should be changed by checking if the config parameter has a to property. Otherwise, you keep the same to address stored in the notification parameter.

You then resend the email. Here, the logic related to sending the email is not implemented as it is generally specific to your Notification Provider.

Finally, you return an object with the to property set to the email (either new or old) and the data property is the same data used before to send the original notification. you can alternatively make any changes to the data.

:::note

The to and data properties are used in the NotificationService in Medusas core to create a new Notification record in the database. No changes are made to the original Notification record created after the sendNotification method. This new record is associated with the original Notification record using the parent_id attribute in the entity. You can learn more about the Notification entity in the Architecture Overview documentation.

:::


Create a Subscriber

After creating your Notification Provider Service, you must create a Subscriber that registers this Service as a notification handler of events.

:::note

This section will not cover the basics of Subscribers. You can read the Subscribers documentation to learn more about them and how to create them.

:::

Following the previous example, to make sure the email-sender Notification Provider handles the order.placed event, create the file src/subscribers/notification.js with the following content:

class NotificationSubscriber {
  constructor({ notificationService }) {
    notificationService.subscribe(
      "order.placed", 
      "email-sender"
    )
  }
  // ...
}

export default NotificationSubscriber

This subscriber accesses the notificationService using dependency injection. The notificationService contains 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 is subscribing to that event.

:::tip

Notice that the value of the identifier static property defined in the EmailSenderService is used to register the Notification Provider to handle the order.placed event.

:::


Test Sending Notifications with your Notification Provider

Make sure you've configured Redis with your Medusa backend as explained in the Prerequisites section and that the Redis service is running.

Then, start by running your Medusa backend:

npm run start

Then, place an order either using the REST APIs or using the storefront.

:::tip

If you dont have a storefront installed you can get started with the Next.js starter storefront in minutes.

:::

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 youve set up.


Test Resending Notifications with your Notification Provider

To test resending a notification, first, retrieve the ID of the notification you just sent using the List Notifications admin endpoint. You can pass as a body parameter the to or event_name parameters to filter out the notification you just sent.

:::tip

You must be authenticated as an admin user before sending this request. You can use the Authenticate a User endpoint to get authenticated.

:::

List Notifications Request

Then, send a request to the Resend Notification endpoint using the ID retrieved from the previous request. You can pass the to parameter in the body to change the receiver of the notification. You should see the message “Notification Resent” in your console and if you implemented your own logic for resending the notification it will be resent.

Resend Notifications Request

This request returns the same notification object as the List Notifications endpoint, but it now has a new object in the resends array. This is the resent notification. If you supplied a to parameter in the request body, you should see its value in the to property of the resent notification object.


See Also