Skip to content

Commit

Permalink
chore(): Handle medusa service base methods events (#9179)
Browse files Browse the repository at this point in the history
* chore(): Handle medusa service base methods events

* cleanup

* cleanup

* fix import

* fix decorator order

* fixes

* apply default event emition

* fix binding

* fix binding

* align tests with new event emition
  • Loading branch information
adrien2p committed Sep 18, 2024
1 parent 81d3ae0 commit a734184
Show file tree
Hide file tree
Showing 13 changed files with 290 additions and 116 deletions.
4 changes: 4 additions & 0 deletions packages/core/utils/src/event-bus/message-aggregator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export class MessageAggregator implements IMessageAggregator {
this.messages = []
}

count(): number {
return this.messages.length
}

save(msg: Message | Message[]): void {
if (!msg || (Array.isArray(msg) && msg.length === 0)) {
return
Expand Down
30 changes: 26 additions & 4 deletions packages/core/utils/src/event-bus/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,25 @@ type ReturnType<TNames extends string[]> = {
>}_DETACHED`]: `${KebabCase<K & string>}.detached`
}

/**
* Build a conventional event name from the object name and the action and the prefix if provided
* @param prefix
* @param objectName
* @param action
*/
export function buildModuleResourceEventName({
prefix,
objectName,
action,
}: {
prefix?: string
objectName: string
action: string
}): string {
const kebabCaseName = lowerCaseFirst(kebabCase(objectName))
return `${prefix ? `${prefix}.` : ""}${kebabCaseName}.${action}`
}

/**
* From the given strings it will produce the event names accordingly.
* the result will look like:
Expand All @@ -43,6 +62,7 @@ type ReturnType<TNames extends string[]> = {
* }
*
* @param names
* @param prefix
*/
export function buildEventNamesFromEntityName<TNames extends string[]>(
names: TNames,
Expand All @@ -53,13 +73,15 @@ export function buildEventNamesFromEntityName<TNames extends string[]>(
for (let i = 0; i < names.length; i++) {
const name = names[i]
const snakedCaseName = camelToSnakeCase(name).toUpperCase()
const kebabCaseName = lowerCaseFirst(kebabCase(name))

for (const event of Object.values(CommonEvents) as string[]) {
const upperCasedEvent = event.toUpperCase()
events[`${snakedCaseName}_${upperCasedEvent}`] = `${
prefix ? prefix + "." : ""
}${kebabCaseName}.${event}` as `${KebabCase<typeof name>}.${typeof event}`
events[`${snakedCaseName}_${upperCasedEvent}`] =
buildModuleResourceEventName({
prefix,
objectName: name,
action: event,
}) as `${KebabCase<typeof name>}.${typeof event}`
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { MedusaService } from "../medusa-service"
import { model } from "../../dml"
import { MessageAggregator } from "../../event-bus"
import { ModuleJoinerConfig } from "@medusajs/types"

const baseRepoMock = {
serialize: jest.fn().mockImplementation((item) => item),
transaction: (task) => task("transactionManager"),
getFreshManager: jest.fn().mockReturnThis(),
}

const defaultContext = { __type: "MedusaContext", manager: baseRepoMock }
const defaultContext = {
__type: "MedusaContext",
manager: baseRepoMock,
messageAggregator: new MessageAggregator(),
}
const defaultTransactionContext = {
__type: "MedusaContext",
manager: baseRepoMock,
messageAggregator: new MessageAggregator(),
}

describe("Abstract Module Service Factory", () => {
Expand Down Expand Up @@ -58,6 +65,9 @@ describe("Abstract Module Service Factory", () => {
beforeEach(() => {
jest.clearAllMocks()
instance = new medusaService(containerMock)
;(instance as any).__joinerConfig = {
serviceName: "serviceName",
} as ModuleJoinerConfig
})

it("should have retrieve method", async () => {
Expand Down Expand Up @@ -120,6 +130,9 @@ describe("Abstract Module Service Factory", () => {
beforeEach(() => {
jest.clearAllMocks()
instance = new medusaService(containerMock)
;(instance as any).__joinerConfig = {
serviceName: "serviceName",
}
})

it("should have retrieve method for other models", async () => {
Expand Down
7 changes: 5 additions & 2 deletions packages/core/utils/src/modules-sdk/decorators/emit-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ export function EmitEvents(

const argIndex = target.MedusaContextIndex_[propertyKey]
const aggregator = args[argIndex].messageAggregator as MessageAggregator
await target.emitEvents_.apply(this, [aggregator.getMessages(options)])

aggregator.clearMessages()
if (aggregator.count() > 0) {
await target.emitEvents_.apply(this, [aggregator.getMessages(options)])
aggregator.clearMessages()
}

return result
}
}
Expand Down
33 changes: 25 additions & 8 deletions packages/core/utils/src/modules-sdk/event-builder-factory.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Context, EventBusTypes } from "@medusajs/types"
import { buildModuleResourceEventName } from "../event-bus"

// TODO should that move closer to the event bus? and maybe be rename to moduleEventBuilderFactory
// TODO should that move closer to the event bus? and maybe be rename to modulemoduleEventBuilderFactory

/**
*
* Factory function to create event builders for different entities
*
* @example
* const createdFulfillment = eventBuilderFactory({
* const createdFulfillment = moduleEventBuilderFactory({
* source: Modules.FULFILLMENT,
* action: CommonEvents.CREATED,
* object: "fulfillment",
Expand All @@ -24,24 +25,31 @@ import { Context, EventBusTypes } from "@medusajs/types"
* @param eventsEnum
* @param service
*/
export function eventBuilderFactory({
export function moduleEventBuilderFactory({
action,
object,
eventsEnum,
eventName,
source,
}: {
action: string
object: string
eventsEnum: Record<string, string>
/**
* @deprecated use eventName instead
*/
eventsEnum?: Record<string, string>
eventName?: string
source: string
}) {
return function ({
data,
sharedContext,
}: {
data: { id: string }[]
data: { id: string } | { id: string }[]
sharedContext: Context
}) {
data = Array.isArray(data) ? data : [data]

if (!data.length) {
return
}
Expand All @@ -51,16 +59,25 @@ export function eventBuilderFactory({

// The event enums contains event formatted like so [object]_[action] e.g. PRODUCT_CREATED
// We expect the keys of events to be fully uppercased
const eventName =
eventsEnum[`${object.toUpperCase()}_${action.toUpperCase()}`]
let eventName_ = eventsEnum
? eventsEnum[`${object.toUpperCase()}_${action.toUpperCase()}`]
: eventName

if (!eventName_) {
eventName_ = buildModuleResourceEventName({
prefix: source,
objectName: object,
action,
})
}

data.forEach((dataItem) => {
messages.push({
source,
action,
context: sharedContext,
data: { id: dataItem.id },
eventName,
eventName: eventName_,
object,
})
})
Expand Down
Loading

0 comments on commit a734184

Please sign in to comment.