feat: event aggregator (#6218)
What: - Event Aggregator Util - Preparation for normalizing event in a new format (backward compatible with the current format) - GQL Schema to joiner config and some Entities configured - Link modules emmiting events
This commit is contained in:
1 parent
73fd92a1af
commit
884428a1b5
66 files changed
+1088
-430
No files matched your search
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/event-bus-local": patch
|
||||||
|
"@medusajs/event-bus-redis": patch
|
||||||
|
"@medusajs/link-modules": patch
|
||||||
|
"@medusajs/modules-sdk": patch
|
||||||
|
"@medusajs/inventory": patch
|
||||||
|
"@medusajs/product": patch
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Feat: Event Aggregator
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import { IAuthModuleService } from "@medusajs/types"
|
|
||||||
import { MikroOrmWrapper } from "../../../utils"
|
|
||||||
import { Modules } from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { IAuthModuleService } from "@medusajs/types"
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
import { initModules } from "medusa-test-utils"
|
||||||
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
|
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
|
||||||
import { createAuthUsers } from "../../../__fixtures__/auth-user"
|
import { createAuthUsers } from "../../../__fixtures__/auth-user"
|
||||||
|
import { MikroOrmWrapper } from "../../../utils"
|
||||||
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
|
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
|
||||||
import { initModules } from "medusa-test-utils"
|
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { IAuthModuleService } from "@medusajs/types"
|
|
||||||
import { MikroOrmWrapper } from "../../../utils"
|
|
||||||
import { Modules } from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { IAuthModuleService } from "@medusajs/types"
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
import { initModules } from "medusa-test-utils"
|
||||||
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
|
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
|
||||||
import { createAuthUsers } from "../../../__fixtures__/auth-user"
|
import { createAuthUsers } from "../../../__fixtures__/auth-user"
|
||||||
|
import { MikroOrmWrapper } from "../../../utils"
|
||||||
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
|
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
|
||||||
import { initModules } from "medusa-test-utils"
|
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
import { IAuthModuleService } from "@medusajs/types"
|
import { IAuthModuleService } from "@medusajs/types"
|
||||||
import { MikroOrmWrapper } from "../../../utils"
|
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
|
|
||||||
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
|
|
||||||
import { initModules } from "medusa-test-utils"
|
import { initModules } from "medusa-test-utils"
|
||||||
|
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
|
||||||
|
import { MikroOrmWrapper } from "../../../utils"
|
||||||
|
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
@@ -70,7 +69,10 @@ describe("AuthModuleService - AuthProvider", () => {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const { success, error } = await service.authenticate("notRegistered", {} as any)
|
const { success, error } = await service.authenticate(
|
||||||
|
"notRegistered",
|
||||||
|
{} as any
|
||||||
|
)
|
||||||
|
|
||||||
expect(success).toBe(false)
|
expect(success).toBe(false)
|
||||||
expect(error).toEqual(
|
expect(error).toEqual(
|
||||||
@@ -79,12 +81,9 @@ describe("AuthModuleService - AuthProvider", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("fails to authenticate using a valid provider with an invalid scope", async () => {
|
it("fails to authenticate using a valid provider with an invalid scope", async () => {
|
||||||
const { success, error } = await service.authenticate(
|
const { success, error } = await service.authenticate("emailpass", {
|
||||||
"emailpass",
|
|
||||||
{
|
|
||||||
authScope: "non-existing",
|
authScope: "non-existing",
|
||||||
} as any
|
} as any)
|
||||||
)
|
|
||||||
|
|
||||||
expect(success).toBe(false)
|
expect(success).toBe(false)
|
||||||
expect(error).toEqual(
|
expect(error).toEqual(
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { MedusaContainer } from "@medusajs/modules-sdk"
|
import { MedusaContainer } from "@medusajs/modules-sdk"
|
||||||
import { EmitData, EventBusTypes, Logger, Subscriber } from "@medusajs/types"
|
import {
|
||||||
|
EmitData,
|
||||||
|
EventBusTypes,
|
||||||
|
Logger,
|
||||||
|
Message,
|
||||||
|
Subscriber,
|
||||||
|
} from "@medusajs/types"
|
||||||
import { AbstractEventBusModuleService } from "@medusajs/utils"
|
import { AbstractEventBusModuleService } from "@medusajs/utils"
|
||||||
import { EventEmitter } from "events"
|
import { EventEmitter } from "events"
|
||||||
import { ulid } from "ulid"
|
import { ulid } from "ulid"
|
||||||
@@ -37,14 +43,16 @@ export default class LocalEventBusService extends AbstractEventBusModuleService
|
|||||||
*/
|
*/
|
||||||
async emit<T>(data: EmitData<T>[]): Promise<void>
|
async emit<T>(data: EmitData<T>[]): Promise<void>
|
||||||
|
|
||||||
async emit<T, TInput extends string | EmitData<T>[] = string>(
|
async emit<T>(data: Message<T>[]): Promise<void>
|
||||||
|
|
||||||
|
async emit<T, TInput extends string | EmitData<T>[] | Message<T>[] = string>(
|
||||||
eventOrData: TInput,
|
eventOrData: TInput,
|
||||||
data?: T,
|
data?: T,
|
||||||
options: Record<string, unknown> = {}
|
options: Record<string, unknown> = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const isBulkEmit = Array.isArray(eventOrData)
|
const isBulkEmit = Array.isArray(eventOrData)
|
||||||
|
|
||||||
const events: EmitData[] = isBulkEmit
|
const events: EmitData[] | Message<T>[] = isBulkEmit
|
||||||
? eventOrData
|
? eventOrData
|
||||||
: [{ eventName: eventOrData, data }]
|
: [{ eventName: eventOrData, data }]
|
||||||
|
|
||||||
@@ -61,7 +69,8 @@ export default class LocalEventBusService extends AbstractEventBusModuleService
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eventEmitter_.emit(event.eventName, event.data)
|
const data = (event as EmitData).data ?? (event as Message<T>).body
|
||||||
|
this.eventEmitter_.emit(event.eventName, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { InternalModuleDeclaration } from "@medusajs/modules-sdk"
|
import { InternalModuleDeclaration } from "@medusajs/modules-sdk"
|
||||||
import { EmitData, Logger } from "@medusajs/types"
|
import { EmitData, Logger, Message } from "@medusajs/types"
|
||||||
import { AbstractEventBusModuleService } from "@medusajs/utils"
|
import { AbstractEventBusModuleService, isString } from "@medusajs/utils"
|
||||||
import { BulkJobOptions, JobsOptions, Queue, Worker } from "bullmq"
|
import { BulkJobOptions, JobsOptions, Queue, Worker } from "bullmq"
|
||||||
import { Redis } from "ioredis"
|
import { Redis } from "ioredis"
|
||||||
import { BullJob, EmitOptions, EventBusRedisModuleOptions } from "../types"
|
import { BullJob, EmitOptions, EventBusRedisModuleOptions } from "../types"
|
||||||
@@ -67,7 +67,9 @@ export default class RedisEventBusService extends AbstractEventBusModuleService
|
|||||||
*/
|
*/
|
||||||
async emit<T>(data: EmitData<T>[]): Promise<void>
|
async emit<T>(data: EmitData<T>[]): Promise<void>
|
||||||
|
|
||||||
async emit<T, TInput extends string | EmitData<T>[] = string>(
|
async emit<T>(data: Message<T>[]): Promise<void>
|
||||||
|
|
||||||
|
async emit<T, TInput extends string | EmitData<T>[] | Message<T>[] = string>(
|
||||||
eventNameOrData: TInput,
|
eventNameOrData: TInput,
|
||||||
data?: T,
|
data?: T,
|
||||||
options: BulkJobOptions | JobsOptions = {}
|
options: BulkJobOptions | JobsOptions = {}
|
||||||
@@ -84,10 +86,17 @@ export default class RedisEventBusService extends AbstractEventBusModuleService
|
|||||||
...globalJobOptions,
|
...globalJobOptions,
|
||||||
} as EmitOptions
|
} as EmitOptions
|
||||||
|
|
||||||
|
const dataBody = isString(eventNameOrData)
|
||||||
|
? data ?? (data as Message<T>).body
|
||||||
|
: undefined
|
||||||
|
|
||||||
const events = isBulkEmit
|
const events = isBulkEmit
|
||||||
? eventNameOrData.map((event) => ({
|
? eventNameOrData.map((event) => ({
|
||||||
name: event.eventName,
|
name: event.eventName,
|
||||||
data: { eventName: event.eventName, data: event.data },
|
data: {
|
||||||
|
eventName: event.eventName,
|
||||||
|
data: (event as EmitData).data ?? (event as Message<T>).body,
|
||||||
|
},
|
||||||
opts: {
|
opts: {
|
||||||
...opts,
|
...opts,
|
||||||
// local options
|
// local options
|
||||||
@@ -97,7 +106,7 @@ export default class RedisEventBusService extends AbstractEventBusModuleService
|
|||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
name: eventNameOrData as string,
|
name: eventNameOrData as string,
|
||||||
data: { eventName: eventNameOrData, data },
|
data: { eventName: eventNameOrData, data: dataBody },
|
||||||
opts: {
|
opts: {
|
||||||
...opts,
|
...opts,
|
||||||
// local options
|
// local options
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Modules } from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||||
import { InventoryItem, InventoryLevel, ReservationItem } from "./models"
|
import { InventoryItem, InventoryLevel, ReservationItem } from "./models"
|
||||||
|
import moduleSchema from "./schema"
|
||||||
|
|
||||||
export const joinerConfig: ModuleJoinerConfig = {
|
export const joinerConfig: ModuleJoinerConfig = {
|
||||||
serviceName: Modules.INVENTORY,
|
serviceName: Modules.INVENTORY,
|
||||||
@@ -10,46 +11,30 @@ export const joinerConfig: ModuleJoinerConfig = {
|
|||||||
inventory_level_id: InventoryLevel.name,
|
inventory_level_id: InventoryLevel.name,
|
||||||
reservation_item_id: ReservationItem.name,
|
reservation_item_id: ReservationItem.name,
|
||||||
},
|
},
|
||||||
|
schema: moduleSchema,
|
||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
name: "inventory_items",
|
name: ["inventory_items", "inventory"],
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "inventory",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "inventory_level",
|
|
||||||
args: {
|
args: {
|
||||||
|
entity: "InventoryItem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ["inventory_level", "inventory_levels"],
|
||||||
|
args: {
|
||||||
|
entity: "InventoryLevel",
|
||||||
methodSuffix: "InventoryLevels",
|
methodSuffix: "InventoryLevels",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "inventory_levels",
|
name: [
|
||||||
args: {
|
"reservation",
|
||||||
methodSuffix: "InventoryLevels",
|
"reservations",
|
||||||
},
|
"reservation_item",
|
||||||
},
|
"reservation_items",
|
||||||
{
|
],
|
||||||
name: "reservation_items",
|
|
||||||
args: {
|
|
||||||
methodSuffix: "ReservationItems",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "reservation_item",
|
|
||||||
args: {
|
|
||||||
methodSuffix: "ReservationItems",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "reservation",
|
|
||||||
args: {
|
|
||||||
methodSuffix: "ReservationItems",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "reservations",
|
|
||||||
args: {
|
args: {
|
||||||
|
entity: "ReservationItem",
|
||||||
methodSuffix: "ReservationItems",
|
methodSuffix: "ReservationItems",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import {
|
|||||||
DeleteDateColumn,
|
DeleteDateColumn,
|
||||||
Entity,
|
Entity,
|
||||||
Index,
|
Index,
|
||||||
|
OneToMany,
|
||||||
PrimaryColumn,
|
PrimaryColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from "typeorm"
|
} from "typeorm"
|
||||||
|
import { InventoryLevel } from "./inventory-level"
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class InventoryItem {
|
export class InventoryItem {
|
||||||
@@ -67,6 +69,12 @@ export class InventoryItem {
|
|||||||
@Column({ type: "jsonb", nullable: true })
|
@Column({ type: "jsonb", nullable: true })
|
||||||
metadata: Record<string, unknown> | null
|
metadata: Record<string, unknown> | null
|
||||||
|
|
||||||
|
@OneToMany(
|
||||||
|
() => InventoryLevel,
|
||||||
|
(inventoryLevel) => inventoryLevel.inventory_item
|
||||||
|
)
|
||||||
|
inventory_levels!: InventoryLevel[]
|
||||||
|
|
||||||
@BeforeInsert()
|
@BeforeInsert()
|
||||||
private beforeInsert(): void {
|
private beforeInsert(): void {
|
||||||
this.id = generateEntityId(this.id, "iitem")
|
this.id = generateEntityId(this.id, "iitem")
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ import {
|
|||||||
DeleteDateColumn,
|
DeleteDateColumn,
|
||||||
Entity,
|
Entity,
|
||||||
Index,
|
Index,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
PrimaryColumn,
|
PrimaryColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from "typeorm"
|
} from "typeorm"
|
||||||
|
import { InventoryItem } from "./inventory-item"
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Index(["inventory_item_id", "location_id"], { unique: true })
|
@Index(["inventory_item_id", "location_id"], { unique: true })
|
||||||
@@ -45,6 +48,10 @@ export class InventoryLevel {
|
|||||||
@Column({ type: "jsonb", nullable: true })
|
@Column({ type: "jsonb", nullable: true })
|
||||||
metadata: Record<string, unknown> | null
|
metadata: Record<string, unknown> | null
|
||||||
|
|
||||||
|
@ManyToOne(() => InventoryItem)
|
||||||
|
@JoinColumn({ name: "inventory_item_id" })
|
||||||
|
inventory_item: InventoryItem
|
||||||
|
|
||||||
@BeforeInsert()
|
@BeforeInsert()
|
||||||
private beforeInsert(): void {
|
private beforeInsert(): void {
|
||||||
this.id = generateEntityId(this.id, "ilev")
|
this.id = generateEntityId(this.id, "ilev")
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
export default `
|
||||||
|
scalar DateTime
|
||||||
|
scalar JSON
|
||||||
|
|
||||||
|
type InventoryItem {
|
||||||
|
id: ID!
|
||||||
|
created_at: DateTime!
|
||||||
|
updated_at: DateTime!
|
||||||
|
deleted_at: DateTime
|
||||||
|
sku: String
|
||||||
|
origin_country: String
|
||||||
|
hs_code: String
|
||||||
|
mid_code: String
|
||||||
|
material: String
|
||||||
|
weight: Int
|
||||||
|
length: Int
|
||||||
|
height: Int
|
||||||
|
width: Int
|
||||||
|
requires_shipping: Boolean!
|
||||||
|
description: String
|
||||||
|
title: String
|
||||||
|
thumbnail: String
|
||||||
|
metadata: JSON
|
||||||
|
|
||||||
|
inventory_levels: [InventoryLevel]
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryLevel {
|
||||||
|
id: ID!
|
||||||
|
created_at: DateTime!
|
||||||
|
updated_at: DateTime!
|
||||||
|
deleted_at: DateTime
|
||||||
|
inventory_item_id: String!
|
||||||
|
location_id: String!
|
||||||
|
stocked_quantity: Int!
|
||||||
|
reserved_quantity: Int!
|
||||||
|
incoming_quantity: Int!
|
||||||
|
metadata: JSON
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReservationItem {
|
||||||
|
id: ID!
|
||||||
|
created_at: DateTime!
|
||||||
|
updated_at: DateTime!
|
||||||
|
deleted_at: DateTime
|
||||||
|
line_item_id: String
|
||||||
|
inventory_item_id: String!
|
||||||
|
location_id: String!
|
||||||
|
quantity: Int!
|
||||||
|
external_id: String
|
||||||
|
description: String
|
||||||
|
created_by: String
|
||||||
|
metadata: JSON
|
||||||
|
}
|
||||||
|
`
|
||||||
@@ -12,6 +12,9 @@ export const ProductShippingProfile: ModuleJoinerConfig = {
|
|||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
name: "product_shipping_profile",
|
name: "product_shipping_profile",
|
||||||
|
args: {
|
||||||
|
entity: "LinkProductShippingProfile",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
primaryKeys: ["id", "product_id", "profile_id"],
|
primaryKeys: ["id", "product_id", "profile_id"],
|
||||||
|
|||||||
@@ -17,10 +17,13 @@ export const ProductVariantInventoryItem: ModuleJoinerConfig = {
|
|||||||
},
|
},
|
||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
name: "product_variant_inventory_item",
|
name: [
|
||||||
|
"product_variant_inventory_item",
|
||||||
|
"product_variant_inventory_items",
|
||||||
|
],
|
||||||
|
args: {
|
||||||
|
entity: "LinkProductVariantInventoryItem",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "product_variant_inventory_items",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
primaryKeys: ["id", "variant_id", "inventory_item_id"],
|
primaryKeys: ["id", "variant_id", "inventory_item_id"],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { LINKS } from "../links"
|
|
||||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
|
||||||
import { Modules } from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||||
|
import { LINKS } from "../links"
|
||||||
|
|
||||||
export const ProductVariantPriceSet: ModuleJoinerConfig = {
|
export const ProductVariantPriceSet: ModuleJoinerConfig = {
|
||||||
serviceName: LINKS.ProductVariantPriceSet,
|
serviceName: LINKS.ProductVariantPriceSet,
|
||||||
@@ -11,10 +11,10 @@ export const ProductVariantPriceSet: ModuleJoinerConfig = {
|
|||||||
},
|
},
|
||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
name: "product_variant_price_set",
|
name: ["product_variant_price_set", "product_variant_price_sets"],
|
||||||
|
args: {
|
||||||
|
entity: "LinkProductVariantPriceSet",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "product_variant_price_sets",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
primaryKeys: ["id", "variant_id", "price_set_id"],
|
primaryKeys: ["id", "variant_id", "price_set_id"],
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { InternalModuleDeclaration, MedusaModule } from "@medusajs/modules-sdk"
|
import {
|
||||||
|
InternalModuleDeclaration,
|
||||||
|
MedusaModule,
|
||||||
|
ModuleRegistrationName,
|
||||||
|
} from "@medusajs/modules-sdk"
|
||||||
import {
|
import {
|
||||||
ExternalModuleDeclaration,
|
ExternalModuleDeclaration,
|
||||||
ILinkModule,
|
ILinkModule,
|
||||||
@@ -15,11 +19,16 @@ import {
|
|||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
lowerCaseFirst,
|
lowerCaseFirst,
|
||||||
simpleHash,
|
simpleHash,
|
||||||
|
toPascalCase,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import * as linkDefinitions from "../definitions"
|
import * as linkDefinitions from "../definitions"
|
||||||
import { getMigration } from "../migration"
|
import { getMigration } from "../migration"
|
||||||
import { InitializeModuleInjectableDependencies } from "../types"
|
import { InitializeModuleInjectableDependencies } from "../types"
|
||||||
import { composeLinkName, generateGraphQLSchema } from "../utils"
|
import {
|
||||||
|
composeLinkName,
|
||||||
|
composeTableName,
|
||||||
|
generateGraphQLSchema,
|
||||||
|
} from "../utils"
|
||||||
import { getLinkModuleDefinition } from "./module-definition"
|
import { getLinkModuleDefinition } from "./module-definition"
|
||||||
|
|
||||||
export const initialize = async (
|
export const initialize = async (
|
||||||
@@ -98,7 +107,28 @@ export const initialize = async (
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
definition.schema = generateGraphQLSchema(definition, primary, foreign)
|
const logger =
|
||||||
|
injectedDependencies?.[ContainerRegistrationKeys.LOGGER] ?? console.log
|
||||||
|
|
||||||
|
definition.schema = generateGraphQLSchema(definition, primary, foreign, {
|
||||||
|
logger,
|
||||||
|
})
|
||||||
|
|
||||||
|
definition.alias ??= []
|
||||||
|
for (const alias of definition.alias) {
|
||||||
|
alias.args ??= {}
|
||||||
|
|
||||||
|
alias.args.entity = toPascalCase(
|
||||||
|
"Link_" +
|
||||||
|
(definition.databaseConfig?.tableName ??
|
||||||
|
composeTableName(
|
||||||
|
primary.serviceName,
|
||||||
|
primary.foreignKey,
|
||||||
|
foreign.serviceName,
|
||||||
|
foreign.foreignKey
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const moduleDefinition = getLinkModuleDefinition(
|
const moduleDefinition = getLinkModuleDefinition(
|
||||||
definition,
|
definition,
|
||||||
@@ -110,6 +140,7 @@ export const initialize = async (
|
|||||||
key: serviceKey,
|
key: serviceKey,
|
||||||
registrationName: serviceKey,
|
registrationName: serviceKey,
|
||||||
label: serviceKey,
|
label: serviceKey,
|
||||||
|
dependencies: [ModuleRegistrationName.EVENT_BUS],
|
||||||
defaultModuleDeclaration: {
|
defaultModuleDeclaration: {
|
||||||
scope: MODULE_SCOPE.INTERNAL,
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
resources: injectedDependencies?.[
|
resources: injectedDependencies?.[
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import {
|
|||||||
ModuleJoinerConfig,
|
ModuleJoinerConfig,
|
||||||
ModulesSdkTypes,
|
ModulesSdkTypes,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
import { lowerCaseFirst, simpleHash, toPascalCase } from "@medusajs/utils"
|
||||||
import { asClass, asValue } from "awilix"
|
import { asClass, asValue } from "awilix"
|
||||||
|
import { composeLinkName, composeTableName } from "../utils"
|
||||||
|
|
||||||
export function containerLoader(entity, joinerConfig: ModuleJoinerConfig) {
|
export function containerLoader(entity, joinerConfig: ModuleJoinerConfig) {
|
||||||
return async (
|
return async (
|
||||||
@@ -22,6 +24,29 @@ export function containerLoader(entity, joinerConfig: ModuleJoinerConfig) {
|
|||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const [primary, foreign] = joinerConfig.relationships!
|
const [primary, foreign] = joinerConfig.relationships!
|
||||||
|
|
||||||
|
const serviceName = !joinerConfig.isReadOnlyLink
|
||||||
|
? lowerCaseFirst(
|
||||||
|
joinerConfig.serviceName ??
|
||||||
|
composeLinkName(
|
||||||
|
primary.serviceName,
|
||||||
|
primary.foreignKey,
|
||||||
|
foreign.serviceName,
|
||||||
|
foreign.foreignKey
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: simpleHash(JSON.stringify(joinerConfig.extends))
|
||||||
|
|
||||||
|
const entityName = toPascalCase(
|
||||||
|
"Link_" +
|
||||||
|
(joinerConfig.databaseConfig?.tableName ??
|
||||||
|
composeTableName(
|
||||||
|
primary.serviceName,
|
||||||
|
primary.foreignKey,
|
||||||
|
foreign.serviceName,
|
||||||
|
foreign.foreignKey
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
container.register({
|
container.register({
|
||||||
joinerConfig: asValue(joinerConfig),
|
joinerConfig: asValue(joinerConfig),
|
||||||
primaryKey: asValue(primary.foreignKey.split(",")),
|
primaryKey: asValue(primary.foreignKey.split(",")),
|
||||||
@@ -35,6 +60,8 @@ export function containerLoader(entity, joinerConfig: ModuleJoinerConfig) {
|
|||||||
|
|
||||||
baseRepository: asClass(BaseRepository).singleton(),
|
baseRepository: asClass(BaseRepository).singleton(),
|
||||||
linkRepository: asClass(getLinkRepository(entity)).singleton(),
|
linkRepository: asClass(getLinkRepository(entity)).singleton(),
|
||||||
|
entityName: asValue(entityName),
|
||||||
|
serviceName: asValue(serviceName),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
Context,
|
Context,
|
||||||
DAL,
|
DAL,
|
||||||
FindConfig,
|
FindConfig,
|
||||||
|
IEventBusModuleService,
|
||||||
ILinkModule,
|
ILinkModule,
|
||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
ModuleJoinerConfig,
|
ModuleJoinerConfig,
|
||||||
@@ -9,6 +10,7 @@ import {
|
|||||||
SoftDeleteReturn,
|
SoftDeleteReturn,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
|
CommonEvents,
|
||||||
InjectManager,
|
InjectManager,
|
||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
MapToConfig,
|
MapToConfig,
|
||||||
@@ -24,14 +26,20 @@ import { shouldForceTransaction } from "../utils"
|
|||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
linkService: LinkService<any>
|
linkService: LinkService<any>
|
||||||
|
eventBusModuleService?: IEventBusModuleService
|
||||||
primaryKey: string | string[]
|
primaryKey: string | string[]
|
||||||
foreignKey: string
|
foreignKey: string
|
||||||
extraFields: string[]
|
extraFields: string[]
|
||||||
|
entityName: string
|
||||||
|
serviceName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class LinkModuleService<TLink> implements ILinkModule {
|
export default class LinkModuleService<TLink> implements ILinkModule {
|
||||||
protected baseRepository_: DAL.RepositoryService
|
protected baseRepository_: DAL.RepositoryService
|
||||||
protected readonly linkService_: LinkService<TLink>
|
protected readonly linkService_: LinkService<TLink>
|
||||||
|
protected readonly eventBusModuleService_?: IEventBusModuleService
|
||||||
|
protected readonly entityName_: string
|
||||||
|
protected readonly serviceName_: string
|
||||||
protected primaryKey_: string[]
|
protected primaryKey_: string[]
|
||||||
protected foreignKey_: string
|
protected foreignKey_: string
|
||||||
protected extraFields_: string[]
|
protected extraFields_: string[]
|
||||||
@@ -40,17 +48,23 @@ export default class LinkModuleService<TLink> implements ILinkModule {
|
|||||||
{
|
{
|
||||||
baseRepository,
|
baseRepository,
|
||||||
linkService,
|
linkService,
|
||||||
|
eventBusModuleService,
|
||||||
primaryKey,
|
primaryKey,
|
||||||
foreignKey,
|
foreignKey,
|
||||||
extraFields,
|
extraFields,
|
||||||
|
entityName,
|
||||||
|
serviceName,
|
||||||
}: InjectedDependencies,
|
}: InjectedDependencies,
|
||||||
readonly moduleDeclaration: InternalModuleDeclaration
|
readonly moduleDeclaration: InternalModuleDeclaration
|
||||||
) {
|
) {
|
||||||
this.baseRepository_ = baseRepository
|
this.baseRepository_ = baseRepository
|
||||||
this.linkService_ = linkService
|
this.linkService_ = linkService
|
||||||
|
this.eventBusModuleService_ = eventBusModuleService
|
||||||
this.primaryKey_ = !Array.isArray(primaryKey) ? [primaryKey] : primaryKey
|
this.primaryKey_ = !Array.isArray(primaryKey) ? [primaryKey] : primaryKey
|
||||||
this.foreignKey_ = foreignKey
|
this.foreignKey_ = foreignKey
|
||||||
this.extraFields_ = extraFields
|
this.extraFields_ = extraFields
|
||||||
|
this.entityName_ = entityName
|
||||||
|
this.serviceName_ = serviceName
|
||||||
}
|
}
|
||||||
|
|
||||||
__joinerConfig(): ModuleJoinerConfig {
|
__joinerConfig(): ModuleJoinerConfig {
|
||||||
@@ -188,6 +202,21 @@ export default class LinkModuleService<TLink> implements ILinkModule {
|
|||||||
|
|
||||||
const links = await this.linkService_.create(data, sharedContext)
|
const links = await this.linkService_.create(data, sharedContext)
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<Record<string, unknown>>(
|
||||||
|
(data as { id: unknown }[]).map(({ id }) => ({
|
||||||
|
eventName: this.entityName_ + "." + CommonEvents.ATTACHED,
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: this.serviceName_,
|
||||||
|
action: CommonEvents.ATTACHED,
|
||||||
|
object: this.entityName_,
|
||||||
|
eventGroupId: sharedContext.eventGroupId,
|
||||||
|
},
|
||||||
|
data: { id },
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
return await this.baseRepository_.serialize<object[]>(links)
|
return await this.baseRepository_.serialize<object[]>(links)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,6 +253,22 @@ export default class LinkModuleService<TLink> implements ILinkModule {
|
|||||||
this.validateFields(data)
|
this.validateFields(data)
|
||||||
|
|
||||||
await this.linkService_.delete(data, sharedContext)
|
await this.linkService_.delete(data, sharedContext)
|
||||||
|
|
||||||
|
const allData = Array.isArray(data) ? data : [data]
|
||||||
|
await this.eventBusModuleService_?.emit<Record<string, unknown>>(
|
||||||
|
allData.map(({ id }) => ({
|
||||||
|
eventName: this.entityName_ + "." + CommonEvents.DETACHED,
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: this.serviceName_,
|
||||||
|
action: CommonEvents.DETACHED,
|
||||||
|
object: this.entityName_,
|
||||||
|
eventGroupId: sharedContext.eventGroupId,
|
||||||
|
},
|
||||||
|
data: { id },
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async softDelete(
|
async softDelete(
|
||||||
@@ -233,7 +278,10 @@ export default class LinkModuleService<TLink> implements ILinkModule {
|
|||||||
): Promise<Record<string, unknown[]> | void> {
|
): Promise<Record<string, unknown[]> | void> {
|
||||||
this.validateFields(data)
|
this.validateFields(data)
|
||||||
|
|
||||||
let [, cascadedEntitiesMap] = await this.softDelete_(data, sharedContext)
|
let [deletedEntities, cascadedEntitiesMap] = await this.softDelete_(
|
||||||
|
data,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
const pk = this.primaryKey_.join(",")
|
const pk = this.primaryKey_.join(",")
|
||||||
const entityNameToLinkableKeysMap: MapToConfig = {
|
const entityNameToLinkableKeysMap: MapToConfig = {
|
||||||
@@ -256,6 +304,21 @@ export default class LinkModuleService<TLink> implements ILinkModule {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<Record<string, unknown>>(
|
||||||
|
(deletedEntities as { id: string }[]).map(({ id }) => ({
|
||||||
|
eventName: this.entityName_ + "." + CommonEvents.DETACHED,
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: this.serviceName_,
|
||||||
|
action: CommonEvents.DETACHED,
|
||||||
|
object: this.entityName_,
|
||||||
|
eventGroupId: sharedContext.eventGroupId,
|
||||||
|
},
|
||||||
|
data: { id },
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
return mappedCascadedEntitiesMap ? mappedCascadedEntitiesMap : void 0
|
return mappedCascadedEntitiesMap ? mappedCascadedEntitiesMap : void 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +326,7 @@ export default class LinkModuleService<TLink> implements ILinkModule {
|
|||||||
protected async softDelete_(
|
protected async softDelete_(
|
||||||
data: any,
|
data: any,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<[string[], Record<string, string[]>]> {
|
): Promise<[object[], Record<string, string[]>]> {
|
||||||
return await this.linkService_.softDelete(data, sharedContext)
|
return await this.linkService_.softDelete(data, sharedContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +337,10 @@ export default class LinkModuleService<TLink> implements ILinkModule {
|
|||||||
): Promise<Record<string, unknown[]> | void> {
|
): Promise<Record<string, unknown[]> | void> {
|
||||||
this.validateFields(data)
|
this.validateFields(data)
|
||||||
|
|
||||||
let [, cascadedEntitiesMap] = await this.restore_(data, sharedContext)
|
let [restoredEntities, cascadedEntitiesMap] = await this.restore_(
|
||||||
|
data,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
const pk = this.primaryKey_.join(",")
|
const pk = this.primaryKey_.join(",")
|
||||||
const entityNameToLinkableKeysMap: MapToConfig = {
|
const entityNameToLinkableKeysMap: MapToConfig = {
|
||||||
@@ -297,6 +363,21 @@ export default class LinkModuleService<TLink> implements ILinkModule {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<Record<string, unknown>>(
|
||||||
|
(restoredEntities as { id: string }[]).map(({ id }) => ({
|
||||||
|
eventName: this.entityName_ + "." + CommonEvents.ATTACHED,
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: this.serviceName_,
|
||||||
|
action: CommonEvents.ATTACHED,
|
||||||
|
object: this.entityName_,
|
||||||
|
eventGroupId: sharedContext.eventGroupId,
|
||||||
|
},
|
||||||
|
data: { id },
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
return mappedCascadedEntitiesMap ? mappedCascadedEntitiesMap : void 0
|
return mappedCascadedEntitiesMap ? mappedCascadedEntitiesMap : void 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +385,7 @@ export default class LinkModuleService<TLink> implements ILinkModule {
|
|||||||
async restore_(
|
async restore_(
|
||||||
data: any,
|
data: any,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<[string[], Record<string, string[]>]> {
|
): Promise<[object[], Record<string, string[]>]> {
|
||||||
return await this.linkService_.restore(data, sharedContext)
|
return await this.linkService_.restore(data, sharedContext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ export default class LinkService<TEntity> {
|
|||||||
async softDelete(
|
async softDelete(
|
||||||
data: any,
|
data: any,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<[string[], Record<string, string[]>]> {
|
): Promise<[object[], Record<string, string[]>]> {
|
||||||
const filter = {}
|
const filter = {}
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
filter[key] = { $in: Array.isArray(data[key]) ? data[key] : [data[key]] }
|
filter[key] = { $in: Array.isArray(data[key]) ? data[key] : [data[key]] }
|
||||||
@@ -104,7 +104,7 @@ export default class LinkService<TEntity> {
|
|||||||
async restore(
|
async restore(
|
||||||
data: any,
|
data: any,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<[string[], Record<string, string[]>]> {
|
): Promise<[object[], Record<string, string[]>]> {
|
||||||
const filter = {}
|
const filter = {}
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
filter[key] = { $in: Array.isArray(data[key]) ? data[key] : [data[key]] }
|
filter[key] = { $in: Array.isArray(data[key]) ? data[key] : [data[key]] }
|
||||||
|
|||||||
@@ -16,8 +16,15 @@ class EventBus extends EventBusUtils.AbstractEventBusModuleService {
|
|||||||
options: Record<string, unknown>
|
options: Record<string, unknown>
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
async emit<T>(data: EventBusTypes.EmitData<T>[]): Promise<void>
|
async emit<T>(data: EventBusTypes.EmitData<T>[]): Promise<void>
|
||||||
|
async emit<T>(data: EventBusTypes.Message<T>[]): Promise<void>
|
||||||
|
|
||||||
async emit<T, TInput extends string | EventBusTypes.EmitData<T>[] = string>(
|
async emit<
|
||||||
|
T,
|
||||||
|
TInput extends
|
||||||
|
| string
|
||||||
|
| EventBusTypes.EmitData<T>[]
|
||||||
|
| EventBusTypes.Message<T>[] = string
|
||||||
|
>(
|
||||||
eventOrData: TInput,
|
eventOrData: TInput,
|
||||||
data?: T,
|
data?: T,
|
||||||
options: Record<string, unknown> = {}
|
options: Record<string, unknown> = {}
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ export default {
|
|||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
name: ["publishable_api_key", "publishable_api_keys"],
|
name: ["publishable_api_key", "publishable_api_keys"],
|
||||||
args: { entity: "PublishableApiKey" },
|
args: {
|
||||||
|
entity: "PublishableApiKey",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as ModuleJoinerConfig
|
} as ModuleJoinerConfig
|
||||||
@@ -20,10 +20,10 @@ export default {
|
|||||||
`,
|
`,
|
||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
name: "shipping_profile",
|
name: ["shipping_profile", "shipping_profiles"],
|
||||||
|
args: {
|
||||||
|
entity: "ShippingProfile",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "shipping_profiles",
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as ModuleJoinerConfig
|
} as ModuleJoinerConfig
|
||||||
@@ -188,7 +188,7 @@ export class SubscriberLoader {
|
|||||||
|
|
||||||
const events = Array.isArray(event) ? event : [event]
|
const events = Array.isArray(event) ? event : [event]
|
||||||
|
|
||||||
const subscriber: Subscriber<T> = async (data: T, eventName: string) => {
|
const subscriber = async (data: T, eventName: string) => {
|
||||||
return handler({
|
return handler({
|
||||||
eventName,
|
eventName,
|
||||||
data,
|
data,
|
||||||
@@ -200,7 +200,7 @@ export class SubscriberLoader {
|
|||||||
const subscriberId = this.inferIdentifier(fileName, config, handler)
|
const subscriberId = this.inferIdentifier(fileName, config, handler)
|
||||||
|
|
||||||
for (const e of events) {
|
for (const e of events) {
|
||||||
eventBusService.subscribe(e, subscriber as Subscriber<unknown>, {
|
eventBusService.subscribe(e, subscriber as Subscriber, {
|
||||||
...(config.context ?? {}),
|
...(config.context ?? {}),
|
||||||
subscriberId,
|
subscriberId,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -128,7 +128,10 @@ export const loadMedusaApp = async (
|
|||||||
return medusaApp
|
return medusaApp
|
||||||
}
|
}
|
||||||
|
|
||||||
container.register("remoteLink", asValue(medusaApp.link))
|
container.register(
|
||||||
|
ContainerRegistrationKeys.REMOTE_LINK,
|
||||||
|
asValue(medusaApp.link)
|
||||||
|
)
|
||||||
container.register(
|
container.register(
|
||||||
ContainerRegistrationKeys.REMOTE_QUERY,
|
ContainerRegistrationKeys.REMOTE_QUERY,
|
||||||
asValue(medusaApp.query)
|
asValue(medusaApp.query)
|
||||||
@@ -143,7 +146,7 @@ export const loadMedusaApp = async (
|
|||||||
|
|
||||||
// Register all unresolved modules as undefined to be present in the container with undefined value by defaul
|
// Register all unresolved modules as undefined to be present in the container with undefined value by defaul
|
||||||
// but still resolvable
|
// but still resolvable
|
||||||
for (const [, moduleDefinition] of Object.entries(ModulesDefinition)) {
|
for (const moduleDefinition of Object.values(ModulesDefinition)) {
|
||||||
if (!container.hasRegistration(moduleDefinition.registrationName)) {
|
if (!container.hasRegistration(moduleDefinition.registrationName)) {
|
||||||
container.register(moduleDefinition.registrationName, asValue(undefined))
|
container.register(moduleDefinition.registrationName, asValue(undefined))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { EventBusTypes, Logger } from "@medusajs/types"
|
import { EmitData, EventBusTypes, Logger, Message } from "@medusajs/types"
|
||||||
import { DatabaseErrorCode, EventBusUtils } from "@medusajs/utils"
|
import { DatabaseErrorCode, EventBusUtils } from "@medusajs/utils"
|
||||||
|
import { EOL } from "os"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
import { TransactionBaseService } from "../interfaces"
|
import { TransactionBaseService } from "../interfaces"
|
||||||
import { StagedJob } from "../models"
|
import { StagedJob } from "../models"
|
||||||
|
import { FindConfig } from "../types/common"
|
||||||
import { ConfigModule } from "../types/global"
|
import { ConfigModule } from "../types/global"
|
||||||
import { isString } from "../utils"
|
import { isString } from "../utils"
|
||||||
import { sleep } from "../utils/sleep"
|
import { sleep } from "../utils/sleep"
|
||||||
import StagedJobService from "./staged-job"
|
import StagedJobService from "./staged-job"
|
||||||
import { FindConfig } from "../types/common"
|
|
||||||
import { EOL } from "os"
|
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
stagedJobService: StagedJobService
|
stagedJobService: StagedJobService
|
||||||
@@ -118,6 +118,8 @@ export default class EventBusService
|
|||||||
*/
|
*/
|
||||||
async emit<T>(data: EventBusTypes.EmitData<T>[]): Promise<StagedJob[] | void>
|
async emit<T>(data: EventBusTypes.EmitData<T>[]): Promise<StagedJob[] | void>
|
||||||
|
|
||||||
|
async emit<T>(data: EventBusTypes.Message<T>[]): Promise<StagedJob[] | void>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls all subscribers when an event occurs.
|
* Calls all subscribers when an event occurs.
|
||||||
* @param {string} eventName - the name of the event to be process.
|
* @param {string} eventName - the name of the event to be process.
|
||||||
@@ -133,7 +135,10 @@ export default class EventBusService
|
|||||||
|
|
||||||
async emit<
|
async emit<
|
||||||
T,
|
T,
|
||||||
TInput extends string | EventBusTypes.EmitData<T>[] = string,
|
TInput extends
|
||||||
|
| string
|
||||||
|
| EventBusTypes.EmitData<T>[]
|
||||||
|
| EventBusTypes.Message<T>[] = string,
|
||||||
TResult = TInput extends EventBusTypes.EmitData<T>[]
|
TResult = TInput extends EventBusTypes.EmitData<T>[]
|
||||||
? StagedJob[]
|
? StagedJob[]
|
||||||
: StagedJob
|
: StagedJob
|
||||||
@@ -144,16 +149,19 @@ export default class EventBusService
|
|||||||
): Promise<TResult | void> {
|
): Promise<TResult | void> {
|
||||||
const manager = this.activeManager_
|
const manager = this.activeManager_
|
||||||
const isBulkEmit = !isString(eventNameOrData)
|
const isBulkEmit = !isString(eventNameOrData)
|
||||||
|
const dataBody = isString(eventNameOrData)
|
||||||
|
? data ?? (data as Message<T>).body
|
||||||
|
: undefined
|
||||||
const events: EventBusTypes.EmitData[] = isBulkEmit
|
const events: EventBusTypes.EmitData[] = isBulkEmit
|
||||||
? eventNameOrData.map((event) => ({
|
? eventNameOrData.map((event) => ({
|
||||||
eventName: event.eventName,
|
eventName: event.eventName,
|
||||||
data: event.data,
|
data: (event as EmitData).data ?? (event as Message<T>).body.data,
|
||||||
options: event.options,
|
options: event.options,
|
||||||
}))
|
}))
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
eventName: eventNameOrData,
|
eventName: eventNameOrData,
|
||||||
data: data,
|
data: dataBody,
|
||||||
options: options,
|
options: options,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,32 +1,40 @@
|
|||||||
import { EntityManager, In } from "typeorm"
|
|
||||||
import {
|
import {
|
||||||
IEventBusService,
|
IEventBusService,
|
||||||
IInventoryService,
|
IInventoryService,
|
||||||
|
IStockLocationService,
|
||||||
InventoryItemDTO,
|
InventoryItemDTO,
|
||||||
InventoryLevelDTO,
|
InventoryLevelDTO,
|
||||||
IStockLocationService,
|
RemoteQueryFunction,
|
||||||
ReservationItemDTO,
|
ReservationItemDTO,
|
||||||
ReserveQuantityContext,
|
ReserveQuantityContext,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
FlagRouter,
|
||||||
|
MedusaError,
|
||||||
|
MedusaV2Flag,
|
||||||
|
isDefined,
|
||||||
|
promiseAll,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import { EntityManager, In } from "typeorm"
|
||||||
import { LineItem, Product, ProductVariant } from "../models"
|
import { LineItem, Product, ProductVariant } from "../models"
|
||||||
import { isDefined, MedusaError, promiseAll } from "@medusajs/utils"
|
|
||||||
import { PricedProduct, PricedVariant } from "../types/pricing"
|
import { PricedProduct, PricedVariant } from "../types/pricing"
|
||||||
|
|
||||||
|
import { TransactionBaseService } from "../interfaces"
|
||||||
import { ProductVariantInventoryItem } from "../models/product-variant-inventory-item"
|
import { ProductVariantInventoryItem } from "../models/product-variant-inventory-item"
|
||||||
|
import { getSetDifference } from "../utils/diff-set"
|
||||||
import ProductVariantService from "./product-variant"
|
import ProductVariantService from "./product-variant"
|
||||||
import SalesChannelInventoryService from "./sales-channel-inventory"
|
import SalesChannelInventoryService from "./sales-channel-inventory"
|
||||||
import SalesChannelLocationService from "./sales-channel-location"
|
import SalesChannelLocationService from "./sales-channel-location"
|
||||||
import { TransactionBaseService } from "../interfaces"
|
|
||||||
import { getSetDifference } from "../utils/diff-set"
|
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
manager: EntityManager
|
manager: EntityManager
|
||||||
salesChannelLocationService: SalesChannelLocationService
|
salesChannelLocationService: SalesChannelLocationService
|
||||||
salesChannelInventoryService: SalesChannelInventoryService
|
salesChannelInventoryService: SalesChannelInventoryService
|
||||||
productVariantService: ProductVariantService
|
productVariantService: ProductVariantService
|
||||||
stockLocationService: IStockLocationService
|
|
||||||
inventoryService: IInventoryService
|
|
||||||
eventBusService: IEventBusService
|
eventBusService: IEventBusService
|
||||||
|
featureFlagRouter: FlagRouter
|
||||||
|
remoteQuery: RemoteQueryFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
type AvailabilityContext = {
|
type AvailabilityContext = {
|
||||||
@@ -42,6 +50,8 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
protected readonly salesChannelInventoryService_: SalesChannelInventoryService
|
protected readonly salesChannelInventoryService_: SalesChannelInventoryService
|
||||||
protected readonly productVariantService_: ProductVariantService
|
protected readonly productVariantService_: ProductVariantService
|
||||||
protected readonly eventBusService_: IEventBusService
|
protected readonly eventBusService_: IEventBusService
|
||||||
|
protected readonly featureFlagRouter_: FlagRouter
|
||||||
|
protected readonly remoteQuery_: RemoteQueryFunction
|
||||||
|
|
||||||
protected get inventoryService_(): IInventoryService {
|
protected get inventoryService_(): IInventoryService {
|
||||||
return this.__container__.inventoryService
|
return this.__container__.inventoryService
|
||||||
@@ -56,6 +66,8 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
salesChannelInventoryService,
|
salesChannelInventoryService,
|
||||||
productVariantService,
|
productVariantService,
|
||||||
eventBusService,
|
eventBusService,
|
||||||
|
featureFlagRouter,
|
||||||
|
remoteQuery,
|
||||||
}: InjectedDependencies) {
|
}: InjectedDependencies) {
|
||||||
// eslint-disable-next-line prefer-rest-params
|
// eslint-disable-next-line prefer-rest-params
|
||||||
super(arguments[0])
|
super(arguments[0])
|
||||||
@@ -64,6 +76,8 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
this.salesChannelInventoryService_ = salesChannelInventoryService
|
this.salesChannelInventoryService_ = salesChannelInventoryService
|
||||||
this.productVariantService_ = productVariantService
|
this.productVariantService_ = productVariantService
|
||||||
this.eventBusService_ = eventBusService
|
this.eventBusService_ = eventBusService
|
||||||
|
this.featureFlagRouter_ = featureFlagRouter
|
||||||
|
this.remoteQuery_ = remoteQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -307,7 +321,19 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify that variant exists
|
// Verify that variant exists
|
||||||
const variants = await this.productVariantService_
|
let variants
|
||||||
|
if (this.featureFlagRouter_.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||||
|
variants = await this.remoteQuery_(
|
||||||
|
remoteQueryObjectFromString({
|
||||||
|
entryPoint: "variants",
|
||||||
|
variables: {
|
||||||
|
id: data.map((d) => d.variantId),
|
||||||
|
},
|
||||||
|
fields: ["id"],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
variants = await this.productVariantService_
|
||||||
.withTransaction(this.activeManager_)
|
.withTransaction(this.activeManager_)
|
||||||
.list(
|
.list(
|
||||||
{
|
{
|
||||||
@@ -317,6 +343,7 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
select: ["id"],
|
select: ["id"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const foundVariantIds = new Set(variants.map((v) => v.id))
|
const foundVariantIds = new Set(variants.map((v) => v.id))
|
||||||
const requestedVariantIds = new Set(data.map((v) => v.variantId))
|
const requestedVariantIds = new Set(data.map((v) => v.variantId))
|
||||||
@@ -404,7 +431,11 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
): tc is ProductVariantInventoryItem => !!tc
|
): tc is ProductVariantInventoryItem => !!tc
|
||||||
)
|
)
|
||||||
|
|
||||||
return await variantInventoryRepo.save(toCreate)
|
const createdVariantInventoryItems = await variantInventoryRepo.save(
|
||||||
|
toCreate
|
||||||
|
)
|
||||||
|
|
||||||
|
return createdVariantInventoryItems
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
import { mergeTypeDefs } from "@graphql-tools/merge"
|
|
||||||
import { makeExecutableSchema } from "@graphql-tools/schema"
|
|
||||||
import { getFieldsAndRelations } from "../../utils"
|
|
||||||
|
|
||||||
const userModule = `
|
|
||||||
type User {
|
|
||||||
id: ID!
|
|
||||||
name: String!
|
|
||||||
blabla: WHATEVER
|
|
||||||
}
|
|
||||||
|
|
||||||
type Post {
|
|
||||||
author: User!
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const postModule = `
|
|
||||||
type Post {
|
|
||||||
id: ID!
|
|
||||||
title: String!
|
|
||||||
date: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type User {
|
|
||||||
posts: [Post!]!
|
|
||||||
}
|
|
||||||
|
|
||||||
type WHATEVER {
|
|
||||||
random_field: String
|
|
||||||
post: Post
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const mergedSchema = mergeTypeDefs([userModule, postModule])
|
|
||||||
const schema = makeExecutableSchema({
|
|
||||||
typeDefs: mergedSchema,
|
|
||||||
})
|
|
||||||
|
|
||||||
const types = schema.getTypeMap()
|
|
||||||
|
|
||||||
describe("getFieldsAndRelations", function () {
|
|
||||||
it("Should get all fields of a given entity", async function () {
|
|
||||||
const fields = getFieldsAndRelations(types, "User")
|
|
||||||
expect(fields).toEqual(expect.arrayContaining(["id", "name"]))
|
|
||||||
})
|
|
||||||
|
|
||||||
it("Should get all fields of a given entity and a relation", async function () {
|
|
||||||
const fields = getFieldsAndRelations(types, "User", ["posts"])
|
|
||||||
expect(fields).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
"posts.id",
|
|
||||||
"posts.title",
|
|
||||||
"posts.date",
|
|
||||||
])
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("Should get all fields of a given entity and many relations", async function () {
|
|
||||||
const fields = getFieldsAndRelations(types, "User", [
|
|
||||||
"posts",
|
|
||||||
"blabla",
|
|
||||||
"blabla.post",
|
|
||||||
])
|
|
||||||
expect(fields).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
"posts.id",
|
|
||||||
"posts.title",
|
|
||||||
"posts.date",
|
|
||||||
"blabla.random_field",
|
|
||||||
"blabla.post.id",
|
|
||||||
"blabla.post.title",
|
|
||||||
"blabla.post.date",
|
|
||||||
])
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("Should get all fields of a given entity and many relations limited to the relations given", async function () {
|
|
||||||
const fields = getFieldsAndRelations(types, "User", ["posts", "blabla"])
|
|
||||||
expect(fields).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
"posts.id",
|
|
||||||
"posts.title",
|
|
||||||
"posts.date",
|
|
||||||
"blabla.random_field",
|
|
||||||
])
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
ModuleJoinerConfig,
|
ModuleJoinerConfig,
|
||||||
ModuleServiceInitializeOptions,
|
ModuleServiceInitializeOptions,
|
||||||
RemoteJoinerQuery,
|
RemoteJoinerQuery,
|
||||||
|
RemoteQueryFunction,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
@@ -41,6 +42,7 @@ export type RunMigrationFn = (
|
|||||||
|
|
||||||
export type MedusaModuleConfig = {
|
export type MedusaModuleConfig = {
|
||||||
[key: string | Modules]:
|
[key: string | Modules]:
|
||||||
|
| string
|
||||||
| boolean
|
| boolean
|
||||||
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
|
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
|
||||||
}
|
}
|
||||||
@@ -182,10 +184,7 @@ function registerCustomJoinerConfigs(servicesConfig: ModuleJoinerConfig[]) {
|
|||||||
export type MedusaAppOutput = {
|
export type MedusaAppOutput = {
|
||||||
modules: Record<string, LoadedModule | LoadedModule[]>
|
modules: Record<string, LoadedModule | LoadedModule[]>
|
||||||
link: RemoteLink | undefined
|
link: RemoteLink | undefined
|
||||||
query: (
|
query: RemoteQueryFunction
|
||||||
query: string | RemoteJoinerQuery | object,
|
|
||||||
variables?: Record<string, unknown>
|
|
||||||
) => Promise<any>
|
|
||||||
entitiesMap?: Record<string, any>
|
entitiesMap?: Record<string, any>
|
||||||
notFound?: Record<string, Record<string, string>>
|
notFound?: Record<string, Record<string, string>>
|
||||||
runMigrations: RunMigrationFn
|
runMigrations: RunMigrationFn
|
||||||
@@ -201,6 +200,7 @@ export async function MedusaApp({
|
|||||||
linkModules,
|
linkModules,
|
||||||
remoteFetchData,
|
remoteFetchData,
|
||||||
injectedDependencies,
|
injectedDependencies,
|
||||||
|
onApplicationStartCb,
|
||||||
}: {
|
}: {
|
||||||
sharedContainer?: MedusaContainer
|
sharedContainer?: MedusaContainer
|
||||||
sharedResourcesConfig?: SharedResources
|
sharedResourcesConfig?: SharedResources
|
||||||
@@ -212,6 +212,7 @@ export async function MedusaApp({
|
|||||||
linkModules?: ModuleJoinerConfig | ModuleJoinerConfig[]
|
linkModules?: ModuleJoinerConfig | ModuleJoinerConfig[]
|
||||||
remoteFetchData?: RemoteFetchDataCallback
|
remoteFetchData?: RemoteFetchDataCallback
|
||||||
injectedDependencies?: any
|
injectedDependencies?: any
|
||||||
|
onApplicationStartCb?: () => void
|
||||||
} = {}): Promise<{
|
} = {}): Promise<{
|
||||||
modules: Record<string, LoadedModule | LoadedModule[]>
|
modules: Record<string, LoadedModule | LoadedModule[]>
|
||||||
link: RemoteLink | undefined
|
link: RemoteLink | undefined
|
||||||
@@ -355,6 +356,6 @@ export async function MedusaApp({
|
|||||||
runMigrations,
|
runMigrations,
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await MedusaModule.onApplicationStart()
|
MedusaModule.onApplicationStart(onApplicationStartCb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,8 @@ export type LinkModuleBootstrapOptions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MedusaModule {
|
export class MedusaModule {
|
||||||
private static instances_: Map<string, any> = new Map()
|
private static instances_: Map<string, { [key: string]: IModuleService }> =
|
||||||
|
new Map()
|
||||||
private static modules_: Map<string, ModuleAlias[]> = new Map()
|
private static modules_: Map<string, ModuleAlias[]> = new Map()
|
||||||
private static loading_: Map<string, Promise<any>> = new Map()
|
private static loading_: Map<string, Promise<any>> = new Map()
|
||||||
private static joinerConfig_: Map<string, ModuleJoinerConfig> = new Map()
|
private static joinerConfig_: Map<string, ModuleJoinerConfig> = new Map()
|
||||||
@@ -87,12 +88,15 @@ export class MedusaModule {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public static onApplicationStart(): void {
|
public static onApplicationStart(onApplicationStartCb?: () => void): void {
|
||||||
for (const instances of MedusaModule.instances_.values()) {
|
for (const instances of MedusaModule.instances_.values()) {
|
||||||
for (const instance of Object.values(instances) as IModuleService[]) {
|
for (const instance of Object.values(instances) as IModuleService[]) {
|
||||||
if (instance?.__hooks) {
|
if (instance?.__hooks) {
|
||||||
instance.__hooks?.onApplicationStart
|
instance.__hooks?.onApplicationStart
|
||||||
?.bind(instance)()
|
?.bind(instance)()
|
||||||
|
.then(() => {
|
||||||
|
onApplicationStartCb?.()
|
||||||
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// The module should handle this and log it
|
// The module should handle this and log it
|
||||||
return void 0
|
return void 0
|
||||||
@@ -217,7 +221,9 @@ export class MedusaModule {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (MedusaModule.instances_.has(hashKey)) {
|
if (MedusaModule.instances_.has(hashKey)) {
|
||||||
return MedusaModule.instances_.get(hashKey)
|
return MedusaModule.instances_.get(hashKey)! as {
|
||||||
|
[key: string]: T
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MedusaModule.loading_.has(hashKey)) {
|
if (MedusaModule.loading_.has(hashKey)) {
|
||||||
@@ -249,7 +255,12 @@ export class MedusaModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = createMedusaContainer({}, sharedContainer)
|
// TODO: Only do that while legacy modules sharing the manager exists then remove the ternary in favor of createMedusaContainer({}, globalContainer)
|
||||||
|
const container =
|
||||||
|
modDeclaration.scope === MODULE_SCOPE.INTERNAL &&
|
||||||
|
modDeclaration.resources === MODULE_RESOURCE_TYPE.SHARED
|
||||||
|
? sharedContainer ?? createMedusaContainer()
|
||||||
|
: createMedusaContainer({}, sharedContainer)
|
||||||
|
|
||||||
if (injectedDependencies) {
|
if (injectedDependencies) {
|
||||||
for (const service in injectedDependencies) {
|
for (const service in injectedDependencies) {
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import { GraphQLNamedType, GraphQLObjectType, isObjectType } from "graphql"
|
|
||||||
|
|
||||||
export function getFieldsAndRelations(
|
|
||||||
schemaTypeMap: { [key: string]: GraphQLNamedType },
|
|
||||||
typeName: string,
|
|
||||||
relations: string[] = []
|
|
||||||
) {
|
|
||||||
const result: string[] = []
|
|
||||||
|
|
||||||
function traverseFields(typeName, prefix) {
|
|
||||||
const type = schemaTypeMap[typeName]
|
|
||||||
|
|
||||||
if (!(type instanceof GraphQLObjectType)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const fields = type.getFields()
|
|
||||||
|
|
||||||
for (const fieldName in fields) {
|
|
||||||
const field = fields[fieldName]
|
|
||||||
let fieldType = field.type as any
|
|
||||||
|
|
||||||
while (fieldType.ofType) {
|
|
||||||
fieldType = fieldType.ofType
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isObjectType(fieldType)) {
|
|
||||||
result.push(`${prefix}${fieldName}`)
|
|
||||||
} else if (relations.includes(prefix + fieldName)) {
|
|
||||||
traverseFields(fieldType.name, `${prefix}${fieldName}.`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
traverseFields(typeName, "")
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from "./clean-graphql-schema"
|
export * from "./clean-graphql-schema"
|
||||||
export * from "./get-fields-and-relations"
|
|
||||||
export * from "./graphql-schema-to-fields"
|
export * from "./graphql-schema-to-fields"
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
PriceSet,
|
PriceSet,
|
||||||
PriceSetMoneyAmount,
|
PriceSetMoneyAmount,
|
||||||
} from "@models"
|
} from "@models"
|
||||||
|
import schema from "./schema"
|
||||||
|
|
||||||
export const LinkableKeys = {
|
export const LinkableKeys = {
|
||||||
money_amount_id: MoneyAmount.name,
|
money_amount_id: MoneyAmount.name,
|
||||||
@@ -32,45 +33,30 @@ export const joinerConfig: ModuleJoinerConfig = {
|
|||||||
serviceName: Modules.PRICING,
|
serviceName: Modules.PRICING,
|
||||||
primaryKeys: ["id"],
|
primaryKeys: ["id"],
|
||||||
linkableKeys: LinkableKeys,
|
linkableKeys: LinkableKeys,
|
||||||
|
schema,
|
||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
name: "price_set",
|
name: ["price_set", "price_sets"],
|
||||||
|
args: {
|
||||||
|
entity: "PriceSet",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "price_sets",
|
name: ["money_amount", "money_amounts"],
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "money_amount",
|
|
||||||
args: {
|
args: {
|
||||||
methodSuffix: "MoneyAmounts",
|
methodSuffix: "MoneyAmounts",
|
||||||
|
entity: "MoneyAmount",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "money_amounts",
|
name: ["currency", "currencies"],
|
||||||
args: {
|
|
||||||
methodSuffix: "MoneyAmounts",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "currency",
|
|
||||||
args: {
|
args: {
|
||||||
methodSuffix: "Currencies",
|
methodSuffix: "Currencies",
|
||||||
|
entity: "Currency",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "currencies",
|
name: ["price_list", "price_lists"],
|
||||||
args: {
|
|
||||||
methodSuffix: "Currencies",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "price_list",
|
|
||||||
args: {
|
|
||||||
methodSuffix: "PriceLists",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "price_lists",
|
|
||||||
args: {
|
args: {
|
||||||
methodSuffix: "PriceLists",
|
methodSuffix: "PriceLists",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export const schema = `
|
||||||
|
type PriceSet {
|
||||||
|
id: String!
|
||||||
|
money_amounts: [MoneyAmount]
|
||||||
|
}
|
||||||
|
|
||||||
|
type MoneyAmount {
|
||||||
|
id: String!
|
||||||
|
currency_code: String
|
||||||
|
currency: Currency
|
||||||
|
amount: Float
|
||||||
|
min_quantity: Float
|
||||||
|
max_quantity: Float
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default schema
|
||||||
@@ -1,19 +1,27 @@
|
|||||||
import {
|
import {
|
||||||
|
EmitData,
|
||||||
EventBusTypes,
|
EventBusTypes,
|
||||||
IEventBusModuleService,
|
IEventBusModuleService,
|
||||||
|
Message,
|
||||||
Subscriber,
|
Subscriber,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
|
||||||
export class EventBusService implements IEventBusModuleService {
|
export class EventBusService implements IEventBusModuleService {
|
||||||
async emit<T>(
|
emit<T>(
|
||||||
eventName: string,
|
eventName: string,
|
||||||
data: T,
|
data: T,
|
||||||
options: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
|
emit<T>(data: EmitData<T>[]): Promise<void>
|
||||||
|
emit<T>(data: Message<T>[]): Promise<void>
|
||||||
|
|
||||||
async emit<T>(data: EventBusTypes.EmitData<T>[]): Promise<void>
|
async emit<
|
||||||
|
T,
|
||||||
async emit<T, TInput extends string | EventBusTypes.EmitData<T>[] = string>(
|
TInput extends
|
||||||
|
| string
|
||||||
|
| EventBusTypes.EmitData<T>[]
|
||||||
|
| EventBusTypes.Message<T>[] = string
|
||||||
|
>(
|
||||||
eventOrData: TInput,
|
eventOrData: TInput,
|
||||||
data?: T,
|
data?: T,
|
||||||
options: Record<string, unknown> = {}
|
options: Record<string, unknown> = {}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
|||||||
import { IProductModuleService } from "@medusajs/types"
|
import { IProductModuleService } from "@medusajs/types"
|
||||||
import { kebabCase } from "@medusajs/utils"
|
import { kebabCase } from "@medusajs/utils"
|
||||||
import { knex } from "knex"
|
import { knex } from "knex"
|
||||||
|
import { initModules } from "medusa-test-utils"
|
||||||
import * as CustomRepositories from "../__fixtures__/module"
|
import * as CustomRepositories from "../__fixtures__/module"
|
||||||
import {
|
import {
|
||||||
buildProductAndRelationsData,
|
buildProductAndRelationsData,
|
||||||
@@ -9,7 +10,6 @@ import {
|
|||||||
} from "../__fixtures__/product"
|
} from "../__fixtures__/product"
|
||||||
import { productsData } from "../__fixtures__/product/data"
|
import { productsData } from "../__fixtures__/product/data"
|
||||||
import { DB_URL, TestDatabase } from "../utils"
|
import { DB_URL, TestDatabase } from "../utils"
|
||||||
import { initModules } from "medusa-test-utils"
|
|
||||||
import { getInitModuleConfig } from "../utils/get-init-module-config"
|
import { getInitModuleConfig } from "../utils/get-init-module-config"
|
||||||
|
|
||||||
const sharedPgConnection = knex<any, any>({
|
const sharedPgConnection = knex<any, any>({
|
||||||
@@ -238,7 +238,23 @@ describe("Product module", function () {
|
|||||||
thumbnail: images[0],
|
thumbnail: images[0],
|
||||||
})
|
})
|
||||||
|
|
||||||
const products = await module.create([data])
|
const productsCreated = await module.create([data])
|
||||||
|
|
||||||
|
const products = await module.list(
|
||||||
|
{ id: productsCreated[0].id },
|
||||||
|
{
|
||||||
|
relations: [
|
||||||
|
"images",
|
||||||
|
"categories",
|
||||||
|
"variants",
|
||||||
|
"variants.options",
|
||||||
|
"options",
|
||||||
|
"options.values",
|
||||||
|
"tags",
|
||||||
|
"type",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
expect(products).toHaveLength(1)
|
expect(products).toHaveLength(1)
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -1,12 +1,12 @@
|
|||||||
|
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
||||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||||
import { Product, ProductCategory } from "@models"
|
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
import { getInitModuleConfig, TestDatabase } from "../../../utils"
|
import { Product, ProductCategory } from "@models"
|
||||||
|
import { initModules } from "medusa-test-utils"
|
||||||
|
import { EventBusService } from "../../../__fixtures__/event-bus"
|
||||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||||
import { productCategoriesRankData } from "../../../__fixtures__/product-category/data"
|
import { productCategoriesRankData } from "../../../__fixtures__/product-category/data"
|
||||||
import { EventBusService } from "../../../__fixtures__/event-bus"
|
import { TestDatabase, getInitModuleConfig } from "../../../utils"
|
||||||
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
|
||||||
import { initModules } from "medusa-test-utils/dist"
|
|
||||||
|
|
||||||
describe("ProductModuleService product categories", () => {
|
describe("ProductModuleService product categories", () => {
|
||||||
let service: IProductModuleService
|
let service: IProductModuleService
|
||||||
|
|||||||
+5
-5
@@ -1,9 +1,9 @@
|
|||||||
import { getInitModuleConfig, TestDatabase } from "../../../utils"
|
|
||||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
|
||||||
import { Product, ProductTag } from "@models"
|
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
|
||||||
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
||||||
import { initModules } from "medusa-test-utils/dist"
|
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||||
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
import { Product, ProductTag } from "@models"
|
||||||
|
import { initModules } from "medusa-test-utils"
|
||||||
|
import { TestDatabase, getInitModuleConfig } from "../../../utils"
|
||||||
|
|
||||||
describe("ProductModuleService product tags", () => {
|
describe("ProductModuleService product tags", () => {
|
||||||
let service: IProductModuleService
|
let service: IProductModuleService
|
||||||
|
|||||||
+5
-5
@@ -1,9 +1,9 @@
|
|||||||
import { getInitModuleConfig, TestDatabase } from "../../../utils"
|
|
||||||
import { IProductModuleService } from "@medusajs/types"
|
|
||||||
import { ProductType } from "@models"
|
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
|
||||||
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
||||||
import { initModules } from "medusa-test-utils/dist"
|
import { IProductModuleService } from "@medusajs/types"
|
||||||
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
import { ProductType } from "@models"
|
||||||
|
import { initModules } from "medusa-test-utils"
|
||||||
|
import { getInitModuleConfig, TestDatabase } from "../../../utils"
|
||||||
|
|
||||||
describe("ProductModuleService product types", () => {
|
describe("ProductModuleService product types", () => {
|
||||||
let service: IProductModuleService
|
let service: IProductModuleService
|
||||||
|
|||||||
+5
-5
@@ -1,9 +1,9 @@
|
|||||||
import { getInitModuleConfig, TestDatabase } from "../../../utils"
|
|
||||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
|
||||||
import { Product, ProductVariant } from "@models"
|
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
|
||||||
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
||||||
import { initModules } from "medusa-test-utils/dist"
|
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||||
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
import { Product, ProductVariant } from "@models"
|
||||||
|
import { initModules } from "medusa-test-utils"
|
||||||
|
import { getInitModuleConfig, TestDatabase } from "../../../utils"
|
||||||
|
|
||||||
describe("ProductModuleService product variants", () => {
|
describe("ProductModuleService product variants", () => {
|
||||||
let service: IProductModuleService
|
let service: IProductModuleService
|
||||||
|
|||||||
+19
-3
@@ -13,13 +13,13 @@ import {
|
|||||||
ProductVariant,
|
ProductVariant,
|
||||||
} from "@models"
|
} from "@models"
|
||||||
|
|
||||||
|
import { initModules } from "medusa-test-utils"
|
||||||
import { initialize } from "../../../../src"
|
import { initialize } from "../../../../src"
|
||||||
import { EventBusService } from "../../../__fixtures__/event-bus"
|
import { EventBusService } from "../../../__fixtures__/event-bus"
|
||||||
import { createCollections, createTypes } from "../../../__fixtures__/product"
|
import { createCollections, createTypes } from "../../../__fixtures__/product"
|
||||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||||
import { buildProductAndRelationsData } from "../../../__fixtures__/product/data/create-product"
|
import { buildProductAndRelationsData } from "../../../__fixtures__/product/data/create-product"
|
||||||
import { DB_URL, getInitModuleConfig, TestDatabase } from "../../../utils"
|
import { DB_URL, TestDatabase, getInitModuleConfig } from "../../../utils"
|
||||||
import { initModules } from "medusa-test-utils"
|
|
||||||
|
|
||||||
const beforeEach_ = async () => {
|
const beforeEach_ = async () => {
|
||||||
await TestDatabase.setupDatabase()
|
await TestDatabase.setupDatabase()
|
||||||
@@ -584,7 +584,23 @@ describe("ProductModuleService products", function () {
|
|||||||
thumbnail: images[0],
|
thumbnail: images[0],
|
||||||
})
|
})
|
||||||
|
|
||||||
const products = await module.create([data])
|
const productsCreated = await module.create([data])
|
||||||
|
|
||||||
|
const products = await module.list(
|
||||||
|
{ id: productsCreated[0].id },
|
||||||
|
{
|
||||||
|
relations: [
|
||||||
|
"images",
|
||||||
|
"categories",
|
||||||
|
"variants",
|
||||||
|
"variants.options",
|
||||||
|
"options",
|
||||||
|
"options.values",
|
||||||
|
"tags",
|
||||||
|
"type",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
expect(products).toHaveLength(1)
|
expect(products).toHaveLength(1)
|
||||||
expect(products[0].images).toHaveLength(1)
|
expect(products[0].images).toHaveLength(1)
|
||||||
|
|||||||
@@ -43,92 +43,57 @@ export const joinerConfig: ModuleJoinerConfig = {
|
|||||||
schema: moduleSchema,
|
schema: moduleSchema,
|
||||||
alias: [
|
alias: [
|
||||||
{
|
{
|
||||||
name: "product",
|
name: ["product", "products"],
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "products",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "variant",
|
|
||||||
args: {
|
args: {
|
||||||
|
entity: "Product",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: ["variant", "variants"],
|
||||||
|
args: {
|
||||||
|
entity: "ProductVariant",
|
||||||
methodSuffix: "Variants",
|
methodSuffix: "Variants",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "variants",
|
name: ["product_option", "product_options"],
|
||||||
args: {
|
|
||||||
methodSuffix: "Variants",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "product_option",
|
|
||||||
args: {
|
args: {
|
||||||
|
entity: "ProductOption",
|
||||||
methodSuffix: "Options",
|
methodSuffix: "Options",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "product_options",
|
name: ["product_type", "product_types"],
|
||||||
args: {
|
|
||||||
methodSuffix: "Options",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "product_type",
|
|
||||||
args: {
|
args: {
|
||||||
|
entity: "ProductType",
|
||||||
methodSuffix: "Types",
|
methodSuffix: "Types",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "product_types",
|
name: ["product_image", "product_images"],
|
||||||
args: {
|
|
||||||
methodSuffix: "Types",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "product_image",
|
|
||||||
args: {
|
args: {
|
||||||
|
entity: "ProductImage",
|
||||||
methodSuffix: "Images",
|
methodSuffix: "Images",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "product_images",
|
name: ["product_tag", "product_tags"],
|
||||||
args: {
|
|
||||||
methodSuffix: "Images",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "product_tag",
|
|
||||||
args: {
|
args: {
|
||||||
|
entity: "ProductTag",
|
||||||
methodSuffix: "Tags",
|
methodSuffix: "Tags",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "product_tags",
|
name: ["product_collection", "product_collections"],
|
||||||
args: {
|
|
||||||
methodSuffix: "Tags",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "product_collection",
|
|
||||||
args: {
|
args: {
|
||||||
|
entity: "ProductCollection",
|
||||||
methodSuffix: "Collections",
|
methodSuffix: "Collections",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "product_collections",
|
name: ["product_category", "product_categories"],
|
||||||
args: {
|
|
||||||
methodSuffix: "Collections",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "product_category",
|
|
||||||
args: {
|
|
||||||
methodSuffix: "Categories",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "product_categories",
|
|
||||||
args: {
|
args: {
|
||||||
|
entity: "ProductCategory",
|
||||||
methodSuffix: "Categories",
|
methodSuffix: "Categories",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Index,
|
Index,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
|
OnInit,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
@@ -14,8 +15,8 @@ import {
|
|||||||
Unique,
|
Unique,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
|
|
||||||
import Product from "./product"
|
|
||||||
import { DAL } from "@medusajs/types"
|
import { DAL } from "@medusajs/types"
|
||||||
|
import Product from "./product"
|
||||||
|
|
||||||
type OptionalFields = DAL.SoftDeletableEntityDateColumns
|
type OptionalFields = DAL.SoftDeletableEntityDateColumns
|
||||||
|
|
||||||
@@ -85,6 +86,11 @@ class ProductCategory {
|
|||||||
@ManyToMany(() => Product, (product) => product.categories)
|
@ManyToMany(() => Product, (product) => product.categories)
|
||||||
products = new Collection<Product>(this)
|
products = new Collection<Product>(this)
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
async onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "pcat")
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
async onCreate(args: EventArgs<ProductCategory>) {
|
async onCreate(args: EventArgs<ProductCategory>) {
|
||||||
this.id = generateEntityId(this.id, "pcat")
|
this.id = generateEntityId(this.id, "pcat")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
Entity,
|
Entity,
|
||||||
Filter,
|
Filter,
|
||||||
Index,
|
Index,
|
||||||
|
OnInit,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
@@ -11,9 +12,9 @@ import {
|
|||||||
Unique,
|
Unique,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
|
|
||||||
|
import { DAL } from "@medusajs/types"
|
||||||
import { DALUtils, generateEntityId, kebabCase } from "@medusajs/utils"
|
import { DALUtils, generateEntityId, kebabCase } from "@medusajs/utils"
|
||||||
import Product from "./product"
|
import Product from "./product"
|
||||||
import { DAL } from "@medusajs/types"
|
|
||||||
|
|
||||||
type OptionalRelations = "products"
|
type OptionalRelations = "products"
|
||||||
type OptionalFields = DAL.SoftDeletableEntityDateColumns
|
type OptionalFields = DAL.SoftDeletableEntityDateColumns
|
||||||
@@ -61,6 +62,11 @@ class ProductCollection {
|
|||||||
@Property({ columnType: "timestamptz", nullable: true })
|
@Property({ columnType: "timestamptz", nullable: true })
|
||||||
deleted_at?: Date
|
deleted_at?: Date
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "pcol")
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
onCreate() {
|
onCreate() {
|
||||||
this.id = generateEntityId(this.id, "pcol")
|
this.id = generateEntityId(this.id, "pcol")
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ import {
|
|||||||
Filter,
|
Filter,
|
||||||
Index,
|
Index,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
|
OnInit,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Property,
|
Property,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
|
|
||||||
|
import { DAL } from "@medusajs/types"
|
||||||
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
||||||
import Product from "./product"
|
import Product from "./product"
|
||||||
import { DAL } from "@medusajs/types"
|
|
||||||
|
|
||||||
type OptionalRelations = "products"
|
type OptionalRelations = "products"
|
||||||
type OptionalFields = DAL.SoftDeletableEntityDateColumns
|
type OptionalFields = DAL.SoftDeletableEntityDateColumns
|
||||||
@@ -54,6 +55,11 @@ class ProductImage {
|
|||||||
@ManyToMany(() => Product, (product) => product.images)
|
@ManyToMany(() => Product, (product) => product.images)
|
||||||
products = new Collection<Product>(this)
|
products = new Collection<Product>(this)
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "img")
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
onCreate() {
|
onCreate() {
|
||||||
this.id = generateEntityId(this.id, "img")
|
this.id = generateEntityId(this.id, "img")
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
|
import { DAL } from "@medusajs/types"
|
||||||
|
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
BeforeCreate,
|
BeforeCreate,
|
||||||
Entity,
|
Entity,
|
||||||
Filter,
|
Filter,
|
||||||
Index,
|
Index,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
|
OnInit,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Property,
|
Property,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import { ProductOption, ProductVariant } from "./index"
|
import { ProductOption, ProductVariant } from "./index"
|
||||||
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
|
||||||
import { DAL } from "@medusajs/types"
|
|
||||||
|
|
||||||
type OptionalFields =
|
type OptionalFields =
|
||||||
| "allow_backorder"
|
| "allow_backorder"
|
||||||
@@ -72,6 +73,11 @@ class ProductOptionValue {
|
|||||||
@Property({ columnType: "timestamptz", nullable: true })
|
@Property({ columnType: "timestamptz", nullable: true })
|
||||||
deleted_at?: Date
|
deleted_at?: Date
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "optval")
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
beforeCreate() {
|
beforeCreate() {
|
||||||
this.id = generateEntityId(this.id, "optval")
|
this.id = generateEntityId(this.id, "optval")
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
Filter,
|
Filter,
|
||||||
Index,
|
Index,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
|
OnInit,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
@@ -70,6 +71,11 @@ class ProductOption {
|
|||||||
@Property({ columnType: "timestamptz", nullable: true })
|
@Property({ columnType: "timestamptz", nullable: true })
|
||||||
deleted_at?: Date
|
deleted_at?: Date
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "opt")
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
beforeCreate() {
|
beforeCreate() {
|
||||||
this.id = generateEntityId(this.id, "opt")
|
this.id = generateEntityId(this.id, "opt")
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ import {
|
|||||||
Filter,
|
Filter,
|
||||||
Index,
|
Index,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
|
OnInit,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Property,
|
Property,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
|
|
||||||
|
import { DAL } from "@medusajs/types"
|
||||||
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
||||||
import Product from "./product"
|
import Product from "./product"
|
||||||
import { DAL } from "@medusajs/types"
|
|
||||||
|
|
||||||
type OptionalRelations = "products"
|
type OptionalRelations = "products"
|
||||||
type OptionalFields = DAL.SoftDeletableEntityDateColumns
|
type OptionalFields = DAL.SoftDeletableEntityDateColumns
|
||||||
@@ -53,6 +54,11 @@ class ProductTag {
|
|||||||
@ManyToMany(() => Product, (product) => product.tags)
|
@ManyToMany(() => Product, (product) => product.tags)
|
||||||
products = new Collection<Product>(this)
|
products = new Collection<Product>(this)
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "ptag")
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
onCreate() {
|
onCreate() {
|
||||||
this.id = generateEntityId(this.id, "ptag")
|
this.id = generateEntityId(this.id, "ptag")
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import {
|
|||||||
Entity,
|
Entity,
|
||||||
Filter,
|
Filter,
|
||||||
Index,
|
Index,
|
||||||
|
OnInit,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Property,
|
Property,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
|
|
||||||
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
|
||||||
import { DAL } from "@medusajs/types"
|
import { DAL } from "@medusajs/types"
|
||||||
|
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
||||||
|
|
||||||
type OptionalFields = DAL.SoftDeletableEntityDateColumns
|
type OptionalFields = DAL.SoftDeletableEntityDateColumns
|
||||||
|
|
||||||
@@ -46,6 +47,11 @@ class ProductType {
|
|||||||
@Property({ columnType: "timestamptz", nullable: true })
|
@Property({ columnType: "timestamptz", nullable: true })
|
||||||
deleted_at?: Date
|
deleted_at?: Date
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "ptyp")
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
onCreate() {
|
onCreate() {
|
||||||
this.id = generateEntityId(this.id, "ptyp")
|
this.id = generateEntityId(this.id, "ptyp")
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DAL } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
DALUtils,
|
DALUtils,
|
||||||
generateEntityId,
|
generateEntityId,
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
Filter,
|
Filter,
|
||||||
Index,
|
Index,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
|
OnInit,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
@@ -19,7 +21,6 @@ import {
|
|||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import { Product } from "@models"
|
import { Product } from "@models"
|
||||||
import ProductOptionValue from "./product-option-value"
|
import ProductOptionValue from "./product-option-value"
|
||||||
import { DAL } from "@medusajs/types"
|
|
||||||
|
|
||||||
type OptionalFields =
|
type OptionalFields =
|
||||||
| "allow_backorder"
|
| "allow_backorder"
|
||||||
@@ -152,6 +153,11 @@ class ProductVariant {
|
|||||||
})
|
})
|
||||||
options = new Collection<ProductOptionValue>(this)
|
options = new Collection<ProductOptionValue>(this)
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "variant")
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
onCreate() {
|
onCreate() {
|
||||||
this.id = generateEntityId(this.id, "variant")
|
this.id = generateEntityId(this.id, "variant")
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ import {
|
|||||||
ManyToMany,
|
ManyToMany,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
|
OnInit,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
Property,
|
Property,
|
||||||
Unique,
|
Unique,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
|
|
||||||
|
import { DAL } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
DALUtils,
|
DALUtils,
|
||||||
generateEntityId,
|
generateEntityId,
|
||||||
@@ -22,12 +24,11 @@ import {
|
|||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import ProductCategory from "./product-category"
|
import ProductCategory from "./product-category"
|
||||||
import ProductCollection from "./product-collection"
|
import ProductCollection from "./product-collection"
|
||||||
|
import ProductImage from "./product-image"
|
||||||
import ProductOption from "./product-option"
|
import ProductOption from "./product-option"
|
||||||
import ProductTag from "./product-tag"
|
import ProductTag from "./product-tag"
|
||||||
import ProductType from "./product-type"
|
import ProductType from "./product-type"
|
||||||
import ProductVariant from "./product-variant"
|
import ProductVariant from "./product-variant"
|
||||||
import ProductImage from "./product-image"
|
|
||||||
import { DAL } from "@medusajs/types"
|
|
||||||
|
|
||||||
type OptionalRelations = "collection" | "type"
|
type OptionalRelations = "collection" | "type"
|
||||||
type OptionalFields =
|
type OptionalFields =
|
||||||
@@ -176,6 +177,11 @@ class Product {
|
|||||||
@Property({ columnType: "jsonb", nullable: true })
|
@Property({ columnType: "jsonb", nullable: true })
|
||||||
metadata?: Record<string, unknown> | null
|
metadata?: Record<string, unknown> | null
|
||||||
|
|
||||||
|
@OnInit()
|
||||||
|
onInit() {
|
||||||
|
this.id = generateEntityId(this.id, "prod")
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
beforeCreate() {
|
beforeCreate() {
|
||||||
this.id = generateEntityId(this.id, "prod")
|
this.id = generateEntityId(this.id, "prod")
|
||||||
|
|||||||
@@ -42,12 +42,6 @@ import {
|
|||||||
ModulesSdkUtils,
|
ModulesSdkUtils,
|
||||||
promiseAll,
|
promiseAll,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { entityNameToLinkableKeysMap, joinerConfig } from "./../joiner-config"
|
|
||||||
import { ProductEventData, ProductEvents } from "../types/services/product"
|
|
||||||
import {
|
|
||||||
ProductCategoryEventData,
|
|
||||||
ProductCategoryEvents,
|
|
||||||
} from "../types/services/product-category"
|
|
||||||
import {
|
import {
|
||||||
ProductCategoryServiceTypes,
|
ProductCategoryServiceTypes,
|
||||||
ProductCollectionServiceTypes,
|
ProductCollectionServiceTypes,
|
||||||
@@ -55,6 +49,12 @@ import {
|
|||||||
ProductServiceTypes,
|
ProductServiceTypes,
|
||||||
ProductVariantServiceTypes,
|
ProductVariantServiceTypes,
|
||||||
} from "@types"
|
} from "@types"
|
||||||
|
import { ProductEventData, ProductEvents } from "../types/services/product"
|
||||||
|
import {
|
||||||
|
ProductCategoryEventData,
|
||||||
|
ProductCategoryEvents,
|
||||||
|
} from "../types/services/product-category"
|
||||||
|
import { entityNameToLinkableKeysMap, joinerConfig } from "./../joiner-config"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
@@ -371,52 +371,56 @@ export default class ProductModuleService<
|
|||||||
async createTags(
|
async createTags(
|
||||||
data: ProductTypes.CreateProductTagDTO[],
|
data: ProductTypes.CreateProductTagDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
): Promise<ProductTypes.ProductTagDTO[]> {
|
||||||
const productTags = await this.productTagService_.create(
|
const productTags = await this.productTagService_.create(
|
||||||
data,
|
data,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productTags))
|
return await this.baseRepository_.serialize(productTags, { populate: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
async updateTags(
|
async updateTags(
|
||||||
data: ProductTypes.UpdateProductTagDTO[],
|
data: ProductTypes.UpdateProductTagDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
): Promise<ProductTypes.ProductTagDTO[]> {
|
||||||
const productTags = await this.productTagService_.update(
|
const productTags = await this.productTagService_.update(
|
||||||
data,
|
data,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productTags))
|
return await this.baseRepository_.serialize(productTags, { populate: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
async createTypes(
|
async createTypes(
|
||||||
data: ProductTypes.CreateProductTypeDTO[],
|
data: ProductTypes.CreateProductTypeDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
): Promise<ProductTypes.ProductTypeDTO[]> {
|
||||||
const productTypes = await this.productTypeService_.create(
|
const productTypes = await this.productTypeService_.create(
|
||||||
data,
|
data,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productTypes))
|
return await this.baseRepository_.serialize(productTypes, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
async updateTypes(
|
async updateTypes(
|
||||||
data: ProductTypes.UpdateProductTypeDTO[],
|
data: ProductTypes.UpdateProductTypeDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
): Promise<ProductTypes.ProductTypeDTO[]> {
|
||||||
const productTypes = await this.productTypeService_.update(
|
const productTypes = await this.productTypeService_.update(
|
||||||
data,
|
data,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productTypes))
|
return await this.baseRepository_.serialize(productTypes, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
@@ -476,7 +480,7 @@ export default class ProductModuleService<
|
|||||||
async updateCollections(
|
async updateCollections(
|
||||||
data: ProductTypes.UpdateProductCollectionDTO[],
|
data: ProductTypes.UpdateProductCollectionDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
): Promise<ProductTypes.ProductCollectionDTO[]> {
|
||||||
const productCollections = await this.productCollectionService_.update(
|
const productCollections = await this.productCollectionService_.update(
|
||||||
data,
|
data,
|
||||||
sharedContext
|
sharedContext
|
||||||
@@ -492,14 +496,16 @@ export default class ProductModuleService<
|
|||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productCollections))
|
return await this.baseRepository_.serialize(productCollections, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
async createCategory(
|
async createCategory(
|
||||||
data: ProductCategoryServiceTypes.CreateProductCategoryDTO,
|
data: ProductCategoryServiceTypes.CreateProductCategoryDTO,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
): Promise<ProductTypes.ProductCategoryDTO> {
|
||||||
const productCategory = await this.productCategoryService_.create(
|
const productCategory = await this.productCategoryService_.create(
|
||||||
data,
|
data,
|
||||||
sharedContext
|
sharedContext
|
||||||
@@ -510,7 +516,9 @@ export default class ProductModuleService<
|
|||||||
{ id: productCategory.id }
|
{ id: productCategory.id }
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productCategory))
|
return await this.baseRepository_.serialize(productCategory, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
@@ -518,7 +526,7 @@ export default class ProductModuleService<
|
|||||||
categoryId: string,
|
categoryId: string,
|
||||||
data: ProductCategoryServiceTypes.UpdateProductCategoryDTO,
|
data: ProductCategoryServiceTypes.UpdateProductCategoryDTO,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
): Promise<ProductTypes.ProductCategoryDTO> {
|
||||||
const productCategory = await this.productCategoryService_.update(
|
const productCategory = await this.productCategoryService_.update(
|
||||||
categoryId,
|
categoryId,
|
||||||
data,
|
data,
|
||||||
@@ -530,7 +538,9 @@ export default class ProductModuleService<
|
|||||||
{ id: productCategory.id }
|
{ id: productCategory.id }
|
||||||
)
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productCategory))
|
return await this.baseRepository_.serialize(productCategory, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
|
|||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
import { IPromotionModuleService } from "@medusajs/types"
|
import { IPromotionModuleService } from "@medusajs/types"
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
|
import { initModules } from "medusa-test-utils"
|
||||||
import { createCampaigns } from "../../../__fixtures__/campaigns"
|
import { createCampaigns } from "../../../__fixtures__/campaigns"
|
||||||
import { MikroOrmWrapper } from "../../../utils"
|
import { MikroOrmWrapper } from "../../../utils"
|
||||||
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
|
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
|
||||||
import { initModules } from "medusa-test-utils/dist"
|
|
||||||
import { Modules } from "@medusajs/modules-sdk"
|
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,51 @@
|
|||||||
export type Subscriber<T = unknown> = (
|
export type Subscriber<T = unknown> = (
|
||||||
data: T,
|
data: T,
|
||||||
eventName: string
|
eventName: string
|
||||||
) => Promise<void>
|
) => Promise<void>
|
||||||
|
|
||||||
export type SubscriberContext = {
|
export type SubscriberContext = {
|
||||||
subscriberId: string
|
subscriberId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SubscriberDescriptor = {
|
export type SubscriberDescriptor = {
|
||||||
id: string
|
id: string
|
||||||
subscriber: Subscriber
|
subscriber: Subscriber
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventHandler<T = unknown> = (
|
export type EventHandler<T = unknown> = (
|
||||||
data: T,
|
data: T,
|
||||||
eventName: string
|
eventName: string
|
||||||
) => Promise<void>
|
) => Promise<void>
|
||||||
|
|
||||||
export type EmitData<T = unknown> = {
|
export type EmitData<T = unknown> = {
|
||||||
eventName: string
|
eventName: string
|
||||||
data: T
|
data: T
|
||||||
options?: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MessageBody<T = unknown> = {
|
||||||
|
metadata: {
|
||||||
|
service: string
|
||||||
|
action: string
|
||||||
|
object: string
|
||||||
|
eventGroupId?: string
|
||||||
|
}
|
||||||
|
data: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Message<T = unknown> = {
|
||||||
|
eventName: string
|
||||||
|
body: MessageBody<T>
|
||||||
|
options?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MessageFormat<T = unknown> = {
|
||||||
|
eventName: string
|
||||||
|
metadata: {
|
||||||
|
service: string
|
||||||
|
action: string
|
||||||
|
object: string
|
||||||
|
eventGroupId?: string
|
||||||
|
}
|
||||||
|
data: T | T[]
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EmitData, Subscriber, SubscriberContext } from "./common"
|
import { EmitData, Message, Subscriber, SubscriberContext } from "./common"
|
||||||
|
|
||||||
export interface IEventBusModuleService {
|
export interface IEventBusModuleService {
|
||||||
emit<T>(
|
emit<T>(
|
||||||
@@ -7,6 +7,7 @@ export interface IEventBusModuleService {
|
|||||||
options?: Record<string, unknown>
|
options?: Record<string, unknown>
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
emit<T>(data: EmitData<T>[]): Promise<void>
|
emit<T>(data: EmitData<T>[]): Promise<void>
|
||||||
|
emit<T>(data: Message<T>[]): Promise<void>
|
||||||
|
|
||||||
subscribe(
|
subscribe(
|
||||||
eventName: string | symbol,
|
eventName: string | symbol,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Subscriber, SubscriberContext } from "."
|
import { ITransactionBaseService } from "../transaction-base"
|
||||||
import { ITransactionBaseService } from "../transaction-base/transaction-base"
|
import { EmitData, Message, Subscriber, SubscriberContext } from "./common"
|
||||||
|
|
||||||
export interface IEventBusService extends ITransactionBaseService {
|
export interface IEventBusService extends ITransactionBaseService {
|
||||||
subscribe(
|
subscribe(
|
||||||
@@ -13,5 +13,8 @@ export interface IEventBusService extends ITransactionBaseService {
|
|||||||
subscriber: Subscriber,
|
subscriber: Subscriber,
|
||||||
context?: SubscriberContext
|
context?: SubscriberContext
|
||||||
): this
|
): this
|
||||||
|
|
||||||
emit<T>(event: string, data: T, options?: unknown): Promise<unknown | void>
|
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>
|
||||||
}
|
}
|
||||||
@@ -14,15 +14,10 @@ import {
|
|||||||
} from "./common"
|
} from "./common"
|
||||||
|
|
||||||
import { FindConfig } from "../common"
|
import { FindConfig } from "../common"
|
||||||
import { ModuleJoinerConfig } from "../modules-sdk"
|
import { IModuleService } from "../modules-sdk"
|
||||||
import { SharedContext } from "../shared-context"
|
import { SharedContext } from "../shared-context"
|
||||||
|
|
||||||
export interface IInventoryService {
|
export interface IInventoryService extends IModuleService {
|
||||||
/**
|
|
||||||
* @ignore
|
|
||||||
*/
|
|
||||||
__joinerConfig(): ModuleJoinerConfig
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to retrieve a paginated list of inventory items along with the total count of available inventory items satisfying the provided filters.
|
* This method is used to retrieve a paginated list of inventory items along with the total count of available inventory items satisfying the provided filters.
|
||||||
* @param {FilterableInventoryItemProps} selector - The filters to apply on the retrieved inventory items.
|
* @param {FilterableInventoryItemProps} selector - The filters to apply on the retrieved inventory items.
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { FindConfig } from "../common"
|
import { FindConfig } from "../common"
|
||||||
import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
||||||
import { ModuleJoinerConfig } from "../modules-sdk"
|
import { IModuleService } from "../modules-sdk"
|
||||||
import { Context } from "../shared-context"
|
import { Context } from "../shared-context"
|
||||||
|
|
||||||
export interface ILinkModule {
|
export interface ILinkModule extends IModuleService {
|
||||||
__joinerConfig(): ModuleJoinerConfig
|
|
||||||
|
|
||||||
list(
|
list(
|
||||||
filters?: Record<string, unknown>,
|
filters?: Record<string, unknown>,
|
||||||
config?: FindConfig<unknown>,
|
config?: FindConfig<unknown>,
|
||||||
|
|||||||
@@ -45,16 +45,11 @@ import {
|
|||||||
} from "./common"
|
} from "./common"
|
||||||
|
|
||||||
import { FindConfig } from "../common"
|
import { FindConfig } from "../common"
|
||||||
import { ModuleJoinerConfig } from "../modules-sdk"
|
|
||||||
import { Context } from "../shared-context"
|
|
||||||
import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
||||||
|
import { IModuleService } from "../modules-sdk"
|
||||||
|
import { Context } from "../shared-context"
|
||||||
|
|
||||||
export interface IPricingModuleService {
|
export interface IPricingModuleService extends IModuleService {
|
||||||
/**
|
|
||||||
* @ignore
|
|
||||||
*/
|
|
||||||
__joinerConfig(): ModuleJoinerConfig
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to calculate prices based on the provided filters and context.
|
* This method is used to calculate prices based on the provided filters and context.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
||||||
import {
|
import {
|
||||||
CreateProductCategoryDTO,
|
CreateProductCategoryDTO,
|
||||||
CreateProductCollectionDTO,
|
CreateProductCollectionDTO,
|
||||||
@@ -28,18 +29,12 @@ import {
|
|||||||
UpdateProductTypeDTO,
|
UpdateProductTypeDTO,
|
||||||
UpdateProductVariantDTO,
|
UpdateProductVariantDTO,
|
||||||
} from "./common"
|
} from "./common"
|
||||||
import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
|
||||||
|
|
||||||
import { Context } from "../shared-context"
|
|
||||||
import { FindConfig } from "../common"
|
import { FindConfig } from "../common"
|
||||||
import { ModuleJoinerConfig } from "../modules-sdk"
|
import { IModuleService } from "../modules-sdk"
|
||||||
|
import { Context } from "../shared-context"
|
||||||
export interface IProductModuleService {
|
|
||||||
/**
|
|
||||||
* @ignore
|
|
||||||
*/
|
|
||||||
__joinerConfig(): ModuleJoinerConfig
|
|
||||||
|
|
||||||
|
export interface IProductModuleService extends IModuleService {
|
||||||
/**
|
/**
|
||||||
* This method is used to retrieve a product by its ID
|
* This method is used to retrieve a product by its ID
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import { Message } from "./event-bus"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated use `Context` instead
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
* A context used to share resources, such as transaction manager, between the application and the module.
|
* A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
@@ -16,9 +18,19 @@ export type SharedContext = {
|
|||||||
manager?: EntityManager
|
manager?: EntityManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MessageAggregatorFormat {
|
||||||
|
groupBy?: string[]
|
||||||
|
sortBy?: { [key: string]: string[] | string | number }
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IMessageAggregator {
|
||||||
|
save(msg: Message | Message[]): void
|
||||||
|
getMessages(format?: MessageAggregatorFormat): Record<string, Message[]>
|
||||||
|
clearMessages(): void
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
|
||||||
* A context used to share resources, such as transaction manager, between the application and the module.
|
* A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
*/
|
*/
|
||||||
export type Context<TManager = unknown> = {
|
export type Context<TManager = unknown> = {
|
||||||
@@ -39,11 +51,20 @@ export type Context<TManager = unknown> = {
|
|||||||
* A boolean value indicating whether nested transactions are enabled.
|
* A boolean value indicating whether nested transactions are enabled.
|
||||||
*/
|
*/
|
||||||
enableNestedTransactions?: boolean
|
enableNestedTransactions?: boolean
|
||||||
|
/**
|
||||||
|
* A string indicating the ID of the group to aggregate the events to be emitted at a later point.
|
||||||
|
*/
|
||||||
|
eventGroupId?: string
|
||||||
/**
|
/**
|
||||||
* A string indicating the ID of the current transaction.
|
* A string indicating the ID of the current transaction.
|
||||||
*/
|
*/
|
||||||
transactionId?: string
|
transactionId?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.
|
||||||
|
*/
|
||||||
|
messageAggregator?: IMessageAggregator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A string indicating the ID of the current request.
|
* A string indicating the ID of the current request.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -6,15 +6,10 @@ import {
|
|||||||
} from "./common"
|
} from "./common"
|
||||||
|
|
||||||
import { FindConfig } from "../common/common"
|
import { FindConfig } from "../common/common"
|
||||||
import { ModuleJoinerConfig } from "../modules-sdk"
|
import { IModuleService } from "../modules-sdk"
|
||||||
import { SharedContext } from "../shared-context"
|
import { SharedContext } from "../shared-context"
|
||||||
|
|
||||||
export interface IStockLocationService {
|
export interface IStockLocationService extends IModuleService {
|
||||||
/**
|
|
||||||
* @ignore
|
|
||||||
*/
|
|
||||||
__joinerConfig(): ModuleJoinerConfig
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to retrieve a paginated list of stock locations based on optional filters and configuration.
|
* This method is used to retrieve a paginated list of stock locations based on optional filters and configuration.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export const camelToSnakeCase = (string) =>
|
export const camelToSnakeCase = (string) =>
|
||||||
string.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
|
string.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase()
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import { MessageAggregator } from "../message-aggregator"
|
||||||
|
|
||||||
|
describe("MessageAggregator", function () {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should group messages by any given group of keys", function () {
|
||||||
|
const aggregator = new MessageAggregator()
|
||||||
|
aggregator.save({
|
||||||
|
eventName: "ProductVariant.created",
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: "ProductService",
|
||||||
|
action: "created",
|
||||||
|
object: "ProductVariant",
|
||||||
|
eventGroupId: "1",
|
||||||
|
},
|
||||||
|
data: { id: 999 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
aggregator.save({
|
||||||
|
eventName: "Product.created",
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: "ProductService",
|
||||||
|
action: "created",
|
||||||
|
object: "Product",
|
||||||
|
eventGroupId: "1",
|
||||||
|
},
|
||||||
|
data: { id: 1 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
aggregator.save({
|
||||||
|
eventName: "ProductVariant.created",
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: "ProductService",
|
||||||
|
action: "created",
|
||||||
|
object: "ProductVariant",
|
||||||
|
eventGroupId: "1",
|
||||||
|
},
|
||||||
|
data: { id: 222 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
aggregator.save({
|
||||||
|
eventName: "ProductType.detached",
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: "ProductService",
|
||||||
|
action: "detached",
|
||||||
|
object: "ProductType",
|
||||||
|
eventGroupId: "1",
|
||||||
|
},
|
||||||
|
data: { id: 333 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
aggregator.save({
|
||||||
|
eventName: "ProductVariant.updated",
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: "ProductService",
|
||||||
|
action: "updated",
|
||||||
|
object: "ProductVariant",
|
||||||
|
eventGroupId: "1",
|
||||||
|
},
|
||||||
|
data: { id: 123 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const format = {
|
||||||
|
groupBy: ["eventName", "body.metadata.object", "body.metadata.action"],
|
||||||
|
sortBy: {
|
||||||
|
"body.metadata.object": ["ProductType", "ProductVariant", "Product"],
|
||||||
|
"body.data.id": "asc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = aggregator.getMessages(format)
|
||||||
|
|
||||||
|
expect(Object.keys(messages)).toHaveLength(4)
|
||||||
|
|
||||||
|
const allGroups = Object.values(messages)
|
||||||
|
|
||||||
|
expect(allGroups[0]).toEqual([
|
||||||
|
{
|
||||||
|
eventName: "ProductType.detached",
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: "ProductService",
|
||||||
|
action: "detached",
|
||||||
|
object: "ProductType",
|
||||||
|
eventGroupId: "1",
|
||||||
|
},
|
||||||
|
data: { id: 333 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(allGroups[1]).toEqual([
|
||||||
|
{
|
||||||
|
eventName: "ProductVariant.updated",
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: "ProductService",
|
||||||
|
action: "updated",
|
||||||
|
object: "ProductVariant",
|
||||||
|
eventGroupId: "1",
|
||||||
|
},
|
||||||
|
data: { id: 123 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(allGroups[2]).toEqual([
|
||||||
|
{
|
||||||
|
eventName: "ProductVariant.created",
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: "ProductService",
|
||||||
|
action: "created",
|
||||||
|
object: "ProductVariant",
|
||||||
|
eventGroupId: "1",
|
||||||
|
},
|
||||||
|
data: { id: 222 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
eventName: "ProductVariant.created",
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: "ProductService",
|
||||||
|
action: "created",
|
||||||
|
object: "ProductVariant",
|
||||||
|
eventGroupId: "1",
|
||||||
|
},
|
||||||
|
data: { id: 999 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(allGroups[3]).toEqual([
|
||||||
|
{
|
||||||
|
eventName: "Product.created",
|
||||||
|
body: {
|
||||||
|
metadata: {
|
||||||
|
service: "ProductService",
|
||||||
|
action: "created",
|
||||||
|
object: "Product",
|
||||||
|
eventGroupId: "1",
|
||||||
|
},
|
||||||
|
data: { id: 1 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { Context, EventBusTypes } from "@medusajs/types"
|
||||||
|
import { CommonEvents } from "./common-events"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build messages from message data to be consumed by the event bus and emitted to the consumer
|
||||||
|
* @param MessageFormat
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
export function buildEventMessages<T>(
|
||||||
|
messageData:
|
||||||
|
| EventBusTypes.MessageFormat<T>
|
||||||
|
| EventBusTypes.MessageFormat<T>[],
|
||||||
|
options?: Record<string, unknown>
|
||||||
|
): EventBusTypes.Message<T>[] {
|
||||||
|
const messageData_ = Array.isArray(messageData) ? messageData : [messageData]
|
||||||
|
const messages: EventBusTypes.Message<T>[] = []
|
||||||
|
|
||||||
|
messageData_.map((data) => {
|
||||||
|
const data_ = Array.isArray(data.data) ? data.data : [data.data]
|
||||||
|
data_.forEach((bodyData) => {
|
||||||
|
const message = {
|
||||||
|
eventName: data.eventName,
|
||||||
|
body: {
|
||||||
|
metadata: data.metadata,
|
||||||
|
data: bodyData,
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
}
|
||||||
|
messages.push(message)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to compose and normalize a Message to be emitted by EventBus Module
|
||||||
|
* @param eventName Name of the event to be emitted
|
||||||
|
* @param data The content of the message
|
||||||
|
* @param metadata Metadata of the message
|
||||||
|
* @param context Context from the caller service
|
||||||
|
* @param options Options to be passed to the event bus
|
||||||
|
*/
|
||||||
|
export function composeMessage(
|
||||||
|
eventName: string,
|
||||||
|
{
|
||||||
|
data,
|
||||||
|
service,
|
||||||
|
entity,
|
||||||
|
action,
|
||||||
|
context = {},
|
||||||
|
options,
|
||||||
|
}: {
|
||||||
|
data: unknown
|
||||||
|
service: string
|
||||||
|
entity: string
|
||||||
|
action?: string
|
||||||
|
context?: Context
|
||||||
|
options?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
): EventBusTypes.Message {
|
||||||
|
const act = action || eventName.split(".").pop()
|
||||||
|
if (!action && !Object.values(CommonEvents).includes(act as CommonEvents)) {
|
||||||
|
throw new Error("Action is required if eventName is not a CommonEvent")
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata: EventBusTypes.MessageBody["metadata"] = {
|
||||||
|
service,
|
||||||
|
object: entity,
|
||||||
|
action: act!,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.eventGroupId) {
|
||||||
|
metadata.eventGroupId = context.eventGroupId
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
eventName,
|
||||||
|
body: {
|
||||||
|
metadata,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
options,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export enum CommonEvents {
|
||||||
|
CREATED = "created",
|
||||||
|
UPDATED = "updated",
|
||||||
|
DELETED = "deleted",
|
||||||
|
ATTACHED = "attached",
|
||||||
|
DETACHED = "detached",
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ export abstract class AbstractEventBusModuleService
|
|||||||
options: Record<string, unknown>
|
options: Record<string, unknown>
|
||||||
): Promise<void>
|
): Promise<void>
|
||||||
abstract emit<T>(data: EventBusTypes.EmitData<T>[]): Promise<void>
|
abstract emit<T>(data: EventBusTypes.EmitData<T>[]): Promise<void>
|
||||||
|
abstract emit<T>(data: EventBusTypes.Message<T>[]): Promise<void>
|
||||||
|
|
||||||
protected storeSubscribers({
|
protected storeSubscribers({
|
||||||
event,
|
event,
|
||||||
@@ -101,3 +102,7 @@ export abstract class AbstractEventBusModuleService
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export * from "./build-event-messages"
|
||||||
|
export * from "./common-events"
|
||||||
|
export * from "./message-aggregator"
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import {
|
||||||
|
IMessageAggregator,
|
||||||
|
Message,
|
||||||
|
MessageAggregatorFormat,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
|
||||||
|
export class MessageAggregator implements IMessageAggregator {
|
||||||
|
private messages: Message[]
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.messages = []
|
||||||
|
}
|
||||||
|
|
||||||
|
save(msg: Message | Message[]): void {
|
||||||
|
if (!msg || (Array.isArray(msg) && msg.length === 0)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(msg)) {
|
||||||
|
this.messages.push(...msg)
|
||||||
|
} else {
|
||||||
|
this.messages.push(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getMessages(format?: MessageAggregatorFormat): {
|
||||||
|
[group: string]: Message[]
|
||||||
|
} {
|
||||||
|
const { groupBy, sortBy } = format ?? {}
|
||||||
|
|
||||||
|
if (sortBy) {
|
||||||
|
this.messages.sort((a, b) => this.compareMessages(a, b, sortBy))
|
||||||
|
}
|
||||||
|
|
||||||
|
let messages: { [group: string]: Message[] } = { default: this.messages }
|
||||||
|
|
||||||
|
if (groupBy) {
|
||||||
|
const groupedMessages = this.messages.reduce<{
|
||||||
|
[key: string]: Message[]
|
||||||
|
}>((acc, msg) => {
|
||||||
|
const key = groupBy
|
||||||
|
.map((field) => this.getValueFromPath(msg, field))
|
||||||
|
.join("-")
|
||||||
|
if (!acc[key]) {
|
||||||
|
acc[key] = []
|
||||||
|
}
|
||||||
|
acc[key].push(msg)
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
messages = groupedMessages
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMessages(): void {
|
||||||
|
this.messages = []
|
||||||
|
}
|
||||||
|
|
||||||
|
private getValueFromPath(obj: any, path: string): any {
|
||||||
|
const keys = path.split(".")
|
||||||
|
for (const key of keys) {
|
||||||
|
obj = obj[key]
|
||||||
|
if (obj === undefined) break
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
private compareMessages(
|
||||||
|
a: Message,
|
||||||
|
b: Message,
|
||||||
|
sortBy: MessageAggregatorFormat["sortBy"]
|
||||||
|
): number {
|
||||||
|
for (const key of Object.keys(sortBy!)) {
|
||||||
|
const orderCriteria = sortBy![key]
|
||||||
|
const valueA = this.getValueFromPath(a, key)
|
||||||
|
const valueB = this.getValueFromPath(b, key)
|
||||||
|
|
||||||
|
// User defined order
|
||||||
|
if (Array.isArray(orderCriteria)) {
|
||||||
|
const indexA = orderCriteria.indexOf(valueA)
|
||||||
|
const indexB = orderCriteria.indexOf(valueB)
|
||||||
|
|
||||||
|
if (indexA === indexB) {
|
||||||
|
continue
|
||||||
|
} else if (indexA === -1) {
|
||||||
|
return 1
|
||||||
|
} else if (indexB === -1) {
|
||||||
|
return -1
|
||||||
|
} else {
|
||||||
|
return indexA - indexB
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Ascending or descending order
|
||||||
|
let orderMultiplier = 1
|
||||||
|
if (orderCriteria === "desc" || orderCriteria === -1) {
|
||||||
|
orderMultiplier = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueA === valueB) {
|
||||||
|
continue
|
||||||
|
} else if (valueA < valueB) {
|
||||||
|
return -1 * orderMultiplier
|
||||||
|
} else {
|
||||||
|
return 1 * orderMultiplier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
export function InjectIntoContext(
|
||||||
|
properties: Record<string, unknown | Function>
|
||||||
|
): MethodDecorator {
|
||||||
|
return function (
|
||||||
|
target: any,
|
||||||
|
propertyKey: string | symbol,
|
||||||
|
descriptor: any
|
||||||
|
): void {
|
||||||
|
if (!target.MedusaContextIndex_) {
|
||||||
|
throw new Error(
|
||||||
|
`To apply @InjectIntoContext you have to flag a parameter using @MedusaContext`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const argIndex = target.MedusaContextIndex_[propertyKey]
|
||||||
|
const original = descriptor.value
|
||||||
|
descriptor.value = async function (...args: any[]) {
|
||||||
|
for (const key of Object.keys(properties)) {
|
||||||
|
args[argIndex] = args[argIndex] ?? {}
|
||||||
|
args[argIndex][key] =
|
||||||
|
args[argIndex][key] ??
|
||||||
|
(typeof properties[key] === "function"
|
||||||
|
? (properties[key] as Function).apply(this, args)
|
||||||
|
: properties[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
return await original.apply(this, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user