chore: ability to group events on redis event bus (#7655)

* chore: ability to group events on redis event bus

* chore: fix tests

* Update packages/modules/event-bus-redis/src/services/event-bus-redis.ts

Co-authored-by: Adrien de Peretti <adrien.deperetti@gmail.com>

* chore: change shape of input and body data

* chore: fix builds

* chore: address comments

* chore: fix unit test

---------

Co-authored-by: Adrien de Peretti <adrien.deperetti@gmail.com>
This commit is contained in:
Riqwan ThamirandAdrien de Peretti authored and GitHub committed 2024-06-10 22:15:43 +02:00
1 parent 3b8160b564
commit 39ddba2491
24 files changed
+924 -732

No files matched your search

@@ -35,7 +35,7 @@ export const emitEventStep = createStep(
context,
})
await eventBus.emit([message])
await eventBus.emit(message)
},
async (data: void) => {}
)
@@ -1,5 +1,4 @@
import {
EmitData,
EventBusTypes,
IEventBusModuleService,
Message,
@@ -7,24 +6,9 @@ import {
} from "@medusajs/types"
export default class EventBusService implements IEventBusModuleService {
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>
async emit<
T,
TInput extends
| string
| EventBusTypes.EmitData<T>[]
| EventBusTypes.Message<T>[] = string
>(
eventOrData: TInput,
data?: T,
options: Record<string, unknown> = {}
async emit<T>(
data: Message<T> | Message<T>[],
options: Record<string, unknown>
): Promise<void> {}
subscribe(event: string | symbol, subscriber: Subscriber): this {
@@ -1,8 +1,8 @@
import { initModules, InitModulesOptions } from "./init-modules"
import { getDatabaseURL, getMikroOrmWrapper, TestDatabase } from "./database"
import { initModules, InitModulesOptions } from "./init-modules"
import { MockEventBusService } from "."
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
import { MockEventBusService } from "."
export interface SuiteOptions<TService = unknown> {
MikroOrmWrapper: TestDatabase
@@ -75,7 +75,7 @@ export function moduleIntegrationTestRunner<TService = any>({
const moduleOptions_: InitModulesOptions = {
injectedDependencies: {
[ContainerRegistrationKeys.PG_CONNECTION]: connection,
["eventBusModuleService"]: new MockEventBusService(),
eventBusModuleService: new MockEventBusService(),
[ContainerRegistrationKeys.LOGGER]: console,
...injectedDependencies,
},
+16 -24
View File
@@ -1,7 +1,7 @@
import { Context } from "../shared-context"
export type Subscriber<T = unknown> = (
data: T,
export type Subscriber<TData = unknown> = (
data: TData,
eventName: string
) => Promise<void>
@@ -14,36 +14,28 @@ export type SubscriberDescriptor = {
subscriber: Subscriber
}
export type EventHandler<T = unknown> = (
data: T,
export type EventHandler<TData = unknown> = (
data: TData,
eventName: string
) => Promise<void>
export type EmitData<T = unknown> = {
export type EventMetadata = Record<string, unknown> & {
eventGroupId?: string
}
export type MessageBody<TData = unknown> = {
eventName: string
data: T
metadata?: EventMetadata
data: TData
}
export type Message<TData = unknown> = MessageBody<TData> & {
options?: Record<string, unknown>
}
export type MessageBody<T = unknown> = {
metadata: {
source: string
action: string
object: string
eventGroupId?: string
}
data: T
}
export type Message<T = unknown> = {
export type RawMessageFormat<TData = any> = {
eventName: string
body: MessageBody<T>
options?: Record<string, unknown>
}
export type RawMessageFormat<T = any> = {
eventName: string
data: T
data: TData
source: string
object: string
action?: string
@@ -1,13 +1,10 @@
import { EmitData, Message, Subscriber, SubscriberContext } from "./common"
import { Message, Subscriber, SubscriberContext } from "./common"
export interface IEventBusModuleService {
emit<T>(
eventName: string,
data: T,
data: Message<T> | Message<T>[],
options?: Record<string, unknown>
): Promise<void>
emit<T>(data: EmitData<T>[]): Promise<void>
emit<T>(data: Message<T>[]): Promise<void>
subscribe(
eventName: string | symbol,
@@ -1,5 +1,5 @@
import { ITransactionBaseService } from "../transaction-base"
import { EmitData, Message, Subscriber, SubscriberContext } from "./common"
import { Message, Subscriber, SubscriberContext } from "./common"
export interface IEventBusService extends ITransactionBaseService {
subscribe(
@@ -14,7 +14,5 @@ export interface IEventBusService extends ITransactionBaseService {
context?: SubscriberContext
): this
emit<T>(event: string, data: T, options?: unknown): Promise<unknown | void>
emit<T>(data: EmitData<T>[]): Promise<unknown | void>
emit<T>(data: Message<T>[]): Promise<unknown | void>
emit<T>(data: Message<T> | Message<T>[]): Promise<unknown | void>
}
@@ -9,70 +9,60 @@ describe("MessageAggregator", function () {
const aggregator = new MessageAggregator()
aggregator.save({
eventName: "ProductVariant.created",
body: {
metadata: {
source: "ProductService",
action: "created",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 999 },
metadata: {
source: "ProductService",
action: "created",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 999 },
})
aggregator.save({
eventName: "Product.created",
body: {
metadata: {
source: "ProductService",
action: "created",
object: "Product",
eventGroupId: "1",
},
data: { id: 1 },
metadata: {
source: "ProductService",
action: "created",
object: "Product",
eventGroupId: "1",
},
data: { id: 1 },
})
aggregator.save({
eventName: "ProductVariant.created",
body: {
metadata: {
source: "ProductService",
action: "created",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 222 },
metadata: {
source: "ProductService",
action: "created",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 222 },
})
aggregator.save({
eventName: "ProductType.detached",
body: {
metadata: {
source: "ProductService",
action: "detached",
object: "ProductType",
eventGroupId: "1",
},
data: { id: 333 },
metadata: {
source: "ProductService",
action: "detached",
object: "ProductType",
eventGroupId: "1",
},
data: { id: 333 },
})
aggregator.save({
eventName: "ProductVariant.updated",
body: {
metadata: {
source: "ProductService",
action: "updated",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 123 },
metadata: {
source: "ProductService",
action: "updated",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 123 },
})
const format = {
groupBy: ["eventName", "body.metadata.object", "body.metadata.action"],
groupBy: ["eventName", "metadata.object", "metadata.action"],
sortBy: {
"body.metadata.object": ["ProductType", "ProductVariant", "Product"],
"body.data.id": "asc",
"metadata.object": ["ProductType", "ProductVariant", "Product"],
"data.id": "asc",
},
}
@@ -85,72 +75,62 @@ describe("MessageAggregator", function () {
expect(allGroups[0]).toEqual([
{
eventName: "ProductType.detached",
body: {
metadata: {
source: "ProductService",
action: "detached",
object: "ProductType",
eventGroupId: "1",
},
data: { id: 333 },
metadata: {
source: "ProductService",
action: "detached",
object: "ProductType",
eventGroupId: "1",
},
data: { id: 333 },
},
])
expect(allGroups[1]).toEqual([
{
eventName: "ProductVariant.updated",
body: {
metadata: {
source: "ProductService",
action: "updated",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 123 },
metadata: {
source: "ProductService",
action: "updated",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 123 },
},
])
expect(allGroups[2]).toEqual([
{
eventName: "ProductVariant.created",
body: {
metadata: {
source: "ProductService",
action: "created",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 222 },
metadata: {
source: "ProductService",
action: "created",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 222 },
},
{
eventName: "ProductVariant.created",
body: {
metadata: {
source: "ProductService",
action: "created",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 999 },
metadata: {
source: "ProductService",
action: "created",
object: "ProductVariant",
eventGroupId: "1",
},
data: { id: 999 },
},
])
expect(allGroups[3]).toEqual([
{
eventName: "Product.created",
body: {
metadata: {
source: "ProductService",
action: "created",
object: "Product",
eventGroupId: "1",
},
data: { id: 1 },
metadata: {
source: "ProductService",
action: "created",
object: "Product",
eventGroupId: "1",
},
data: { id: 1 },
},
])
})
@@ -45,10 +45,8 @@ export function composeMessage(
return {
eventName,
body: {
metadata,
data,
},
metadata,
data,
options,
}
}
+1 -4
View File
@@ -17,12 +17,9 @@ export abstract class AbstractEventBusModuleService
}
abstract emit<T>(
eventName: string,
data: T,
data: EventBusTypes.Message<T> | EventBusTypes.Message<T>[],
options: Record<string, unknown>
): Promise<void>
abstract emit<T>(data: EventBusTypes.EmitData<T>[]): Promise<void>
abstract emit<T>(data: EventBusTypes.Message<T>[]): Promise<void>
/*
Grouped events are useful when you have distributed transactions
@@ -501,6 +501,7 @@ export function abstractModuleServiceFactory<
await this.eventBusModuleService_?.emit(
softDeletedEntities.map(({ id }) => ({
eventName: `${kebabCase(model.name)}.deleted`,
metadata: { source: "", action: "", object: "" },
data: { id },
}))
)