docs: update event module guide (#10056)

This commit is contained in:
Shahed Nasser
2024-11-13 11:00:22 +02:00
committed by GitHub
parent 3962c2c88f
commit 1fbe1d4ee9
3 changed files with 161 additions and 33 deletions

View File

@@ -1,3 +1,5 @@
import { TypeList } from "docs-ui"
export const metadata = {
title: `How to Create an Event Module`,
}
@@ -19,15 +21,18 @@ Create the file `src/modules/my-event/service.ts` that holds the implementation
The Event Module's main service must extend the `AbstractEventBusModuleService` class imported from `@medusajs/framework/utils`:
```ts title="src/modules/my-event/service.ts"
import { EmitData, Message } from "@medusajs/framework/types"
import { AbstractEventBusModuleService } from "@medusajs/framework/utils"
import { AbstractEventBusModuleService } from "@medusajs/framework/utils";
import { Message } from "@medusajs/types";
class MyEventService extends AbstractEventBusModuleService {
emit<T>(eventName: string, data: T, options: Record<string, unknown>): Promise<void>;
emit<T>(data: EmitData<T>[]): Promise<void>;
emit<T>(data: Message<T>[]): Promise<void>;
emit(eventName: unknown, data?: unknown, options?: unknown): Promise<void> {
throw new Error("Method not implemented.")
async emit<T>(data: Message<T> | Message<T>[], options: Record<string, unknown>): Promise<void> {
throw new Error("Method not implemented.");
}
async releaseGroupedEvents(eventGroupId: string): Promise<void> {
throw new Error("Method not implemented.");
}
async clearGroupedEvents(eventGroupId: string): Promise<void> {
throw new Error("Method not implemented.");
}
}
@@ -36,52 +41,175 @@ export default MyEventService
The service implements the required methods based on the desired publish/subscribe logic.
### Note About the eventToSubscribersMap Property
### eventToSubscribersMap_ Property
The `AbstractEventBusModuleService` has a field `eventToSubscribersMap`, which is a JavaScript Map. The map's keys are the event names, whereas the value of each key is an array of subscribed handler functions.
The `AbstractEventBusModuleService` has a field `eventToSubscribersMap_`, which is a JavaScript Map. The map's keys are the event names, whereas the value of each key is an array of subscribed handler functions.
In your custom implementation, you can use this property to manage the subscribed handler functions:
```ts
const eventSubscribers =
this.eventToSubscribersMap.get(eventName) || []
this.eventToSubscribersMap_.get(eventName) || []
```
### Implement emit Method
### emit Method
The `emit` method is used to push an event from the Medusa application into your messaging system. The subscribers to that event would then pick up the message and execute their asynchronous tasks.
The `emit` method has three different signatures:
An example implementation:
1. The first signature accepts three parameters:
- The first parameter is a string indicating the name of the event to trigger.
- The second parameter is data to send to subscribers of that event.
- The third optional parameter can be used to pass options specific to the event service.
2. The second signature accepts one parameter, which is an array of objects having the following properties:
- `eventName`: A string indicating the name of the event to trigger.
- `data`: The data to send to subsribers of that event.
- `options`: (optional) options specific to the event service.
3. The third signature accepts one parameter, which is an array of objects having the following properties:
- `eventName`: A string indicating the name of the event to trigger.
- `body`: An object of event-related data. It has two properties: `data` holding the data of the event, and `metadata` which is an object with more details on how the event was emitted, such as the action that occurred or the service that emitted it.
- `options`: (optional) options specific to the event service.
```ts title="src/modules/my-event/service.ts"
class MyEventService extends AbstractEventBusModuleService {
async emit<T>(data: Message<T> | Message<T>[], options: Record<string, unknown>): Promise<void> {
const events = Array.isArray(data) ? data : [data]
You can implement your method in a way that supports both signatures by checking the type of the first input. For example:
for (const event of events) {
console.log(`Received the event ${event.name} with data ${event.data}`)
// TODO push the event somewhere
}
}
// ...
}
```
The `emit` method receives the following parameters:
<TypeList
types={[
{
name: "data",
type: "`object or array of objects`",
description: "The emitted event(s).",
optional: false,
children: [
{
name: "name",
type: "`string`",
description: "The name of the emitted event.",
optional: false
},
{
name: "data",
type: "`object`",
description: "The data payload of the event.",
optional: false
},
{
name: "metadata",
type: "`object`",
description: "Additional details of the emitted event.",
optional: false,
children: [
{
name: "eventGroupId",
type: "string",
description: "A group ID that the event belongs to.",
optional: true
}
]
},
{
name: "options",
type: "`object`",
description: "Additional options relevant for the event service.",
optional: false
}
]
}
]}
/>
### releaseGroupedEvents Method
Grouped events are useful when you have distributed transactions where you need to explicitly group, release, and clear events upon lifecycle transaction events.
If your Event Module supports grouped events, this method is used to emit all events in a group, then clear that group.
For example:
```ts title="src/modules/my-event/service.ts"
class MyEventService extends AbstractEventBusModuleService {
protected groupedEventsMap_: Map<string, Message[]>
constructor() {
// @ts-ignore
super(...arguments)
this.groupedEventsMap_ = new Map()
}
async releaseGroupedEvents(eventGroupId: string): Promise<void> {
const groupedEvents = this.groupedEventsMap_.get(eventGroupId) || []
for (const event of groupedEvents) {
const { options, ...eventBody } = event
// TODO emit event
}
await this.clearGroupedEvents(eventGroupId)
}
// ...
}
```
The `releaseGroupedEvents` receives the group ID as a parameter.
In the example above, you add a `groupedEventsMap_` property to store grouped events. Then, in the method, you emit the events in the group, then clear the grouped events using the `clearGroupedEvents` which you'll learn about next.
To add events to the grouped events map, you can do it in the `emit` method:
```ts title="src/modules/my-event/service.ts"
class MyEventService extends AbstractEventBusModuleService {
// ...
emit<T>(eventName: string, data: T, options: Record<string, unknown>): Promise<void>;
emit<T>(data: EmitData<T>[]): Promise<void>;
emit<T>(data: Message<T>[]): Promise<void>;
emit(eventOrData: unknown, data?: unknown, options?: unknown): Promise<void> {
const isBulkEmit = Array.isArray(eventOrData)
async emit<T>(data: Message<T> | Message<T>[], options: Record<string, unknown>): Promise<void> {
const events = Array.isArray(data) ? data : [data]
// ...
for (const event of events) {
console.log(`Received the event ${event.name} with data ${event.data}`)
if (event.metadata.eventGroupId) {
const groupedEvents = this.groupedEventsMap_.get(
event.metadata.eventGroupId
) || []
groupedEvents.push(event)
this.groupedEventsMap_.set(event.metadata.eventGroupId, groupedEvents)
continue
}
// TODO push the event somewhere
}
}
}
```
### clearGroupedEvents Method
If your Event Module supports grouped events, this method is used to remove the events of a group.
For example:
```ts title="src/modules/my-event/service.ts"
class MyEventService extends AbstractEventBusModuleService {
// from previous section
protected groupedEventsMap_: Map<string, Message[]>
async clearGroupedEvents(eventGroupId: string): Promise<void> {
this.groupedEventsMap_.delete(eventGroupId)
}
// ...
}
```
The method accepts the group's name as a parameter.
In the method, you delete the group from the `groupedEventsMap_` property (added in the previous section), deleting the stored events of it as well.
---
## 3. Create Module Definition File

View File

@@ -8,4 +8,4 @@ export const metadata = {
This section includes documentation for official Medusa architectural modules.
<ChildDocs hideItems={["Guides"]} />
<ChildDocs hideItems={["Guides", "Overview"]} />

View File

@@ -236,7 +236,7 @@ export const generatedEditDates = {
"app/architectural-modules/cache/create/page.mdx": "2024-10-16T08:51:35.074Z",
"app/admin-widget-injection-zones/page.mdx": "2024-09-30T08:43:53.147Z",
"app/architectural-modules/notification/page.mdx": "2024-10-15T12:51:28.735Z",
"app/architectural-modules/event/create/page.mdx": "2024-10-16T08:51:41.334Z",
"app/architectural-modules/event/create/page.mdx": "2024-11-12T11:54:51.583Z",
"references/core_flows/Order/functions/core_flows.Order.orderEditUpdateItemQuantityValidationStep/page.mdx": "2024-08-20T00:10:58.913Z",
"references/core_flows/Order/functions/core_flows.Order.orderEditUpdateItemQuantityWorkflow/page.mdx": "2024-08-20T00:10:58.949Z",
"references/core_flows/Order/functions/core_flows.Order.updateOrderEditItemQuantityValidationStep/page.mdx": "2024-08-20T00:10:59.121Z",