feat(product, types, modules-sdk): added event bus events for products (#4654)
what: - adds an eventbus dependency to product module. - emits events on product, category and collection CUD RESOLVES CORE-1450
This commit is contained in:
7
.changeset/fuzzy-tables-build.md
Normal file
7
.changeset/fuzzy-tables-build.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/product": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
"@medusajs/modules-sdk": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(product,types): added event bus events for products
|
||||||
@@ -20,6 +20,7 @@ packages/*
|
|||||||
!packages/cache-redis
|
!packages/cache-redis
|
||||||
!packages/cache-inmemory
|
!packages/cache-inmemory
|
||||||
!packages/create-medusa-app
|
!packages/create-medusa-app
|
||||||
|
!packages/product
|
||||||
|
|
||||||
|
|
||||||
**/models/*
|
**/models/*
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ module.exports = {
|
|||||||
"./packages/cache-redis/tsconfig.spec.json",
|
"./packages/cache-redis/tsconfig.spec.json",
|
||||||
"./packages/cache-inmemory/tsconfig.spec.json",
|
"./packages/cache-inmemory/tsconfig.spec.json",
|
||||||
"./packages/create-medusa-app/tsconfig.json",
|
"./packages/create-medusa-app/tsconfig.json",
|
||||||
|
"./packages/product/tsconfig.json",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
|||||||
@@ -12,11 +12,15 @@ export enum Modules {
|
|||||||
PRODUCT = "productService",
|
PRODUCT = "productService",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ModuleRegistrationName {
|
||||||
|
EVENT_BUS = "eventBusModuleService"
|
||||||
|
}
|
||||||
|
|
||||||
export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } =
|
export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } =
|
||||||
{
|
{
|
||||||
[Modules.EVENT_BUS]: {
|
[Modules.EVENT_BUS]: {
|
||||||
key: Modules.EVENT_BUS,
|
key: Modules.EVENT_BUS,
|
||||||
registrationName: "eventBusModuleService",
|
registrationName: ModuleRegistrationName.EVENT_BUS,
|
||||||
defaultPackage: "@medusajs/event-bus-local",
|
defaultPackage: "@medusajs/event-bus-local",
|
||||||
label: "EventBusModuleService",
|
label: "EventBusModuleService",
|
||||||
canOverride: true,
|
canOverride: true,
|
||||||
@@ -75,7 +79,7 @@ export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } =
|
|||||||
isRequired: false,
|
isRequired: false,
|
||||||
canOverride: true,
|
canOverride: true,
|
||||||
isQueryable: true,
|
isQueryable: true,
|
||||||
dependencies: [],
|
dependencies: [ModuleRegistrationName.EVENT_BUS],
|
||||||
defaultModuleDeclaration: {
|
defaultModuleDeclaration: {
|
||||||
scope: MODULE_SCOPE.EXTERNAL,
|
scope: MODULE_SCOPE.EXTERNAL,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
EmitData,
|
||||||
|
EventBusTypes,
|
||||||
|
Subscriber,
|
||||||
|
IEventBusModuleService
|
||||||
|
} from "@medusajs/types"
|
||||||
|
|
||||||
|
export class EventBusService implements IEventBusModuleService {
|
||||||
|
async emit<T>(
|
||||||
|
eventName: string,
|
||||||
|
data: T,
|
||||||
|
options: Record<string, unknown>
|
||||||
|
): Promise<void>
|
||||||
|
async emit<T>(data: EventBusTypes.EmitData<T>[]): Promise<void>
|
||||||
|
async emit<T, TInput extends string | EventBusTypes.EmitData<T>[] = string>(
|
||||||
|
eventOrData: TInput,
|
||||||
|
data?: T,
|
||||||
|
options: Record<string, unknown> = {}
|
||||||
|
): Promise<void> {}
|
||||||
|
|
||||||
|
subscribe(event: string | symbol, subscriber: Subscriber): this {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
unsubscribe(
|
||||||
|
event: string | symbol,
|
||||||
|
subscriber: Subscriber,
|
||||||
|
context?: EventBusTypes.SubscriberContext
|
||||||
|
): this {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
withTransaction() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,8 +5,6 @@ import { ProductRepository } from "../__fixtures__/module"
|
|||||||
import { createProductAndTags } from "../__fixtures__/product"
|
import { createProductAndTags } 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 { buildProductAndRelationsData } from "../__fixtures__/product/data/create-product"
|
|
||||||
import { kebabCase } from "@medusajs/utils"
|
|
||||||
import { IProductModuleService } from "@medusajs/types"
|
import { IProductModuleService } from "@medusajs/types"
|
||||||
|
|
||||||
const beforeEach_ = async () => {
|
const beforeEach_ = async () => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { initialize } from "../../../../src"
|
|||||||
import { DB_URL, TestDatabase } from "../../../utils"
|
import { DB_URL, TestDatabase } from "../../../utils"
|
||||||
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"
|
||||||
|
|
||||||
describe("ProductModuleService product categories", () => {
|
describe("ProductModuleService product categories", () => {
|
||||||
let service: IProductModuleService
|
let service: IProductModuleService
|
||||||
@@ -16,16 +17,20 @@ describe("ProductModuleService product categories", () => {
|
|||||||
let productCategoryOne: ProductCategory
|
let productCategoryOne: ProductCategory
|
||||||
let productCategoryTwo: ProductCategory
|
let productCategoryTwo: ProductCategory
|
||||||
let productCategories: ProductCategory[]
|
let productCategories: ProductCategory[]
|
||||||
|
let eventBus
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestDatabase.setupDatabase()
|
await TestDatabase.setupDatabase()
|
||||||
repositoryManager = await TestDatabase.forkManager()
|
repositoryManager = await TestDatabase.forkManager()
|
||||||
|
eventBus = new EventBusService()
|
||||||
|
|
||||||
service = await initialize({
|
service = await initialize({
|
||||||
database: {
|
database: {
|
||||||
clientUrl: DB_URL,
|
clientUrl: DB_URL,
|
||||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
eventBusModuleService: eventBus
|
||||||
})
|
})
|
||||||
|
|
||||||
testManager = await TestDatabase.forkManager()
|
testManager = await TestDatabase.forkManager()
|
||||||
@@ -68,6 +73,7 @@ describe("ProductModuleService product categories", () => {
|
|||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await TestDatabase.clearDatabase()
|
await TestDatabase.clearDatabase()
|
||||||
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("listCategories", () => {
|
describe("listCategories", () => {
|
||||||
@@ -279,6 +285,22 @@ describe("ProductModuleService product categories", () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should emit events through event bus", async () => {
|
||||||
|
const eventBusSpy = jest.spyOn(EventBusService.prototype, 'emit')
|
||||||
|
const category = await service.createCategory({
|
||||||
|
name: "New Category",
|
||||||
|
parent_category_id: productCategoryOne.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledWith(
|
||||||
|
"product-category.created",
|
||||||
|
{
|
||||||
|
id: category.id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("should append rank from an existing category depending on parent", async () => {
|
it("should append rank from an existing category depending on parent", async () => {
|
||||||
await service.createCategory({
|
await service.createCategory({
|
||||||
name: "New Category",
|
name: "New Category",
|
||||||
@@ -356,6 +378,21 @@ describe("ProductModuleService product categories", () => {
|
|||||||
productCategoryZeroTwo = categories[5]
|
productCategoryZeroTwo = categories[5]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should emit events through event bus", async () => {
|
||||||
|
const eventBusSpy = jest.spyOn(EventBusService.prototype, 'emit')
|
||||||
|
await service.updateCategory(productCategoryZero.id, {
|
||||||
|
name: "New Category",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledWith(
|
||||||
|
"product-category.updated",
|
||||||
|
{
|
||||||
|
id: productCategoryZero.id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("should update the name of the category successfully", async () => {
|
it("should update the name of the category successfully", async () => {
|
||||||
await service.updateCategory(productCategoryZero.id, {
|
await service.updateCategory(productCategoryZero.id, {
|
||||||
name: "New Category",
|
name: "New Category",
|
||||||
@@ -513,6 +550,19 @@ describe("ProductModuleService product categories", () => {
|
|||||||
productCategoryTwo = categories[2]
|
productCategoryTwo = categories[2]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should emit events through event bus", async () => {
|
||||||
|
const eventBusSpy = jest.spyOn(EventBusService.prototype, 'emit')
|
||||||
|
await service.deleteCategory(productCategoryOne.id)
|
||||||
|
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledWith(
|
||||||
|
"product-category.deleted",
|
||||||
|
{
|
||||||
|
id: productCategoryOne.id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("should throw an error when an id does not exist", async () => {
|
it("should throw an error when an id does not exist", async () => {
|
||||||
let error
|
let error
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ProductTypes } from "@medusajs/types"
|
|||||||
import { initialize } from "../../../../src"
|
import { initialize } from "../../../../src"
|
||||||
import { DB_URL, TestDatabase } from "../../../utils"
|
import { DB_URL, TestDatabase } from "../../../utils"
|
||||||
import { createCollections } from "../../../__fixtures__/product"
|
import { createCollections } from "../../../__fixtures__/product"
|
||||||
|
import { EventBusService } from "../../../__fixtures__/event-bus"
|
||||||
|
|
||||||
describe("ProductModuleService product collections", () => {
|
describe("ProductModuleService product collections", () => {
|
||||||
let service: IProductModuleService
|
let service: IProductModuleService
|
||||||
@@ -16,16 +17,20 @@ describe("ProductModuleService product collections", () => {
|
|||||||
let productCollectionOne: ProductCollection
|
let productCollectionOne: ProductCollection
|
||||||
let productCollectionTwo: ProductCollection
|
let productCollectionTwo: ProductCollection
|
||||||
let productCollections: ProductCollection[]
|
let productCollections: ProductCollection[]
|
||||||
|
let eventBus
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestDatabase.setupDatabase()
|
await TestDatabase.setupDatabase()
|
||||||
repositoryManager = await TestDatabase.forkManager()
|
repositoryManager = await TestDatabase.forkManager()
|
||||||
|
eventBus = new EventBusService()
|
||||||
|
|
||||||
service = await initialize({
|
service = await initialize({
|
||||||
database: {
|
database: {
|
||||||
clientUrl: DB_URL,
|
clientUrl: DB_URL,
|
||||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
eventBusModuleService: eventBus
|
||||||
})
|
})
|
||||||
|
|
||||||
testManager = await TestDatabase.forkManager()
|
testManager = await TestDatabase.forkManager()
|
||||||
@@ -65,6 +70,7 @@ describe("ProductModuleService product collections", () => {
|
|||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await TestDatabase.clearDatabase()
|
await TestDatabase.clearDatabase()
|
||||||
|
jest.clearAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("listCollections", () => {
|
describe("listCollections", () => {
|
||||||
@@ -261,11 +267,41 @@ describe("ProductModuleService product collections", () => {
|
|||||||
|
|
||||||
expect(collections).toHaveLength(0)
|
expect(collections).toHaveLength(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should emit events through event bus", async () => {
|
||||||
|
const eventBusSpy = jest.spyOn(EventBusService.prototype, 'emit')
|
||||||
|
await service.deleteCollections(
|
||||||
|
[collectionId],
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledWith([{
|
||||||
|
eventName: "product-collection.deleted",
|
||||||
|
data: { id: collectionId }
|
||||||
|
}])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("updateCollections", () => {
|
describe("updateCollections", () => {
|
||||||
const collectionId = "test-1"
|
const collectionId = "test-1"
|
||||||
|
|
||||||
|
it("should emit events through event bus", async () => {
|
||||||
|
const eventBusSpy = jest.spyOn(EventBusService.prototype, 'emit')
|
||||||
|
|
||||||
|
await service.updateCollections(
|
||||||
|
[{
|
||||||
|
id: collectionId,
|
||||||
|
title: "New Collection"
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledWith([{
|
||||||
|
eventName: "product-collection.updated",
|
||||||
|
data: { id: collectionId }
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
|
||||||
it("should update the value of the collection successfully", async () => {
|
it("should update the value of the collection successfully", async () => {
|
||||||
await service.updateCollections(
|
await service.updateCollections(
|
||||||
[{
|
[{
|
||||||
@@ -311,6 +347,22 @@ describe("ProductModuleService product collections", () => {
|
|||||||
|
|
||||||
expect(productCollection.title).toEqual("New Collection")
|
expect(productCollection.title).toEqual("New Collection")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should emit events through event bus", async () => {
|
||||||
|
const eventBusSpy = jest.spyOn(EventBusService.prototype, 'emit')
|
||||||
|
|
||||||
|
const collections = await service.createCollections(
|
||||||
|
[{
|
||||||
|
title: "New Collection"
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledWith([{
|
||||||
|
eventName: "product-collection.created",
|
||||||
|
data: { id: collections[0].id }
|
||||||
|
}])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { MedusaModule } from "@medusajs/modules-sdk"
|
import { MedusaModule } from "@medusajs/modules-sdk"
|
||||||
import { Product, ProductCategory, ProductCollection, ProductType, ProductVariant } from "@models"
|
import { Product, ProductCategory, ProductCollection, ProductType, ProductVariant } from "@models"
|
||||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||||
|
import { kebabCase } from "@medusajs/utils"
|
||||||
|
|
||||||
import { initialize } from "../../../../src"
|
import { initialize } from "../../../../src"
|
||||||
import { DB_URL, TestDatabase } from "../../../utils"
|
import { DB_URL, TestDatabase } from "../../../utils"
|
||||||
import { buildProductAndRelationsData } from "../../../__fixtures__/product/data/create-product"
|
import { buildProductAndRelationsData } from "../../../__fixtures__/product/data/create-product"
|
||||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||||
import { createCollections, createTypes } from "../../../__fixtures__/product"
|
import { createCollections, createTypes } from "../../../__fixtures__/product"
|
||||||
|
import { EventBusService } from "../../../__fixtures__/event-bus"
|
||||||
|
|
||||||
const beforeEach_ = async () => {
|
const beforeEach_ = async () => {
|
||||||
await TestDatabase.setupDatabase()
|
await TestDatabase.setupDatabase()
|
||||||
@@ -15,6 +17,7 @@ const beforeEach_ = async () => {
|
|||||||
|
|
||||||
const afterEach_ = async () => {
|
const afterEach_ = async () => {
|
||||||
await TestDatabase.clearDatabase()
|
await TestDatabase.clearDatabase()
|
||||||
|
jest.clearAllMocks()
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("ProductModuleService products", function () {
|
describe("ProductModuleService products", function () {
|
||||||
@@ -32,6 +35,7 @@ describe("ProductModuleService products", function () {
|
|||||||
let productTypeOne: ProductType
|
let productTypeOne: ProductType
|
||||||
let productTypeTwo: ProductType
|
let productTypeTwo: ProductType
|
||||||
let images = ["image-1"]
|
let images = ["image-1"]
|
||||||
|
let eventBus
|
||||||
|
|
||||||
const productCategoriesData = [{
|
const productCategoriesData = [{
|
||||||
id: "test-1",
|
id: "test-1",
|
||||||
@@ -135,11 +139,14 @@ describe("ProductModuleService products", function () {
|
|||||||
|
|
||||||
MedusaModule.clearInstances()
|
MedusaModule.clearInstances()
|
||||||
|
|
||||||
|
eventBus = new EventBusService()
|
||||||
module = await initialize({
|
module = await initialize({
|
||||||
database: {
|
database: {
|
||||||
clientUrl: DB_URL,
|
clientUrl: DB_URL,
|
||||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
eventBusModuleService: eventBus
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -228,6 +235,28 @@ describe("ProductModuleService products", function () {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should emit events through event bus", async () => {
|
||||||
|
const eventBusSpy = jest.spyOn(EventBusService.prototype, 'emit')
|
||||||
|
const data = buildProductAndRelationsData({
|
||||||
|
images,
|
||||||
|
thumbnail: images[0],
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
...data,
|
||||||
|
id: productOne.id,
|
||||||
|
title: "updated title"
|
||||||
|
}
|
||||||
|
|
||||||
|
await module.update([updateData])
|
||||||
|
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledWith([{
|
||||||
|
eventName: "product.updated",
|
||||||
|
data: { id: productOne.id }
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
|
||||||
it("should add relationships to a product", async () => {
|
it("should add relationships to a product", async () => {
|
||||||
const updateData = {
|
const updateData = {
|
||||||
id: productOne.id,
|
id: productOne.id,
|
||||||
@@ -461,4 +490,284 @@ describe("ProductModuleService products", function () {
|
|||||||
expect(error.message).toEqual(`ProductVariant with id "does-not-exist" not found`)
|
expect(error.message).toEqual(`ProductVariant with id "does-not-exist" not found`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("create", function () {
|
||||||
|
let module: IProductModuleService
|
||||||
|
let images = ["image-1"]
|
||||||
|
let eventBus
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await beforeEach_()
|
||||||
|
|
||||||
|
MedusaModule.clearInstances()
|
||||||
|
|
||||||
|
eventBus = new EventBusService()
|
||||||
|
module = await initialize({
|
||||||
|
database: {
|
||||||
|
clientUrl: DB_URL,
|
||||||
|
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
eventBusModuleService: eventBus
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(afterEach_)
|
||||||
|
|
||||||
|
it("should create a product", async () => {
|
||||||
|
const data = buildProductAndRelationsData({
|
||||||
|
images,
|
||||||
|
thumbnail: images[0],
|
||||||
|
})
|
||||||
|
|
||||||
|
const products = await module.create([data])
|
||||||
|
|
||||||
|
expect(products).toHaveLength(1)
|
||||||
|
expect(products[0].images).toHaveLength(1)
|
||||||
|
expect(products[0].options).toHaveLength(1)
|
||||||
|
expect(products[0].tags).toHaveLength(1)
|
||||||
|
expect(products[0].categories).toHaveLength(0)
|
||||||
|
expect(products[0].variants).toHaveLength(1)
|
||||||
|
|
||||||
|
expect(products[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
title: data.title,
|
||||||
|
handle: kebabCase(data.title),
|
||||||
|
description: data.description,
|
||||||
|
subtitle: data.subtitle,
|
||||||
|
is_giftcard: data.is_giftcard,
|
||||||
|
discountable: data.discountable,
|
||||||
|
thumbnail: images[0],
|
||||||
|
status: data.status,
|
||||||
|
images: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
url: images[0],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
options: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
title: data.options[0].title,
|
||||||
|
values: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
value: data.variants[0].options?.[0].value,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
tags: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
value: data.tags[0].value,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
type: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
value: data.type.value,
|
||||||
|
}),
|
||||||
|
variants: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
title: data.variants[0].title,
|
||||||
|
sku: data.variants[0].sku,
|
||||||
|
allow_backorder: false,
|
||||||
|
manage_inventory: true,
|
||||||
|
inventory_quantity: 100,
|
||||||
|
variant_rank: 0,
|
||||||
|
options: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
value: data.variants[0].options?.[0].value,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should emit events through eventBus", async () => {
|
||||||
|
const eventBusSpy = jest.spyOn(EventBusService.prototype, 'emit')
|
||||||
|
const data = buildProductAndRelationsData({
|
||||||
|
images,
|
||||||
|
thumbnail: images[0],
|
||||||
|
})
|
||||||
|
|
||||||
|
const products = await module.create([data])
|
||||||
|
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledWith([{
|
||||||
|
eventName: "product.created",
|
||||||
|
data: { id: products[0].id }
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("softDelete", function () {
|
||||||
|
let module: IProductModuleService
|
||||||
|
let images = ["image-1"]
|
||||||
|
let eventBus
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await beforeEach_()
|
||||||
|
|
||||||
|
MedusaModule.clearInstances()
|
||||||
|
|
||||||
|
eventBus = new EventBusService()
|
||||||
|
module = await initialize({
|
||||||
|
database: {
|
||||||
|
clientUrl: DB_URL,
|
||||||
|
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
eventBusModuleService: eventBus
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(afterEach_)
|
||||||
|
|
||||||
|
it("should soft delete a product and its cascaded relations", async () => {
|
||||||
|
const data = buildProductAndRelationsData({
|
||||||
|
images,
|
||||||
|
thumbnail: images[0],
|
||||||
|
})
|
||||||
|
|
||||||
|
const products = await module.create([data])
|
||||||
|
|
||||||
|
await module.softDelete([products[0].id])
|
||||||
|
|
||||||
|
const deletedProducts = await module.list(
|
||||||
|
{ id: products[0].id },
|
||||||
|
{
|
||||||
|
relations: [
|
||||||
|
"variants",
|
||||||
|
"variants.options",
|
||||||
|
"options",
|
||||||
|
"options.values",
|
||||||
|
],
|
||||||
|
withDeleted: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(deletedProducts).toHaveLength(1)
|
||||||
|
expect(deletedProducts[0].deleted_at).not.toBeNull()
|
||||||
|
|
||||||
|
for (const option of deletedProducts[0].options) {
|
||||||
|
expect(option.deleted_at).not.toBeNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
const productOptionsValues = deletedProducts[0].options
|
||||||
|
.map((o) => o.values)
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
for (const optionValue of productOptionsValues) {
|
||||||
|
expect(optionValue.deleted_at).not.toBeNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const variant of deletedProducts[0].variants) {
|
||||||
|
expect(variant.deleted_at).not.toBeNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantsOptions = deletedProducts[0].options
|
||||||
|
.map((o) => o.values)
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
for (const option of variantsOptions) {
|
||||||
|
expect(option.deleted_at).not.toBeNull()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should emit events through eventBus", async () => {
|
||||||
|
const eventBusSpy = jest.spyOn(EventBusService.prototype, 'emit')
|
||||||
|
const data = buildProductAndRelationsData({
|
||||||
|
images,
|
||||||
|
thumbnail: images[0],
|
||||||
|
})
|
||||||
|
|
||||||
|
const products = await module.create([data])
|
||||||
|
|
||||||
|
await module.softDelete([products[0].id])
|
||||||
|
|
||||||
|
expect(eventBusSpy).toHaveBeenCalledWith([{
|
||||||
|
eventName: "product.created",
|
||||||
|
data: { id: products[0].id }
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("restore", function () {
|
||||||
|
let module: IProductModuleService
|
||||||
|
let images = ["image-1"]
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await beforeEach_()
|
||||||
|
|
||||||
|
MedusaModule.clearInstances()
|
||||||
|
|
||||||
|
module = await initialize({
|
||||||
|
database: {
|
||||||
|
clientUrl: DB_URL,
|
||||||
|
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(afterEach_)
|
||||||
|
|
||||||
|
it("should restore a soft deleted product and its cascaded relations", async () => {
|
||||||
|
const data = buildProductAndRelationsData({
|
||||||
|
images,
|
||||||
|
thumbnail: images[0],
|
||||||
|
})
|
||||||
|
|
||||||
|
const products = await module.create([data])
|
||||||
|
|
||||||
|
await module.softDelete([products[0].id])
|
||||||
|
await module.restore([products[0].id])
|
||||||
|
|
||||||
|
const deletedProducts = await module.list(
|
||||||
|
{ id: products[0].id },
|
||||||
|
{
|
||||||
|
relations: [
|
||||||
|
"variants",
|
||||||
|
"variants.options",
|
||||||
|
"variants.options",
|
||||||
|
"options",
|
||||||
|
"options.values",
|
||||||
|
],
|
||||||
|
withDeleted: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(deletedProducts).toHaveLength(1)
|
||||||
|
expect(deletedProducts[0].deleted_at).toBeNull()
|
||||||
|
|
||||||
|
for (const option of deletedProducts[0].options) {
|
||||||
|
expect(option.deleted_at).toBeNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
const productOptionsValues = deletedProducts[0].options
|
||||||
|
.map((o) => o.values)
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
for (const optionValue of productOptionsValues) {
|
||||||
|
expect(optionValue.deleted_at).toBeNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const variant of deletedProducts[0].variants) {
|
||||||
|
expect(variant.deleted_at).toBeNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantsOptions = deletedProducts[0].options
|
||||||
|
.map((o) => o.values)
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
for (const option of variantsOptions) {
|
||||||
|
expect(option.deleted_at).toBeNull()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -25,13 +25,31 @@ import {
|
|||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
JoinerServiceConfig,
|
JoinerServiceConfig,
|
||||||
ProductTypes,
|
ProductTypes,
|
||||||
|
IEventBusModuleService,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
|
||||||
import ProductImageService from "./product-image"
|
import ProductImageService from "./product-image"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ProductServiceTypes,
|
CreateProductCategoryDTO,
|
||||||
ProductVariantServiceTypes,
|
ProductCategoryEventData,
|
||||||
} from "../types/services"
|
ProductCategoryEvents,
|
||||||
|
UpdateProductCategoryDTO,
|
||||||
|
} from "../types/services/product-category"
|
||||||
|
|
||||||
|
import { UpdateProductVariantDTO } from "../types/services/product-variant"
|
||||||
|
|
||||||
|
import {
|
||||||
|
ProductCollectionEventData,
|
||||||
|
ProductCollectionEvents,
|
||||||
|
} from "../types/services/product-collection"
|
||||||
|
|
||||||
|
import {
|
||||||
|
ProductEventData,
|
||||||
|
ProductEvents,
|
||||||
|
UpdateProductDTO,
|
||||||
|
} from "../types/services/product"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
InjectManager,
|
InjectManager,
|
||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
@@ -49,7 +67,6 @@ import {
|
|||||||
joinerConfig,
|
joinerConfig,
|
||||||
LinkableKeys,
|
LinkableKeys,
|
||||||
} from "./../joiner-config"
|
} from "./../joiner-config"
|
||||||
import { ProductCategoryServiceTypes } from "../types"
|
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
baseRepository: DAL.RepositoryService
|
baseRepository: DAL.RepositoryService
|
||||||
@@ -61,6 +78,7 @@ type InjectedDependencies = {
|
|||||||
productImageService: ProductImageService<any>
|
productImageService: ProductImageService<any>
|
||||||
productTypeService: ProductTypeService<any>
|
productTypeService: ProductTypeService<any>
|
||||||
productOptionService: ProductOptionService<any>
|
productOptionService: ProductOptionService<any>
|
||||||
|
eventBusModuleService?: IEventBusModuleService
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ProductModuleService<
|
export default class ProductModuleService<
|
||||||
@@ -80,12 +98,16 @@ export default class ProductModuleService<
|
|||||||
TProductVariant,
|
TProductVariant,
|
||||||
TProduct
|
TProduct
|
||||||
>
|
>
|
||||||
|
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
protected readonly productCategoryService_: ProductCategoryService<TProductCategory>
|
protected readonly productCategoryService_: ProductCategoryService<TProductCategory>
|
||||||
protected readonly productTagService_: ProductTagService<TProductTag>
|
protected readonly productTagService_: ProductTagService<TProductTag>
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
protected readonly productCollectionService_: ProductCollectionService<TProductCollection>
|
protected readonly productCollectionService_: ProductCollectionService<TProductCollection>
|
||||||
protected readonly productImageService_: ProductImageService<TProductImage>
|
protected readonly productImageService_: ProductImageService<TProductImage>
|
||||||
protected readonly productTypeService_: ProductTypeService<TProductType>
|
protected readonly productTypeService_: ProductTypeService<TProductType>
|
||||||
protected readonly productOptionService_: ProductOptionService<TProductOption>
|
protected readonly productOptionService_: ProductOptionService<TProductOption>
|
||||||
|
protected readonly eventBusModuleService_?: IEventBusModuleService
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
@@ -98,6 +120,7 @@ export default class ProductModuleService<
|
|||||||
productImageService,
|
productImageService,
|
||||||
productTypeService,
|
productTypeService,
|
||||||
productOptionService,
|
productOptionService,
|
||||||
|
eventBusModuleService,
|
||||||
}: InjectedDependencies,
|
}: InjectedDependencies,
|
||||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||||
) {
|
) {
|
||||||
@@ -110,6 +133,7 @@ export default class ProductModuleService<
|
|||||||
this.productImageService_ = productImageService
|
this.productImageService_ = productImageService
|
||||||
this.productTypeService_ = productTypeService
|
this.productTypeService_ = productTypeService
|
||||||
this.productOptionService_ = productOptionService
|
this.productOptionService_ = productOptionService
|
||||||
|
this.eventBusModuleService_ = eventBusModuleService
|
||||||
}
|
}
|
||||||
|
|
||||||
__joinerConfig(): JoinerServiceConfig {
|
__joinerConfig(): JoinerServiceConfig {
|
||||||
@@ -499,6 +523,13 @@ export default class ProductModuleService<
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductCollectionEventData>(
|
||||||
|
productCollections.map(({ id }) => ({
|
||||||
|
eventName: ProductCollectionEvents.COLLECTION_CREATED,
|
||||||
|
data: { id },
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productCollections))
|
return JSON.parse(JSON.stringify(productCollections))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,6 +543,13 @@ export default class ProductModuleService<
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductCollectionEventData>(
|
||||||
|
productCollections.map(({ id }) => ({
|
||||||
|
eventName: ProductCollectionEvents.COLLECTION_UPDATED,
|
||||||
|
data: { id },
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productCollections))
|
return JSON.parse(JSON.stringify(productCollections))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,6 +562,13 @@ export default class ProductModuleService<
|
|||||||
productCollectionIds,
|
productCollectionIds,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductCollectionEventData>(
|
||||||
|
productCollectionIds.map((id) => ({
|
||||||
|
eventName: ProductCollectionEvents.COLLECTION_DELETED,
|
||||||
|
data: { id },
|
||||||
|
}))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
@@ -558,7 +603,7 @@ export default class ProductModuleService<
|
|||||||
|
|
||||||
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
||||||
async createCategory(
|
async createCategory(
|
||||||
data: ProductCategoryServiceTypes.CreateProductCategoryDTO,
|
data: CreateProductCategoryDTO,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
) {
|
||||||
const productCategory = await this.productCategoryService_.create(
|
const productCategory = await this.productCategoryService_.create(
|
||||||
@@ -566,13 +611,18 @@ export default class ProductModuleService<
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductCategoryEventData>(
|
||||||
|
ProductCategoryEvents.CATEGORY_CREATED,
|
||||||
|
{ id: productCategory.id }
|
||||||
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productCategory))
|
return JSON.parse(JSON.stringify(productCategory))
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
||||||
async updateCategory(
|
async updateCategory(
|
||||||
categoryId: string,
|
categoryId: string,
|
||||||
data: ProductCategoryServiceTypes.UpdateProductCategoryDTO,
|
data: UpdateProductCategoryDTO,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
) {
|
) {
|
||||||
const productCategory = await this.productCategoryService_.update(
|
const productCategory = await this.productCategoryService_.update(
|
||||||
@@ -581,6 +631,11 @@ export default class ProductModuleService<
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductCategoryEventData>(
|
||||||
|
ProductCategoryEvents.CATEGORY_UPDATED,
|
||||||
|
{ id: productCategory.id }
|
||||||
|
)
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(productCategory))
|
return JSON.parse(JSON.stringify(productCategory))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,6 +645,11 @@ export default class ProductModuleService<
|
|||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.productCategoryService_.delete(categoryId, sharedContext)
|
await this.productCategoryService_.delete(categoryId, sharedContext)
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductCategoryEventData>(
|
||||||
|
ProductCategoryEvents.CATEGORY_DELETED,
|
||||||
|
{ id: categoryId }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
@@ -612,10 +672,20 @@ export default class ProductModuleService<
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<ProductTypes.ProductDTO[]> {
|
): Promise<ProductTypes.ProductDTO[]> {
|
||||||
const products = await this.create_(data, sharedContext)
|
const products = await this.create_(data, sharedContext)
|
||||||
|
const createdProducts = await this.baseRepository_.serialize<
|
||||||
return this.baseRepository_.serialize<ProductTypes.ProductDTO[]>(products, {
|
ProductTypes.ProductDTO[]
|
||||||
|
>(products, {
|
||||||
populate: true,
|
populate: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductEventData>(
|
||||||
|
createdProducts.map(({ id }) => ({
|
||||||
|
eventName: ProductEvents.PRODUCT_CREATED,
|
||||||
|
data: { id },
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
return createdProducts
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(
|
async update(
|
||||||
@@ -624,9 +694,20 @@ export default class ProductModuleService<
|
|||||||
): Promise<ProductTypes.ProductDTO[]> {
|
): Promise<ProductTypes.ProductDTO[]> {
|
||||||
const products = await this.update_(data, sharedContext)
|
const products = await this.update_(data, sharedContext)
|
||||||
|
|
||||||
return this.baseRepository_.serialize<ProductTypes.ProductDTO[]>(products, {
|
const updatedProducts = await this.baseRepository_.serialize<
|
||||||
|
ProductTypes.ProductDTO[]
|
||||||
|
>(products, {
|
||||||
populate: true,
|
populate: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductEventData>(
|
||||||
|
updatedProducts.map(({ id }) => ({
|
||||||
|
eventName: ProductEvents.PRODUCT_UPDATED,
|
||||||
|
data: { id },
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
return updatedProducts
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
||||||
@@ -701,7 +782,7 @@ export default class ProductModuleService<
|
|||||||
...option,
|
...option,
|
||||||
}
|
}
|
||||||
const product = productByHandleMap.get(handle)
|
const product = productByHandleMap.get(handle)
|
||||||
const productId = product?.id!
|
const productId = product?.id
|
||||||
|
|
||||||
if (productId) {
|
if (productId) {
|
||||||
productOptionsData.product_id = productId
|
productOptionsData.product_id = productId
|
||||||
@@ -810,7 +891,7 @@ export default class ProductModuleService<
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
return productData as ProductServiceTypes.UpdateProductDTO
|
return productData as UpdateProductDTO
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -905,7 +986,7 @@ export default class ProductModuleService<
|
|||||||
promises.push(
|
promises.push(
|
||||||
this.productVariantService_.update(
|
this.productVariantService_.update(
|
||||||
productByIdMap.get(productId)!,
|
productByIdMap.get(productId)!,
|
||||||
variants as unknown as ProductVariantServiceTypes.UpdateProductVariantDTO[],
|
variants as unknown as UpdateProductVariantDTO[],
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -937,9 +1018,13 @@ export default class ProductModuleService<
|
|||||||
|
|
||||||
if (productData.images?.length) {
|
if (productData.images?.length) {
|
||||||
productData.images = await this.productImageService_.upsert(
|
productData.images = await this.productImageService_.upsert(
|
||||||
productData.images.map((image) =>
|
productData.images.map((image) => {
|
||||||
isString(image) ? image : image.url
|
if (isString(image)) {
|
||||||
),
|
return image
|
||||||
|
} else {
|
||||||
|
return image.url
|
||||||
|
}
|
||||||
|
}),
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -977,6 +1062,13 @@ export default class ProductModuleService<
|
|||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.productService_.delete(productIds, sharedContext)
|
await this.productService_.delete(productIds, sharedContext)
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductEventData>(
|
||||||
|
productIds.map((id) => ({
|
||||||
|
eventName: ProductEvents.PRODUCT_DELETED,
|
||||||
|
data: { id },
|
||||||
|
}))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async softDelete<
|
async softDelete<
|
||||||
@@ -992,11 +1084,24 @@ export default class ProductModuleService<
|
|||||||
},
|
},
|
||||||
sharedContext: Context = {}
|
sharedContext: Context = {}
|
||||||
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
|
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
|
||||||
let [, cascadedEntitiesMap] = await this.softDelete_(
|
let [products, cascadedEntitiesMap] = await this.softDelete_(
|
||||||
productIds,
|
productIds,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const softDeletedProducts = await this.baseRepository_.serialize<
|
||||||
|
ProductTypes.ProductDTO[]
|
||||||
|
>(products, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.eventBusModuleService_?.emit<ProductEventData>(
|
||||||
|
softDeletedProducts.map(({ id }) => ({
|
||||||
|
eventName: ProductEvents.PRODUCT_DELETED,
|
||||||
|
data: { id },
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
let mappedCascadedEntitiesMap
|
let mappedCascadedEntitiesMap
|
||||||
if (returnLinkableKeys) {
|
if (returnLinkableKeys) {
|
||||||
mappedCascadedEntitiesMap = mapObjectTo<
|
mappedCascadedEntitiesMap = mapObjectTo<
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
export * from "./services"
|
export * from "./services"
|
||||||
|
|
||||||
import { IEventBusService } from "@medusajs/types"
|
import { IEventBusModuleService } from "@medusajs/types"
|
||||||
|
|
||||||
export type InitializeModuleInjectableDependencies = {
|
export type InitializeModuleInjectableDependencies = {
|
||||||
eventBusService?: IEventBusService
|
eventBusModuleService?: IEventBusModuleService
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from "./services"
|
export * from "./services"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export * as ProductCategoryServiceTypes from "./product-category"
|
export * as ProductCategoryServiceTypes from "./product-category"
|
||||||
export * as ProductServiceTypes from "./product"
|
export * as ProductServiceTypes from "./product"
|
||||||
export * as ProductVariantServiceTypes from "./product-variant"
|
export * as ProductVariantServiceTypes from "./product-variant"
|
||||||
|
export * as ProductCollectionServiceTypes from "./product-collection"
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
export type ProductCategoryEventData = {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProductCategoryEvents {
|
||||||
|
CATEGORY_UPDATED = "product-category.updated",
|
||||||
|
CATEGORY_CREATED = "product-category.created",
|
||||||
|
CATEGORY_DELETED = "product-category.deleted",
|
||||||
|
}
|
||||||
|
|
||||||
export interface CreateProductCategoryDTO {
|
export interface CreateProductCategoryDTO {
|
||||||
name: string
|
name: string
|
||||||
handle?: string
|
handle?: string
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export type ProductCollectionEventData = {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProductCollectionEvents {
|
||||||
|
COLLECTION_UPDATED = "product-collection.updated",
|
||||||
|
COLLECTION_CREATED = "product-collection.created",
|
||||||
|
COLLECTION_DELETED = "product-collection.deleted",
|
||||||
|
}
|
||||||
@@ -1,5 +1,15 @@
|
|||||||
import { ProductStatus, ProductCategoryDTO } from "@medusajs/types"
|
import { ProductStatus, ProductCategoryDTO } from "@medusajs/types"
|
||||||
|
|
||||||
|
export type ProductEventData = {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ProductEvents {
|
||||||
|
PRODUCT_UPDATED = "product.updated",
|
||||||
|
PRODUCT_CREATED = "product.created",
|
||||||
|
PRODUCT_DELETED = "product.deleted",
|
||||||
|
}
|
||||||
|
|
||||||
export interface UpdateProductDTO {
|
export interface UpdateProductDTO {
|
||||||
id: string
|
id: string
|
||||||
title?: string
|
title?: string
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export interface IEventBusModuleService {
|
|||||||
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: EmitData<T>[]): Promise<void>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user