feat: Add the basic implementation of notification module (#7282)
* feat: Add the basic implementation of notification module * fix: Minor fixes and introduction of idempotency key * fix: Changes based on PR review
This commit is contained in:
@@ -36,6 +36,7 @@ export enum Modules {
|
||||
STORE = "store",
|
||||
CURRENCY = "currency",
|
||||
FILE = "file",
|
||||
NOTIFICATION = "notification",
|
||||
}
|
||||
|
||||
export enum ModuleRegistrationName {
|
||||
@@ -61,6 +62,7 @@ export enum ModuleRegistrationName {
|
||||
STORE = "storeModuleService",
|
||||
CURRENCY = "currencyModuleService",
|
||||
FILE = "fileModuleService",
|
||||
NOTIFICATION = "notificationModuleService",
|
||||
}
|
||||
|
||||
export const MODULE_PACKAGE_NAMES = {
|
||||
@@ -87,6 +89,7 @@ export const MODULE_PACKAGE_NAMES = {
|
||||
[Modules.STORE]: "@medusajs/store",
|
||||
[Modules.CURRENCY]: "@medusajs/currency",
|
||||
[Modules.FILE]: "@medusajs/file",
|
||||
[Modules.NOTIFICATION]: "@medusajs/notification",
|
||||
}
|
||||
|
||||
export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } =
|
||||
@@ -378,6 +381,19 @@ export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } =
|
||||
resources: MODULE_RESOURCE_TYPE.SHARED,
|
||||
},
|
||||
},
|
||||
[Modules.NOTIFICATION]: {
|
||||
key: Modules.NOTIFICATION,
|
||||
registrationName: ModuleRegistrationName.NOTIFICATION,
|
||||
defaultPackage: false,
|
||||
label: upperCaseFirst(ModuleRegistrationName.NOTIFICATION),
|
||||
isRequired: false,
|
||||
isQueryable: true,
|
||||
dependencies: ["logger"],
|
||||
defaultModuleDeclaration: {
|
||||
scope: MODULE_SCOPE.INTERNAL,
|
||||
resources: MODULE_RESOURCE_TYPE.SHARED,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const MODULE_DEFINITIONS: ModuleDefinition[] =
|
||||
|
||||
@@ -27,3 +27,4 @@ export * as StoreTypes from "./store"
|
||||
export * as CurrencyTypes from "./currency"
|
||||
export * as HttpTypes from "./http"
|
||||
export * as FileTypes from "./file"
|
||||
export * as NotificationTypes from "./notification"
|
||||
|
||||
@@ -38,3 +38,4 @@ export * from "./transaction-base"
|
||||
export * from "./user"
|
||||
export * from "./workflow"
|
||||
export * from "./workflows"
|
||||
export * from "./notification"
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import { BaseFilterable } from "../dal"
|
||||
import { OperatorMap } from "../dal/utils"
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*
|
||||
* A notification's data.
|
||||
*/
|
||||
export interface NotificationDTO {
|
||||
/**
|
||||
* The ID of the notification.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The recipient of the notification. It can be email, phone number, or username, depending on the channel.
|
||||
*/
|
||||
to: string
|
||||
/**
|
||||
* The channel through which the notification is sent, such as 'email' or 'sms'
|
||||
*/
|
||||
channel: string
|
||||
/**
|
||||
* The template name in the provider's system.
|
||||
*/
|
||||
template: string
|
||||
/**
|
||||
* The data that gets passed over to the provider for rendering the notification.
|
||||
*/
|
||||
data: Record<string, unknown> | null
|
||||
/**
|
||||
* The event name, the workflow, or anything else that can help to identify what triggered the notification.
|
||||
*/
|
||||
trigger_type?: string | null
|
||||
/**
|
||||
* The ID of the resource this notification is for, if applicable. Useful for displaying relevant information in the UI
|
||||
*/
|
||||
resource_id?: string | null
|
||||
/**
|
||||
* The type of the resource this notification is for, if applicable, eg. "order"
|
||||
*/
|
||||
resource_type?: string | null
|
||||
/**
|
||||
* The ID of the customer this notification is for, if applicable.
|
||||
*/
|
||||
receiver_id?: string | null
|
||||
/**
|
||||
* The original notification, in case this is a retried notification.
|
||||
*/
|
||||
original_notification_id?: string | null
|
||||
/**
|
||||
* The id of the notification in the external system, if applicable
|
||||
*/
|
||||
external_id?: string | null
|
||||
/**
|
||||
* The ID of the notification provider.
|
||||
*/
|
||||
provider_id: string
|
||||
/**
|
||||
* Information about the notification provider
|
||||
*/
|
||||
provider: NotificationProviderDTO
|
||||
/**
|
||||
* The date and time the notification was created.
|
||||
*/
|
||||
created_at: Date
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*
|
||||
* Information about the notification provider
|
||||
*/
|
||||
export interface NotificationProviderDTO {
|
||||
/**
|
||||
* The ID of the notification provider.
|
||||
*/
|
||||
id: string
|
||||
/**
|
||||
* The handle of the notification provider.
|
||||
*/
|
||||
handle: string
|
||||
/**
|
||||
* A user-friendly name of the notification provider.
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* The supported channels by the notification provider.
|
||||
*/
|
||||
channels: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*
|
||||
* The filters to apply on retrieved notifications.
|
||||
*
|
||||
* @prop q - Search through the notifications' attributes, such as trigger types and recipients, using this search term.
|
||||
*/
|
||||
|
||||
export interface FilterableNotificationProps
|
||||
extends BaseFilterable<FilterableNotificationProps> {
|
||||
/**
|
||||
* Search through the notifications' attributes, such as trigger types and recipients, using this search term.
|
||||
*/
|
||||
q?: string
|
||||
/**
|
||||
* Filter based on the recipient of the notification.
|
||||
*/
|
||||
to?: string | string[] | OperatorMap<string | string[]>
|
||||
/**
|
||||
* Filter based on the channel through which the notification is sent, such as 'email' or 'sms'
|
||||
*/
|
||||
channel?: string | string[] | OperatorMap<string | string[]>
|
||||
/**
|
||||
* Filter based on the template name.
|
||||
*/
|
||||
template?: string | string[] | OperatorMap<string | string[]>
|
||||
/**
|
||||
* Filter based on the trigger type.
|
||||
*/
|
||||
trigger_type?: string | string[] | OperatorMap<string | string[]>
|
||||
/**
|
||||
* Filter based on the resource that was the trigger for the notification.
|
||||
*/
|
||||
resource_id?: string | string[] | OperatorMap<string | string[]>
|
||||
/**
|
||||
* T* Filter based on the resource type that was the trigger for the notification.
|
||||
*/
|
||||
resource_type?: string | string[] | OperatorMap<string | string[]>
|
||||
/**
|
||||
* Filter based on the customer ID.
|
||||
*/
|
||||
receiver_id?: string | string[] | OperatorMap<string | string[]>
|
||||
/**
|
||||
* Filters a notification based on when it was sent and created in the database
|
||||
*/
|
||||
created_at?: OperatorMap<string>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from "./common"
|
||||
export * from "./providers"
|
||||
export * from "./mutations"
|
||||
export * from "./service"
|
||||
export * from "./provider"
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @interface
|
||||
*
|
||||
* A notification to send and have created in the DB
|
||||
*
|
||||
*/
|
||||
export interface CreateNotificationDTO {
|
||||
/**
|
||||
* The recipient of the notification. It can be email, phone number, or username, depending on the channel.
|
||||
*/
|
||||
to: string
|
||||
/**
|
||||
* The channel through which the notification is sent, such as 'email' or 'sms'
|
||||
*/
|
||||
channel: string
|
||||
/**
|
||||
* The template name in the provider's system.
|
||||
*/
|
||||
template: string
|
||||
/**
|
||||
* The data that gets passed over to the provider for rendering the notification.
|
||||
*/
|
||||
data?: Record<string, unknown> | null
|
||||
/**
|
||||
* The event name, the workflow, or anything else that can help to identify what triggered the notification.
|
||||
*/
|
||||
trigger_type?: string | null
|
||||
/**
|
||||
* The ID of the resource this notification is for, if applicable. Useful for displaying relevant information in the UI
|
||||
*/
|
||||
resource_id?: string | null
|
||||
/**
|
||||
* The type of the resource this notification is for, if applicable, eg. "order"
|
||||
*/
|
||||
resource_type?: string | null
|
||||
/**
|
||||
* The ID of the customer this notification is for, if applicable.
|
||||
*/
|
||||
receiver_id?: string | null
|
||||
/**
|
||||
* The original notification, in case this is a retried notification.
|
||||
*/
|
||||
original_notification_id?: string | null
|
||||
/**
|
||||
* An idempotency key that ensures the same notification is not sent multiple times.
|
||||
*/
|
||||
idempotency_key?: string | null
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* @interface
|
||||
*
|
||||
* The details of the notification to send.
|
||||
*/
|
||||
export type ProviderSendNotificationDTO = {
|
||||
/**
|
||||
* The recipient of the notification. It can be email, phone number, or username, depending on the channel.
|
||||
*/
|
||||
to: string
|
||||
/**
|
||||
* The channel through which the notification is sent, such as 'email' or 'sms'
|
||||
*/
|
||||
channel: string
|
||||
/**
|
||||
* The template name in the provider's system.
|
||||
*/
|
||||
template: string
|
||||
/**
|
||||
* The data that gets passed over to the provider for rendering the notification.
|
||||
*/
|
||||
data?: Record<string, unknown> | null
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*
|
||||
* The result of sending the notification
|
||||
*/
|
||||
export type ProviderSendNotificationResultsDTO = {
|
||||
/**
|
||||
* The ID of the notification in the external system, if provided in the response
|
||||
*/
|
||||
id?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Overview
|
||||
*
|
||||
* Notification provider interface for the notification module.
|
||||
*
|
||||
*/
|
||||
export interface INotificationProvider {
|
||||
/**
|
||||
* This method is used to send a notification.
|
||||
*
|
||||
* @param {ProviderSendNotificationDTO} notification - All information needed to send a notification.
|
||||
* @returns {Promise<ProviderSendNotificationResultsDTO>} The result of sending the notification.
|
||||
*
|
||||
*/
|
||||
send(
|
||||
notification: ProviderSendNotificationDTO
|
||||
): Promise<ProviderSendNotificationResultsDTO>
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./local"
|
||||
@@ -0,0 +1 @@
|
||||
export interface LocalNotificationServiceOptions {}
|
||||
@@ -0,0 +1,199 @@
|
||||
import { FindConfig } from "../common"
|
||||
import { IModuleService } from "../modules-sdk"
|
||||
import { Context } from "../shared-context"
|
||||
import { FilterableNotificationProps, NotificationDTO } from "./common"
|
||||
import { CreateNotificationDTO } from "./mutations"
|
||||
|
||||
/**
|
||||
* The main service interface for the Notification Module.
|
||||
*/
|
||||
export interface INotificationModuleService extends IModuleService {
|
||||
/**
|
||||
* This method is used to send multiple notifications, and store the requests in the DB.
|
||||
*
|
||||
* @param {CreateNotificationDTO[]} data - The notifications to be sent.
|
||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||
* @returns {Promise<NotificationDTO[]>} The list of sent notifications.
|
||||
*
|
||||
* @example
|
||||
* const notifications = await notificationModuleService.create([
|
||||
* {
|
||||
* to: "john@doe.me",
|
||||
* template: "order-confirmation",
|
||||
* channel: "email",
|
||||
* },
|
||||
* {
|
||||
* to: "+38975123456",
|
||||
* template: "order-confirmation",
|
||||
* channel: "sms",
|
||||
* },
|
||||
* ])
|
||||
*/
|
||||
create(
|
||||
data: CreateNotificationDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<NotificationDTO[]>
|
||||
|
||||
/**
|
||||
* This method is used to send a notification, and store the request in the DB.
|
||||
*
|
||||
* @param {CreateNotificationDTO} data - The notification to be sent.
|
||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||
* @returns {Promise<NotificationDTO>} The sent notification.
|
||||
*
|
||||
* @example
|
||||
* const notification = await notificationModuleService.create({
|
||||
* to: "john@doe.me",
|
||||
* template: "order-confirmation",
|
||||
* channel: "email",
|
||||
* })
|
||||
*/
|
||||
create(
|
||||
data: CreateNotificationDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<NotificationDTO>
|
||||
|
||||
/**
|
||||
* This method is used to retrieve a notification by its ID
|
||||
*
|
||||
* @param {string} notificationId - The ID of the notification to retrieve.
|
||||
* @param {FindConfig<NotificationDTO>} config -
|
||||
* The configurations determining how the notification is retrieved. Its properties, such as `select` or `relations`, accept the
|
||||
* attributes or relations associated with a notification.
|
||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||
* @returns {Promise<NotificationDTO>} The retrieved notification.
|
||||
*
|
||||
* @example
|
||||
* A simple example that retrieves a notification by its ID:
|
||||
*
|
||||
* ```ts
|
||||
* const notification =
|
||||
* await notificationModuleService.retrieve("noti_123")
|
||||
* ```
|
||||
*
|
||||
* To specify relations that should be retrieved:
|
||||
*
|
||||
* ```ts
|
||||
* const notification = await notificationModuleService.retrieve(
|
||||
* "noti_123",
|
||||
* {
|
||||
* relations: ["provider"],
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
retrieve(
|
||||
notificationId: string,
|
||||
config?: FindConfig<NotificationDTO>,
|
||||
sharedContext?: Context
|
||||
): Promise<NotificationDTO>
|
||||
|
||||
/**
|
||||
* This method is used to retrieve a paginated list of notifications based on optional filters and configuration.
|
||||
*
|
||||
* @param {FilterableNotificationProps} filters - The filters to apply on the retrieved notifications.
|
||||
* @param {FindConfig<NotificationDTO>} config -
|
||||
* The configurations determining how the notifications are retrieved. Its properties, such as `select` or `relations`, accept the
|
||||
* attributes or relations associated with a notification.
|
||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||
* @returns {Promise<NotificationDTO[]>} The list of notifications.
|
||||
*
|
||||
* @example
|
||||
* To retrieve a list of notifications using their IDs:
|
||||
*
|
||||
* ```ts
|
||||
* const notifications = await notificationModuleService.list({
|
||||
* id: ["noti_123", "noti_321"],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* To specify relations that should be retrieved within the notifications:
|
||||
*
|
||||
* ```ts
|
||||
* const notifications = await notificationModuleService.list(
|
||||
* {
|
||||
* id: ["noti_123", "noti_321"],
|
||||
* },
|
||||
* {
|
||||
* relations: ["provider"],
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter:
|
||||
*
|
||||
* ```ts
|
||||
* const notifications = await notificationModuleService.list(
|
||||
* {
|
||||
* id: ["noti_123", "noti_321"],
|
||||
* },
|
||||
* {
|
||||
* relations: ["provider"],
|
||||
* take: 20,
|
||||
* skip: 2,
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
list(
|
||||
filters?: FilterableNotificationProps,
|
||||
config?: FindConfig<NotificationDTO>,
|
||||
sharedContext?: Context
|
||||
): Promise<NotificationDTO[]>
|
||||
|
||||
/**
|
||||
* This method is used to retrieve a paginated list of notifications along with the total count of available notifications satisfying the provided filters.
|
||||
*
|
||||
* @param {FilterableNotificationProps} filters - The filters to apply on the retrieved notifications.
|
||||
* @param {FindConfig<NotificationDTO>} config -
|
||||
* The configurations determining how the notifications are retrieved. Its properties, such as `select` or `relations`, accept the
|
||||
* attributes or relations associated with a notification.
|
||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||
* @returns {Promise<NotificationDTO[]>} The list of notifications along with the total count.
|
||||
*
|
||||
* @example
|
||||
* To retrieve a list of notifications using their IDs:
|
||||
*
|
||||
* ```ts
|
||||
* const [notifications, count] =
|
||||
* await notificationModuleService.listAndCount({
|
||||
* id: ["noti_123", "noti_321"],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* To specify relations that should be retrieved within the notifications:
|
||||
*
|
||||
* ```ts
|
||||
* const [notifications, count] =
|
||||
* await notificationModuleService.listAndCount(
|
||||
* {
|
||||
* id: ["noti_123", "noti_321"],
|
||||
* },
|
||||
* {
|
||||
* relations: ["provider"],
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter:
|
||||
*
|
||||
* ```ts
|
||||
* const [notifications, count] =
|
||||
* await notificationModuleService.listAndCount(
|
||||
* {
|
||||
* id: ["noti_123", "noti_321"],
|
||||
* },
|
||||
* {
|
||||
* relations: ["provider"],
|
||||
* take: 20,
|
||||
* skip: 2,
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
listAndCount(
|
||||
filters?: FilterableNotificationProps,
|
||||
config?: FindConfig<NotificationDTO>,
|
||||
sharedContext?: Context
|
||||
): Promise<[NotificationDTO[], number]>
|
||||
}
|
||||
@@ -79,7 +79,7 @@ export interface IProductModuleService extends IModuleService {
|
||||
): Promise<ProductDTO>
|
||||
|
||||
/**
|
||||
* This method is used to retrieve a paginated list of price sets based on optional filters and configuration.
|
||||
* This method is used to retrieve a paginated list of products based on optional filters and configuration.
|
||||
*
|
||||
* @param {FilterableProductProps} filters - The filters to apply on the retrieved products.
|
||||
* @param {FindConfig<ProductDTO>} config -
|
||||
|
||||
@@ -24,5 +24,6 @@ export * from "./user"
|
||||
export * from "./api-key"
|
||||
export * from "./link"
|
||||
export * from "./file"
|
||||
export * from "./notification"
|
||||
|
||||
export const MedusaModuleType = Symbol.for("MedusaModule")
|
||||
|
||||
@@ -22,4 +22,5 @@ export enum Modules {
|
||||
STORE = "store",
|
||||
CURRENCY = "currency",
|
||||
FILE = "file",
|
||||
NOTIFICATION = "notification",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NotificationTypes, INotificationProvider } from "@medusajs/types"
|
||||
|
||||
export class AbstractNotificationProviderService
|
||||
implements INotificationProvider
|
||||
{
|
||||
async send(
|
||||
notification: NotificationTypes.ProviderSendNotificationDTO
|
||||
): Promise<NotificationTypes.ProviderSendNotificationResultsDTO> {
|
||||
throw Error(
|
||||
`send is not implemented in ${
|
||||
Object.getPrototypeOf(this).constructor.name
|
||||
}`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./abstract-notification-provider"
|
||||
Reference in New Issue
Block a user