feat: Revamp of product categories (#7695)
* feat: Normalize the categories interface to match standards * feat: Revamp the product category implementation * fix: Adjustments to code and tests around product categories
This commit is contained in:
1 parent
fbd8eef18b
commit
d862d03de0
35 files changed
+1134
-873
No files matched your search
@@ -684,7 +684,6 @@ medusaIntegrationTestRunner({
|
|||||||
{
|
{
|
||||||
name: "category child 2",
|
name: "category child 2",
|
||||||
parent_category_id: productCategoryChild.id,
|
parent_category_id: productCategoryChild.id,
|
||||||
rank: 2,
|
|
||||||
description: "category child 2",
|
description: "category child 2",
|
||||||
},
|
},
|
||||||
adminHeaders
|
adminHeaders
|
||||||
@@ -697,7 +696,6 @@ medusaIntegrationTestRunner({
|
|||||||
{
|
{
|
||||||
name: "category child 3",
|
name: "category child 3",
|
||||||
parent_category_id: productCategoryChild.id,
|
parent_category_id: productCategoryChild.id,
|
||||||
rank: 2,
|
|
||||||
description: "category child 3",
|
description: "category child 3",
|
||||||
},
|
},
|
||||||
adminHeaders
|
adminHeaders
|
||||||
@@ -767,7 +765,7 @@ medusaIntegrationTestRunner({
|
|||||||
expect(siblingsResponse.data.product_categories).toEqual([
|
expect(siblingsResponse.data.product_categories).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: productCategoryChild3.id,
|
id: productCategoryChild3.id,
|
||||||
rank: 1,
|
rank: 0,
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
@@ -891,7 +889,9 @@ medusaIntegrationTestRunner({
|
|||||||
).data.product_category
|
).data.product_category
|
||||||
})
|
})
|
||||||
|
|
||||||
it("throws an error if invalid ID is sent", async () => {
|
// TODO: In almost all places we use a selector, not an id, to do the update, so throwing a 404 doesn't make sense from the workflow POV
|
||||||
|
// Discuss how we want this handled across all endpoints
|
||||||
|
it.skip("throws an error if invalid ID is sent", async () => {
|
||||||
const error = await api
|
const error = await api
|
||||||
.post(
|
.post(
|
||||||
`/admin/product-categories/not-found-id`,
|
`/admin/product-categories/not-found-id`,
|
||||||
@@ -925,22 +925,6 @@ medusaIntegrationTestRunner({
|
|||||||
expect(error.response.data.type).toEqual("invalid_data")
|
expect(error.response.data.type).toEqual("invalid_data")
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: This seems to be a redundant test, I would remove this in V2
|
|
||||||
it("throws an error if invalid attribute is sent", async () => {
|
|
||||||
const error = await api
|
|
||||||
.post(
|
|
||||||
`/admin/product-categories/${productCategory.id}`,
|
|
||||||
{
|
|
||||||
invalid_property: "string",
|
|
||||||
},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
.catch((e) => e)
|
|
||||||
|
|
||||||
expect(error.response.status).toEqual(400)
|
|
||||||
expect(error.response.data.type).toEqual("invalid_data")
|
|
||||||
})
|
|
||||||
|
|
||||||
it("successfully updates a product category", async () => {
|
it("successfully updates a product category", async () => {
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
`/admin/product-categories/${productCategoryChild2.id}`,
|
`/admin/product-categories/${productCategoryChild2.id}`,
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
CreateProductCategoryDTO,
|
||||||
|
IProductModuleService,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
type CreateProductCategoriesStepInput = {
|
||||||
|
product_categories: CreateProductCategoryDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createProductCategoriesStepId = "create-product-categories"
|
||||||
|
export const createProductCategoriesStep = createStep(
|
||||||
|
createProductCategoriesStepId,
|
||||||
|
async (data: CreateProductCategoriesStepInput, { container }) => {
|
||||||
|
const service = container.resolve<IProductModuleService>(
|
||||||
|
ModuleRegistrationName.PRODUCT
|
||||||
|
)
|
||||||
|
|
||||||
|
const created = await service.createCategories(data.product_categories)
|
||||||
|
|
||||||
|
return new StepResponse(
|
||||||
|
created,
|
||||||
|
created.map((c) => c.id)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
async (createdIds, { container }) => {
|
||||||
|
if (!createdIds?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = container.resolve<IProductModuleService>(
|
||||||
|
ModuleRegistrationName.PRODUCT
|
||||||
|
)
|
||||||
|
|
||||||
|
await service.deleteCategories(createdIds)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
|
||||||
import {
|
|
||||||
CreateProductCategoryDTO,
|
|
||||||
IProductModuleService,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
|
||||||
|
|
||||||
type CreateProductCategoryStepInput = {
|
|
||||||
product_category: CreateProductCategoryDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createProductCategoryStepId = "create-product-category"
|
|
||||||
export const createProductCategoryStep = createStep(
|
|
||||||
createProductCategoryStepId,
|
|
||||||
async (data: CreateProductCategoryStepInput, { container }) => {
|
|
||||||
const service = container.resolve<IProductModuleService>(
|
|
||||||
ModuleRegistrationName.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
const created = await service.createCategory(data.product_category)
|
|
||||||
|
|
||||||
return new StepResponse(created, created.id)
|
|
||||||
},
|
|
||||||
async (createdId, { container }) => {
|
|
||||||
if (!createdId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const service = container.resolve<IProductModuleService>(
|
|
||||||
ModuleRegistrationName.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
await service.deleteCategory(createdId)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { IProductModuleService } from "@medusajs/types"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
export const deleteProductCategoriesStepId = "delete-product-categories"
|
||||||
|
export const deleteProductCategoriesStep = createStep(
|
||||||
|
deleteProductCategoriesStepId,
|
||||||
|
async (ids: string[], { container }) => {
|
||||||
|
const service = container.resolve<IProductModuleService>(
|
||||||
|
ModuleRegistrationName.PRODUCT
|
||||||
|
)
|
||||||
|
|
||||||
|
await service.softDeleteCategories(ids)
|
||||||
|
return new StepResponse(void 0, ids)
|
||||||
|
},
|
||||||
|
async (prevIds, { container }) => {
|
||||||
|
if (!prevIds?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = container.resolve<IProductModuleService>(
|
||||||
|
ModuleRegistrationName.PRODUCT
|
||||||
|
)
|
||||||
|
|
||||||
|
await service.restoreCategories(prevIds)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
|
||||||
import { IProductModuleService } from "@medusajs/types"
|
|
||||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
|
||||||
|
|
||||||
export const deleteProductCategoryStepId = "delete-product-category"
|
|
||||||
export const deleteProductCategoryStep = createStep(
|
|
||||||
deleteProductCategoryStepId,
|
|
||||||
async (id: string, { container }) => {
|
|
||||||
const service = container.resolve<IProductModuleService>(
|
|
||||||
ModuleRegistrationName.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
await service.deleteCategory(id)
|
|
||||||
return new StepResponse(void 0, id)
|
|
||||||
},
|
|
||||||
async (prevId, { container }) => {
|
|
||||||
if (!prevId) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const service = container.resolve<IProductModuleService>(
|
|
||||||
ModuleRegistrationName.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: There is no soft delete support for categories yet
|
|
||||||
// await service.restoreCategory(prevId)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
export * from "./create-product-category"
|
export * from "./create-product-categories"
|
||||||
export * from "./update-product-category"
|
export * from "./update-product-categories"
|
||||||
export * from "./delete-product-category"
|
export * from "./delete-product-categories"
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
FilterableProductCategoryProps,
|
||||||
|
IProductModuleService,
|
||||||
|
UpdateProductCategoryDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
type UpdateProductCategoriesStepInput = {
|
||||||
|
selector: FilterableProductCategoryProps
|
||||||
|
update: UpdateProductCategoryDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateProductCategoriesStepId = "update-product-categories"
|
||||||
|
export const updateProductCategoriesStep = createStep(
|
||||||
|
updateProductCategoriesStepId,
|
||||||
|
async (data: UpdateProductCategoriesStepInput, { container }) => {
|
||||||
|
const service = container.resolve<IProductModuleService>(
|
||||||
|
ModuleRegistrationName.PRODUCT
|
||||||
|
)
|
||||||
|
|
||||||
|
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
||||||
|
data.update,
|
||||||
|
])
|
||||||
|
|
||||||
|
const prevData = await service.listCategories(data.selector, {
|
||||||
|
select: selects,
|
||||||
|
relations,
|
||||||
|
})
|
||||||
|
|
||||||
|
const productCategories = await service.updateCategories(
|
||||||
|
data.selector,
|
||||||
|
data.update
|
||||||
|
)
|
||||||
|
return new StepResponse(productCategories, prevData)
|
||||||
|
},
|
||||||
|
async (prevData, { container }) => {
|
||||||
|
if (!prevData?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = container.resolve<IProductModuleService>(
|
||||||
|
ModuleRegistrationName.PRODUCT
|
||||||
|
)
|
||||||
|
|
||||||
|
await service.upsertCategories(prevData)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
|
||||||
import {
|
|
||||||
IProductModuleService,
|
|
||||||
UpdateProductCategoryDTO,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils"
|
|
||||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
|
||||||
|
|
||||||
type UpdateProductCategoryStepInput = {
|
|
||||||
id: string
|
|
||||||
data: UpdateProductCategoryDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
export const updateProductCategoryStepId = "update-product-category"
|
|
||||||
export const updateProductCategoryStep = createStep(
|
|
||||||
updateProductCategoryStepId,
|
|
||||||
async (data: UpdateProductCategoryStepInput, { container }) => {
|
|
||||||
const service = container.resolve<IProductModuleService>(
|
|
||||||
ModuleRegistrationName.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
|
||||||
data.data,
|
|
||||||
])
|
|
||||||
|
|
||||||
const prevData = await service.listCategories(
|
|
||||||
{ id: data.id },
|
|
||||||
{
|
|
||||||
select: selects,
|
|
||||||
relations,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const updated = await service.updateCategory(data.id, data.data)
|
|
||||||
|
|
||||||
return new StepResponse(updated, prevData)
|
|
||||||
},
|
|
||||||
async (prevData, { container }) => {
|
|
||||||
if (!prevData?.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const service = container.resolve<IProductModuleService>(
|
|
||||||
ModuleRegistrationName.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: Should be removed when bulk update is implemented
|
|
||||||
const category = prevData[0]
|
|
||||||
|
|
||||||
await service.updateCategory(category.id, {
|
|
||||||
name: category.name,
|
|
||||||
description: category.description,
|
|
||||||
is_active: category.is_active,
|
|
||||||
is_internal: category.is_internal,
|
|
||||||
rank: category.rank,
|
|
||||||
handle: category.handle,
|
|
||||||
metadata: category.metadata,
|
|
||||||
parent_category_id: category.parent_category_id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { ProductCategoryWorkflow } from "@medusajs/types"
|
||||||
|
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||||
|
import { createProductCategoriesStep } from "../steps"
|
||||||
|
|
||||||
|
type WorkflowInputData =
|
||||||
|
ProductCategoryWorkflow.CreateProductCategoriesWorkflowInput
|
||||||
|
|
||||||
|
export const createProductCategoriesWorkflowId = "create-product-categories"
|
||||||
|
export const createProductCategoriesWorkflow = createWorkflow(
|
||||||
|
createProductCategoriesWorkflowId,
|
||||||
|
(input: WorkflowData<WorkflowInputData>) => {
|
||||||
|
return createProductCategoriesStep(input)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { ProductCategoryWorkflow } from "@medusajs/types"
|
|
||||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
|
||||||
import { createProductCategoryStep } from "../steps"
|
|
||||||
|
|
||||||
type WorkflowInputData =
|
|
||||||
ProductCategoryWorkflow.CreateProductCategoryWorkflowInput
|
|
||||||
|
|
||||||
export const createProductCategoryWorkflowId = "create-product-category"
|
|
||||||
export const createProductCategoryWorkflow = createWorkflow(
|
|
||||||
createProductCategoryWorkflowId,
|
|
||||||
(input: WorkflowData<WorkflowInputData>) => {
|
|
||||||
const category = createProductCategoryStep(input)
|
|
||||||
|
|
||||||
return category
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||||
|
import { deleteProductCategoriesStep } from "../steps"
|
||||||
|
|
||||||
|
export const deleteProductCategoriesWorkflowId = "delete-product-categories"
|
||||||
|
export const deleteProductCategoriesWorkflow = createWorkflow(
|
||||||
|
deleteProductCategoriesWorkflowId,
|
||||||
|
(input: WorkflowData<string[]>) => {
|
||||||
|
return deleteProductCategoriesStep(input)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
|
||||||
import { deleteProductCategoryStep } from "../steps"
|
|
||||||
|
|
||||||
export const deleteProductCategoryWorkflowId = "delete-product-category"
|
|
||||||
export const deleteProductCategoryWorkflow = createWorkflow(
|
|
||||||
deleteProductCategoryWorkflowId,
|
|
||||||
(input: WorkflowData<string>) => {
|
|
||||||
return deleteProductCategoryStep(input)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
export * from "./create-product-category"
|
export * from "./create-product-categories"
|
||||||
export * from "./update-product-category"
|
export * from "./update-product-categories"
|
||||||
export * from "./delete-product-category"
|
export * from "./delete-product-categories"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { ProductCategoryWorkflow } from "@medusajs/types"
|
||||||
|
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||||
|
import { updateProductCategoriesStep } from "../steps"
|
||||||
|
|
||||||
|
type WorkflowInputData =
|
||||||
|
ProductCategoryWorkflow.UpdateProductCategoriesWorkflowInput
|
||||||
|
|
||||||
|
export const updateProductCategoriesWorkflowId = "update-product-categories"
|
||||||
|
export const updateProductCategoriesWorkflow = createWorkflow(
|
||||||
|
updateProductCategoriesWorkflowId,
|
||||||
|
(input: WorkflowData<WorkflowInputData>) => {
|
||||||
|
return updateProductCategoriesStep(input)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { ProductCategoryWorkflow } from "@medusajs/types"
|
|
||||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
|
||||||
import { updateProductCategoryStep } from "../steps"
|
|
||||||
|
|
||||||
type WorkflowInputData =
|
|
||||||
ProductCategoryWorkflow.UpdateProductCategoryWorkflowInput
|
|
||||||
|
|
||||||
export const updateProductCategoryWorkflowId = "update-product-category"
|
|
||||||
export const updateProductCategoryWorkflow = createWorkflow(
|
|
||||||
updateProductCategoryWorkflowId,
|
|
||||||
(input: WorkflowData<WorkflowInputData>) => {
|
|
||||||
const category = updateProductCategoryStep(input)
|
|
||||||
|
|
||||||
return category
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -104,9 +104,9 @@ export interface TreeRepositoryService<T = any>
|
|||||||
context?: Context
|
context?: Context
|
||||||
): Promise<[T[], number]>
|
): Promise<[T[], number]>
|
||||||
|
|
||||||
create(data: unknown, context?: Context): Promise<T>
|
create(data: unknown[], context?: Context): Promise<T[]>
|
||||||
|
|
||||||
delete(id: string, context?: Context): Promise<void>
|
delete(ids: string[], context?: Context): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -371,6 +371,13 @@ export interface CreateProductCategoryDTO {
|
|||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UpsertProductCategoryDTO extends UpdateProductCategoryDTO {
|
||||||
|
/**
|
||||||
|
* The ID of the product category to update. If not provided, the product category is created. In this case, the `name` property is required.
|
||||||
|
*/
|
||||||
|
id?: string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import {
|
|||||||
UpdateProductTagDTO,
|
UpdateProductTagDTO,
|
||||||
UpdateProductTypeDTO,
|
UpdateProductTypeDTO,
|
||||||
UpdateProductVariantDTO,
|
UpdateProductVariantDTO,
|
||||||
|
UpsertProductCategoryDTO,
|
||||||
UpsertProductCollectionDTO,
|
UpsertProductCollectionDTO,
|
||||||
UpsertProductDTO,
|
UpsertProductDTO,
|
||||||
UpsertProductOptionDTO,
|
UpsertProductOptionDTO,
|
||||||
@@ -2201,6 +2202,30 @@ export interface IProductModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<[ProductCategoryDTO[], number]>
|
): Promise<[ProductCategoryDTO[], number]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to create product categories.
|
||||||
|
*
|
||||||
|
* @param {CreateProductCategoryDTO[]} data - The product categories to be created.
|
||||||
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
|
* @returns {Promise<ProductCategoryDTO[]>} The list of created product categories.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const categories =
|
||||||
|
* await productModuleService.createCategories([
|
||||||
|
* {
|
||||||
|
* name: "Tools",
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* name: "Clothing",
|
||||||
|
* },
|
||||||
|
* ])
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
createCategories(
|
||||||
|
data: CreateProductCategoryDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductCategoryDTO[]>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to create a product category.
|
* This method is used to create a product category.
|
||||||
*
|
*
|
||||||
@@ -2209,48 +2234,173 @@ export interface IProductModuleService extends IModuleService {
|
|||||||
* @returns {Promise<ProductCategoryDTO>} The created product category.
|
* @returns {Promise<ProductCategoryDTO>} The created product category.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const category = await productModuleService.createCategory({
|
* const category =
|
||||||
* name: "Shirts",
|
* await productModuleService.createCategories({
|
||||||
* parent_category_id: null,
|
* name: "Tools",
|
||||||
* })
|
* })
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
createCategory(
|
createCategories(
|
||||||
data: CreateProductCategoryDTO,
|
data: CreateProductCategoryDTO,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<ProductCategoryDTO>
|
): Promise<ProductCategoryDTO>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to update a product category by its ID.
|
* This method updates existing categories, or creates new ones if they don't exist.
|
||||||
*
|
*
|
||||||
* @param {string} categoryId - The ID of the product category to update.
|
* @param {UpsertProductCategoryDTO[]} data - The attributes to update or create for each category.
|
||||||
* @param {UpdateProductCategoryDTO} data - The attributes to update in th product category.
|
|
||||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
* @returns {Promise<ProductCategoryDTO>} The updated product category.
|
* @returns {Promise<ProductCategoryDTO[]>} The updated and created categories.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const category = await productModuleService.updateCategory(
|
* const categories =
|
||||||
* "pcat_123",
|
* await productModuleService.upsertCategories([
|
||||||
* {
|
* {
|
||||||
* name: "Shirts",
|
* id: "pcat_123",
|
||||||
* }
|
* name: "Clothing",
|
||||||
* )
|
* },
|
||||||
|
* {
|
||||||
|
* name: "Tools",
|
||||||
|
* },
|
||||||
|
* ])
|
||||||
*/
|
*/
|
||||||
updateCategory(
|
upsertCategories(
|
||||||
categoryId: string,
|
data: UpsertProductCategoryDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductCategoryDTO[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method updates an existing category, or creates a new one if it doesn't exist.
|
||||||
|
*
|
||||||
|
* @param {UpsertProductCategoryDTO} data - The attributes to update or create for the category.
|
||||||
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
|
* @returns {Promise<ProductCategoryDTO>} The updated or created category.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const category =
|
||||||
|
* await productModuleService.upsertCategories({
|
||||||
|
* id: "pcat_123",
|
||||||
|
* name: "Clothing",
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
upsertCategories(
|
||||||
|
data: UpsertProductCategoryDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductCategoryDTO>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to update a category.
|
||||||
|
*
|
||||||
|
* @param {string} id - The ID of the category to be updated.
|
||||||
|
* @param {UpdateProductCategoryDTO} data - The attributes of the category to be updated
|
||||||
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
|
* @returns {Promise<ProductCategoryDTO>} The updated category.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const category =
|
||||||
|
* await productModuleService.updateCategories("pcat_123", {
|
||||||
|
* title: "Tools",
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
updateCategories(
|
||||||
|
id: string,
|
||||||
data: UpdateProductCategoryDTO,
|
data: UpdateProductCategoryDTO,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<ProductCategoryDTO>
|
): Promise<ProductCategoryDTO>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is used to delete a product category by its ID.
|
* This method is used to update a list of categories matching the specified filters.
|
||||||
*
|
*
|
||||||
* @param {string} categoryId - The ID of the product category to delete.
|
* @param {FilterableProductCategoryProps} selector - The filters specifying which categories to update.
|
||||||
|
* @param {UpdateProductCategoryDTO} data - The attributes to be updated on the selected categories
|
||||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
* @returns {Promise<void>} Resolves when the product category is successfully deleted.
|
* @returns {Promise<ProductCategoryDTO[]>} The updated categories.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* await productModuleService.deleteCategory("pcat_123")
|
* const categories =
|
||||||
|
* await productModuleService.updateCategories(
|
||||||
|
* {
|
||||||
|
* id: ["pcat_123", "pcat_321"],
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* title: "Tools",
|
||||||
|
* }
|
||||||
|
* )
|
||||||
*/
|
*/
|
||||||
deleteCategory(categoryId: string, sharedContext?: Context): Promise<void>
|
updateCategories(
|
||||||
|
selector: FilterableProductCategoryProps,
|
||||||
|
data: UpdateProductCategoryDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductCategoryDTO[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to delete categories by their ID.
|
||||||
|
*
|
||||||
|
* @param {string[]} productCategoryIds - The IDs of the product categories to be updated.
|
||||||
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
|
* @returns {Promise<void>} Resolves when the product options are successfully deleted.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* await productModuleService.deleteCategories([
|
||||||
|
* "pcat_123",
|
||||||
|
* "pcat_321",
|
||||||
|
* ])
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
deleteCategories(
|
||||||
|
productCategoryIds: string[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to delete product categories. Unlike the {@link deleteCategories} method, this method won't completely remove the category. It can still be accessed or retrieved using methods like {@link retrieveCategories} if you pass the `withDeleted` property to the `config` object parameter.
|
||||||
|
*
|
||||||
|
* The soft-deleted categories can be restored using the {@link restoreCategories} method.
|
||||||
|
*
|
||||||
|
* @param {string[]} categoryIds - The IDs of the categories to soft-delete.
|
||||||
|
* @param {SoftDeleteReturn<TReturnableLinkableKeys>} config -
|
||||||
|
* Configurations determining which relations to soft delete along with the each of the categories. You can pass to its `returnLinkableKeys`
|
||||||
|
* property any of the category's relation attribute names.
|
||||||
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
|
* @returns {Promise<Record<string, string[]> | void>}
|
||||||
|
* An object that includes the IDs of related records that were also soft deleted. The object's keys are the ID attribute names of the category entity's relations.
|
||||||
|
*
|
||||||
|
* If there are no related records, the promise resolved to `void`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* await productModuleService.softDeleteCategories([
|
||||||
|
* "pcat_123",
|
||||||
|
* "pcat_321",
|
||||||
|
* ])
|
||||||
|
*/
|
||||||
|
softDeleteCategories<TReturnableLinkableKeys extends string = string>(
|
||||||
|
categoryIds: string[],
|
||||||
|
config?: SoftDeleteReturn<TReturnableLinkableKeys>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<Record<string, string[]> | void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to restore categories which were deleted using the {@link softDelete} method.
|
||||||
|
*
|
||||||
|
* @param {string[]} categoryIds - The IDs of the categories to restore.
|
||||||
|
* @param {RestoreReturn<TReturnableLinkableKeys>} config -
|
||||||
|
* Configurations determining which relations to restore along with each of the categories. You can pass to its `returnLinkableKeys`
|
||||||
|
* property any of the category's relation attribute names.
|
||||||
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
|
* @returns {Promise<Record<string, string[]> | void>}
|
||||||
|
* An object that includes the IDs of related records that were restored. The object's keys are the ID attribute names of the product entity's relations.
|
||||||
|
*
|
||||||
|
* If there are no related records that were restored, the promise resolved to `void`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* await productModuleService.restoreCategories([
|
||||||
|
* "pcat_123",
|
||||||
|
* "pcat_321",
|
||||||
|
* ])
|
||||||
|
*/
|
||||||
|
restoreCategories<TReturnableLinkableKeys extends string = string>(
|
||||||
|
categoryIds: string[],
|
||||||
|
config?: RestoreReturn<TReturnableLinkableKeys>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<Record<string, string[]> | void>
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
import { LinkWorkflowInput } from "../../common"
|
import { LinkWorkflowInput } from "../../common"
|
||||||
import {
|
import {
|
||||||
CreateProductCategoryDTO,
|
CreateProductCategoryDTO,
|
||||||
|
FilterableProductCategoryProps,
|
||||||
UpdateProductCategoryDTO,
|
UpdateProductCategoryDTO,
|
||||||
} from "../../product"
|
} from "../../product"
|
||||||
|
|
||||||
export interface CreateProductCategoryWorkflowInput {
|
export interface CreateProductCategoriesWorkflowInput {
|
||||||
product_category: CreateProductCategoryDTO
|
product_categories: CreateProductCategoryDTO[]
|
||||||
}
|
}
|
||||||
|
export interface UpdateProductCategoriesWorkflowInput {
|
||||||
// TODO: Should we converted to bulk update
|
selector: FilterableProductCategoryProps
|
||||||
export interface UpdateProductCategoryWorkflowInput {
|
update: UpdateProductCategoryDTO
|
||||||
// selector: FilterableProductCategoryProps
|
|
||||||
// data: UpdateProductCategoryDTO
|
|
||||||
id: string
|
|
||||||
data: UpdateProductCategoryDTO
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BatchUpdateProductsOnCategoryWorkflowInput
|
export interface BatchUpdateProductsOnCategoryWorkflowInput
|
||||||
|
|||||||
@@ -241,11 +241,15 @@ export class MikroOrmBaseTreeRepository<
|
|||||||
throw new Error("Method not implemented.")
|
throw new Error("Method not implemented.")
|
||||||
}
|
}
|
||||||
|
|
||||||
create(data: unknown, context?: Context): Promise<T> {
|
create(data: unknown[], context?: Context): Promise<T[]> {
|
||||||
throw new Error("Method not implemented.")
|
throw new Error("Method not implemented.")
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(id: string, context?: Context): Promise<void> {
|
update(data: unknown[], context?: Context): Promise<T[]> {
|
||||||
|
throw new Error("Method not implemented.")
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(ids: string[], context?: Context): Promise<void> {
|
||||||
throw new Error("Method not implemented.")
|
throw new Error("Method not implemented.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,16 @@ const eventBaseNames: [
|
|||||||
"productVariant",
|
"productVariant",
|
||||||
"productOption",
|
"productOption",
|
||||||
"productType",
|
"productType",
|
||||||
"productTag"
|
"productTag",
|
||||||
] = ["product", "productVariant", "productOption", "productType", "productTag"]
|
"productCategory"
|
||||||
|
] = [
|
||||||
|
"product",
|
||||||
|
"productVariant",
|
||||||
|
"productOption",
|
||||||
|
"productType",
|
||||||
|
"productTag",
|
||||||
|
"productCategory",
|
||||||
|
]
|
||||||
|
|
||||||
export const ProductEvents = buildEventNamesFromEntityName(
|
export const ProductEvents = buildEventNamesFromEntityName(
|
||||||
eventBaseNames,
|
eventBaseNames,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
deleteProductCategoryWorkflow,
|
deleteProductCategoriesWorkflow,
|
||||||
updateProductCategoryWorkflow,
|
updateProductCategoriesWorkflow,
|
||||||
} from "@medusajs/core-flows"
|
} from "@medusajs/core-flows"
|
||||||
import { AdminProductCategoryResponse } from "@medusajs/types"
|
import { AdminProductCategoryResponse } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
@@ -41,9 +41,8 @@ export const POST = async (
|
|||||||
) => {
|
) => {
|
||||||
const { id } = req.params
|
const { id } = req.params
|
||||||
|
|
||||||
// TODO: Should be converted to bulk update
|
await updateProductCategoriesWorkflow(req.scope).run({
|
||||||
await updateProductCategoryWorkflow(req.scope).run({
|
input: { selector: { id }, update: req.validatedBody },
|
||||||
input: { id, data: req.validatedBody },
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const [category] = await refetchEntities(
|
const [category] = await refetchEntities(
|
||||||
@@ -62,8 +61,8 @@ export const DELETE = async (
|
|||||||
) => {
|
) => {
|
||||||
const id = req.params.id
|
const id = req.params.id
|
||||||
|
|
||||||
await deleteProductCategoryWorkflow(req.scope).run({
|
await deleteProductCategoriesWorkflow(req.scope).run({
|
||||||
input: id,
|
input: [id],
|
||||||
})
|
})
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createProductCategoryWorkflow } from "@medusajs/core-flows"
|
import { createProductCategoriesWorkflow } from "@medusajs/core-flows"
|
||||||
import {
|
import {
|
||||||
AdminProductCategoryListResponse,
|
AdminProductCategoryListResponse,
|
||||||
AdminProductCategoryResponse,
|
AdminProductCategoryResponse,
|
||||||
@@ -37,13 +37,13 @@ export const POST = async (
|
|||||||
req: AuthenticatedMedusaRequest<AdminCreateProductCategoryType>,
|
req: AuthenticatedMedusaRequest<AdminCreateProductCategoryType>,
|
||||||
res: MedusaResponse<AdminProductCategoryResponse>
|
res: MedusaResponse<AdminProductCategoryResponse>
|
||||||
) => {
|
) => {
|
||||||
const { result } = await createProductCategoryWorkflow(req.scope).run({
|
const { result } = await createProductCategoriesWorkflow(req.scope).run({
|
||||||
input: { product_category: req.validatedBody },
|
input: { product_categories: [req.validatedBody] },
|
||||||
})
|
})
|
||||||
|
|
||||||
const [category] = await refetchEntities(
|
const [category] = await refetchEntities(
|
||||||
"product_category",
|
"product_category",
|
||||||
{ id: result.id, ...req.filterableFields },
|
{ id: result[0].id, ...req.filterableFields },
|
||||||
req.scope,
|
req.scope,
|
||||||
req.remoteQueryConfig.fields
|
req.remoteQueryConfig.fields
|
||||||
)
|
)
|
||||||
|
|||||||
+117
-117
@@ -66,126 +66,126 @@ export const productCategoriesRankData = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const eletronicsCategoriesData = eval(`[
|
export const eletronicsCategoriesData = [
|
||||||
{
|
{
|
||||||
id: "electronics",
|
id: "electronics",
|
||||||
name: "Electronics",
|
name: "Electronics",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "computers",
|
id: "computers",
|
||||||
name: "Computers & Accessories",
|
name: "Computers & Accessories",
|
||||||
parent_category_id: "electronics",
|
parent_category_id: "electronics",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "desktops",
|
id: "desktops",
|
||||||
name: "Desktops",
|
name: "Desktops",
|
||||||
parent_category_id: "computers",
|
parent_category_id: "computers",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "gaming-desktops",
|
id: "gaming-desktops",
|
||||||
name: "Gaming Desktops",
|
name: "Gaming Desktops",
|
||||||
parent_category_id: "desktops",
|
parent_category_id: "desktops",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "office-desktops",
|
id: "office-desktops",
|
||||||
name: "Office Desktops",
|
name: "Office Desktops",
|
||||||
parent_category_id: "desktops",
|
parent_category_id: "desktops",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "laptops",
|
id: "laptops",
|
||||||
name: "Laptops",
|
name: "Laptops",
|
||||||
parent_category_id: "computers",
|
parent_category_id: "computers",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "gaming-laptops",
|
id: "gaming-laptops",
|
||||||
name: "Gaming Laptops",
|
name: "Gaming Laptops",
|
||||||
parent_category_id: "laptops",
|
parent_category_id: "laptops",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "budget-gaming",
|
id: "budget-gaming",
|
||||||
name: "Budget Gaming Laptops",
|
name: "Budget Gaming Laptops",
|
||||||
parent_category_id: "gaming-laptops",
|
parent_category_id: "gaming-laptops",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "high-performance",
|
id: "high-performance",
|
||||||
name: "High Performance Gaming Laptops",
|
name: "High Performance Gaming Laptops",
|
||||||
parent_category_id: "gaming-laptops",
|
parent_category_id: "gaming-laptops",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "vr-ready",
|
id: "vr-ready",
|
||||||
name: "VR-Ready High Performance Gaming Laptops",
|
name: "VR-Ready High Performance Gaming Laptops",
|
||||||
parent_category_id: "high-performance",
|
parent_category_id: "high-performance",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "4k-gaming",
|
id: "4k-gaming",
|
||||||
name: "4K Gaming Laptops",
|
name: "4K Gaming Laptops",
|
||||||
parent_category_id: "high-performance",
|
parent_category_id: "high-performance",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "ultrabooks",
|
id: "ultrabooks",
|
||||||
name: "Ultrabooks",
|
name: "Ultrabooks",
|
||||||
parent_category_id: "laptops",
|
parent_category_id: "laptops",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "thin-light",
|
id: "thin-light",
|
||||||
name: "Thin & Light Ultrabooks",
|
name: "Thin & Light Ultrabooks",
|
||||||
parent_category_id: "ultrabooks",
|
parent_category_id: "ultrabooks",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "convertible-ultrabooks",
|
id: "convertible-ultrabooks",
|
||||||
name: "Convertible Ultrabooks",
|
name: "Convertible Ultrabooks",
|
||||||
parent_category_id: "ultrabooks",
|
parent_category_id: "ultrabooks",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "touchscreen-ultrabooks",
|
id: "touchscreen-ultrabooks",
|
||||||
name: "Touchscreen Ultrabooks",
|
name: "Touchscreen Ultrabooks",
|
||||||
parent_category_id: "convertible-ultrabooks",
|
parent_category_id: "convertible-ultrabooks",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "detachable-ultrabooks",
|
id: "detachable-ultrabooks",
|
||||||
name: "Detachable Ultrabooks",
|
name: "Detachable Ultrabooks",
|
||||||
parent_category_id: "convertible-ultrabooks",
|
parent_category_id: "convertible-ultrabooks",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: "mobile",
|
id: "mobile",
|
||||||
name: "Mobile Phones & Accessories",
|
name: "Mobile Phones & Accessories",
|
||||||
parent_category_id: "electronics",
|
parent_category_id: "electronics",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "smartphones",
|
id: "smartphones",
|
||||||
name: "Smartphones",
|
name: "Smartphones",
|
||||||
parent_category_id: "mobile",
|
parent_category_id: "mobile",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "android-phones",
|
id: "android-phones",
|
||||||
name: "Android Phones",
|
name: "Android Phones",
|
||||||
parent_category_id: "smartphones",
|
parent_category_id: "smartphones",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "flagship-phones",
|
id: "flagship-phones",
|
||||||
name: "Flagship Smartphones",
|
name: "Flagship Smartphones",
|
||||||
parent_category_id: "android-phones",
|
parent_category_id: "android-phones",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "budget-phones",
|
id: "budget-phones",
|
||||||
name: "Budget Smartphones",
|
name: "Budget Smartphones",
|
||||||
parent_category_id: "android-phones",
|
parent_category_id: "android-phones",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "iphones",
|
id: "iphones",
|
||||||
name: "iPhones",
|
name: "iPhones",
|
||||||
parent_category_id: "smartphones",
|
parent_category_id: "smartphones",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "pro-phones",
|
id: "pro-phones",
|
||||||
name: "Pro Models",
|
name: "Pro Models",
|
||||||
parent_category_id: "iphones",
|
parent_category_id: "iphones",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "mini-phones",
|
id: "mini-phones",
|
||||||
name: "Mini Models",
|
name: "Mini Models",
|
||||||
parent_category_id: "iphones",
|
parent_category_id: "iphones",
|
||||||
},
|
},
|
||||||
]`)
|
]
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
|
||||||
import { ProductCategory } from "@models"
|
|
||||||
|
|
||||||
export async function createProductCategories(
|
|
||||||
manager: SqlEntityManager,
|
|
||||||
categoriesData: any[]
|
|
||||||
): Promise<ProductCategory[]> {
|
|
||||||
const categories: ProductCategory[] = []
|
|
||||||
|
|
||||||
for (let categoryData of categoriesData) {
|
|
||||||
let categoryDataClone = { ...categoryData }
|
|
||||||
let parentCategory: ProductCategory | null = null
|
|
||||||
const parentCategoryId = categoryDataClone.parent_category_id as string
|
|
||||||
delete categoryDataClone.parent_category_id
|
|
||||||
|
|
||||||
if (parentCategoryId) {
|
|
||||||
parentCategory = await manager.findOne(ProductCategory, parentCategoryId)
|
|
||||||
}
|
|
||||||
|
|
||||||
const category = manager.create(ProductCategory, {
|
|
||||||
...categoryDataClone,
|
|
||||||
parent_category: parentCategory,
|
|
||||||
})
|
|
||||||
|
|
||||||
categories.push(category)
|
|
||||||
}
|
|
||||||
|
|
||||||
await manager.persistAndFlush(categories)
|
|
||||||
|
|
||||||
return categories
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ import { ProductCategoryService } from "@services"
|
|||||||
|
|
||||||
import { Modules } from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
import { moduleIntegrationTestRunner } from "medusa-test-utils"
|
import { moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||||
import { createProductCategories } from "../__fixtures__/product-category"
|
|
||||||
import {
|
import {
|
||||||
eletronicsCategoriesData,
|
eletronicsCategoriesData,
|
||||||
productCategoriesData,
|
productCategoriesData,
|
||||||
@@ -28,10 +27,9 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
|
|
||||||
describe("list", () => {
|
describe("list", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await createProductCategories(
|
for (const entry of productCategoriesData) {
|
||||||
MikroOrmWrapper.forkManager(),
|
await service.create([entry])
|
||||||
productCategoriesData
|
}
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("lists all product categories", async () => {
|
it("lists all product categories", async () => {
|
||||||
@@ -131,33 +129,33 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-0",
|
id: "category-0",
|
||||||
handle: "category-0",
|
handle: "category-0",
|
||||||
mpath: "category-0.",
|
mpath: "category-0",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1",
|
id: "category-1",
|
||||||
handle: "category-1",
|
handle: "category-1",
|
||||||
mpath: "category-0.category-1.",
|
mpath: "category-0.category-1",
|
||||||
parent_category_id: "category-0",
|
parent_category_id: "category-0",
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-a",
|
id: "category-1-a",
|
||||||
handle: "category-1-a",
|
handle: "category-1-a",
|
||||||
mpath: "category-0.category-1.category-1-a.",
|
mpath: "category-0.category-1.category-1-a",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-b",
|
id: "category-1-b",
|
||||||
handle: "category-1-b",
|
handle: "category-1-b",
|
||||||
mpath: "category-0.category-1.category-1-b.",
|
mpath: "category-0.category-1.category-1-b",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-b-1",
|
id: "category-1-b-1",
|
||||||
handle: "category-1-b-1",
|
handle: "category-1-b-1",
|
||||||
mpath:
|
mpath:
|
||||||
"category-0.category-1.category-1-b.category-1-b-1.",
|
"category-0.category-1.category-1-b.category-1-b-1",
|
||||||
parent_category_id: "category-1-b",
|
parent_category_id: "category-1-b",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
@@ -189,20 +187,20 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
{
|
{
|
||||||
id: "category-1-a",
|
id: "category-1-a",
|
||||||
handle: "category-1-a",
|
handle: "category-1-a",
|
||||||
mpath: "category-0.category-1.category-1-a.",
|
mpath: "category-0.category-1.category-1-a",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "category-1-b",
|
id: "category-1-b",
|
||||||
handle: "category-1-b",
|
handle: "category-1-b",
|
||||||
mpath: "category-0.category-1.category-1-b.",
|
mpath: "category-0.category-1.category-1-b",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-b-1",
|
id: "category-1-b-1",
|
||||||
handle: "category-1-b-1",
|
handle: "category-1-b-1",
|
||||||
mpath: "category-0.category-1.category-1-b.category-1-b-1.",
|
mpath: "category-0.category-1.category-1-b.category-1-b-1",
|
||||||
parent_category_id: "category-1-b",
|
parent_category_id: "category-1-b",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
@@ -212,10 +210,9 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("includes the entire list of parents when include_ancestors_tree is true", async () => {
|
it("includes the entire list of parents when include_ancestors_tree is true", async () => {
|
||||||
await createProductCategories(
|
for (const entry of eletronicsCategoriesData) {
|
||||||
MikroOrmWrapper.forkManager(),
|
await service.create([entry])
|
||||||
eletronicsCategoriesData
|
}
|
||||||
)
|
|
||||||
|
|
||||||
const productCategoryResults = await service.list(
|
const productCategoryResults = await service.list(
|
||||||
{
|
{
|
||||||
@@ -236,34 +233,34 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
id: "4k-gaming",
|
id: "4k-gaming",
|
||||||
handle: "4k-gaming-laptops",
|
handle: "4k-gaming-laptops",
|
||||||
mpath:
|
mpath:
|
||||||
"electronics.computers.laptops.gaming-laptops.high-performance.4k-gaming.",
|
"electronics.computers.laptops.gaming-laptops.high-performance.4k-gaming",
|
||||||
parent_category_id: "high-performance",
|
parent_category_id: "high-performance",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "high-performance",
|
id: "high-performance",
|
||||||
parent_category_id: "gaming-laptops",
|
parent_category_id: "gaming-laptops",
|
||||||
handle: "high-performance-gaming-laptops",
|
handle: "high-performance-gaming-laptops",
|
||||||
mpath:
|
mpath:
|
||||||
"electronics.computers.laptops.gaming-laptops.high-performance.",
|
"electronics.computers.laptops.gaming-laptops.high-performance",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "gaming-laptops",
|
id: "gaming-laptops",
|
||||||
handle: "gaming-laptops",
|
handle: "gaming-laptops",
|
||||||
mpath: "electronics.computers.laptops.gaming-laptops.",
|
mpath: "electronics.computers.laptops.gaming-laptops",
|
||||||
parent_category_id: "laptops",
|
parent_category_id: "laptops",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "laptops",
|
id: "laptops",
|
||||||
parent_category_id: "computers",
|
parent_category_id: "computers",
|
||||||
handle: "laptops",
|
handle: "laptops",
|
||||||
mpath: "electronics.computers.laptops.",
|
mpath: "electronics.computers.laptops",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "computers",
|
id: "computers",
|
||||||
handle: "computers-&-accessories",
|
handle: "computers-&-accessories",
|
||||||
mpath: "electronics.computers.",
|
mpath: "electronics.computers",
|
||||||
parent_category_id: "electronics",
|
parent_category_id: "electronics",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "electronics",
|
id: "electronics",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
handle: "electronics",
|
handle: "electronics",
|
||||||
mpath: "electronics.",
|
mpath: "electronics",
|
||||||
parent_category: null,
|
parent_category: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -275,10 +272,9 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("includes the entire list of descendants when include_descendants_tree is true", async () => {
|
it("includes the entire list of descendants when include_descendants_tree is true", async () => {
|
||||||
await createProductCategories(
|
for (const entry of eletronicsCategoriesData) {
|
||||||
MikroOrmWrapper.forkManager(),
|
await service.create([entry])
|
||||||
eletronicsCategoriesData
|
}
|
||||||
)
|
|
||||||
|
|
||||||
const productCategoryResults = await service.list(
|
const productCategoryResults = await service.list(
|
||||||
{
|
{
|
||||||
@@ -298,14 +294,14 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
{
|
{
|
||||||
id: "gaming-laptops",
|
id: "gaming-laptops",
|
||||||
handle: "gaming-laptops",
|
handle: "gaming-laptops",
|
||||||
mpath: "electronics.computers.laptops.gaming-laptops.",
|
mpath: "electronics.computers.laptops.gaming-laptops",
|
||||||
parent_category_id: "laptops",
|
parent_category_id: "laptops",
|
||||||
category_children: [
|
category_children: [
|
||||||
{
|
{
|
||||||
id: "budget-gaming",
|
id: "budget-gaming",
|
||||||
handle: "budget-gaming-laptops",
|
handle: "budget-gaming-laptops",
|
||||||
mpath:
|
mpath:
|
||||||
"electronics.computers.laptops.gaming-laptops.budget-gaming.",
|
"electronics.computers.laptops.gaming-laptops.budget-gaming",
|
||||||
parent_category_id: "gaming-laptops",
|
parent_category_id: "gaming-laptops",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
},
|
},
|
||||||
@@ -313,14 +309,14 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
id: "high-performance",
|
id: "high-performance",
|
||||||
handle: "high-performance-gaming-laptops",
|
handle: "high-performance-gaming-laptops",
|
||||||
mpath:
|
mpath:
|
||||||
"electronics.computers.laptops.gaming-laptops.high-performance.",
|
"electronics.computers.laptops.gaming-laptops.high-performance",
|
||||||
parent_category_id: "gaming-laptops",
|
parent_category_id: "gaming-laptops",
|
||||||
category_children: expect.arrayContaining([
|
category_children: expect.arrayContaining([
|
||||||
{
|
{
|
||||||
id: "4k-gaming",
|
id: "4k-gaming",
|
||||||
handle: "4k-gaming-laptops",
|
handle: "4k-gaming-laptops",
|
||||||
mpath:
|
mpath:
|
||||||
"electronics.computers.laptops.gaming-laptops.high-performance.4k-gaming.",
|
"electronics.computers.laptops.gaming-laptops.high-performance.4k-gaming",
|
||||||
parent_category_id: "high-performance",
|
parent_category_id: "high-performance",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
},
|
},
|
||||||
@@ -328,7 +324,7 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
id: "vr-ready",
|
id: "vr-ready",
|
||||||
handle: "vr-ready-high-performance-gaming-laptops",
|
handle: "vr-ready-high-performance-gaming-laptops",
|
||||||
mpath:
|
mpath:
|
||||||
"electronics.computers.laptops.gaming-laptops.high-performance.vr-ready.",
|
"electronics.computers.laptops.gaming-laptops.high-performance.vr-ready",
|
||||||
parent_category_id: "high-performance",
|
parent_category_id: "high-performance",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
},
|
},
|
||||||
@@ -340,10 +336,9 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("includes the entire list of descendants an parents when include_descendants_tree and include_ancestors_tree are true", async () => {
|
it("includes the entire list of descendants an parents when include_descendants_tree and include_ancestors_tree are true", async () => {
|
||||||
await createProductCategories(
|
for (const entry of eletronicsCategoriesData) {
|
||||||
MikroOrmWrapper.forkManager(),
|
await service.create([entry])
|
||||||
eletronicsCategoriesData
|
}
|
||||||
)
|
|
||||||
|
|
||||||
const productCategoryResults = await service.list(
|
const productCategoryResults = await service.list(
|
||||||
{
|
{
|
||||||
@@ -364,22 +359,22 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
{
|
{
|
||||||
id: "gaming-laptops",
|
id: "gaming-laptops",
|
||||||
handle: "gaming-laptops",
|
handle: "gaming-laptops",
|
||||||
mpath: "electronics.computers.laptops.gaming-laptops.",
|
mpath: "electronics.computers.laptops.gaming-laptops",
|
||||||
parent_category_id: "laptops",
|
parent_category_id: "laptops",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "laptops",
|
id: "laptops",
|
||||||
handle: "laptops",
|
handle: "laptops",
|
||||||
mpath: "electronics.computers.laptops.",
|
mpath: "electronics.computers.laptops",
|
||||||
parent_category_id: "computers",
|
parent_category_id: "computers",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "computers",
|
id: "computers",
|
||||||
handle: "computers-&-accessories",
|
handle: "computers-&-accessories",
|
||||||
mpath: "electronics.computers.",
|
mpath: "electronics.computers",
|
||||||
parent_category_id: "electronics",
|
parent_category_id: "electronics",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "electronics",
|
id: "electronics",
|
||||||
handle: "electronics",
|
handle: "electronics",
|
||||||
mpath: "electronics.",
|
mpath: "electronics",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
parent_category: null,
|
parent_category: null,
|
||||||
},
|
},
|
||||||
@@ -390,14 +385,14 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
id: "budget-gaming",
|
id: "budget-gaming",
|
||||||
handle: "budget-gaming-laptops",
|
handle: "budget-gaming-laptops",
|
||||||
mpath:
|
mpath:
|
||||||
"electronics.computers.laptops.gaming-laptops.budget-gaming.",
|
"electronics.computers.laptops.gaming-laptops.budget-gaming",
|
||||||
parent_category_id: "gaming-laptops",
|
parent_category_id: "gaming-laptops",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "high-performance",
|
id: "high-performance",
|
||||||
handle: "high-performance-gaming-laptops",
|
handle: "high-performance-gaming-laptops",
|
||||||
mpath:
|
mpath:
|
||||||
"electronics.computers.laptops.gaming-laptops.high-performance.",
|
"electronics.computers.laptops.gaming-laptops.high-performance",
|
||||||
parent_category_id: "gaming-laptops",
|
parent_category_id: "gaming-laptops",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -424,17 +419,17 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
{
|
{
|
||||||
id: "category-1-a",
|
id: "category-1-a",
|
||||||
handle: "category-1-a",
|
handle: "category-1-a",
|
||||||
mpath: "category-0.category-1.category-1-a.",
|
mpath: "category-0.category-1.category-1-a",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "category-1",
|
id: "category-1",
|
||||||
handle: "category-1",
|
handle: "category-1",
|
||||||
mpath: "category-0.category-1.",
|
mpath: "category-0.category-1",
|
||||||
parent_category_id: "category-0",
|
parent_category_id: "category-0",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "category-0",
|
id: "category-0",
|
||||||
handle: "category-0",
|
handle: "category-0",
|
||||||
mpath: "category-0.",
|
mpath: "category-0",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
parent_category: null,
|
parent_category: null,
|
||||||
},
|
},
|
||||||
@@ -443,17 +438,17 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
{
|
{
|
||||||
id: "category-1-b",
|
id: "category-1-b",
|
||||||
handle: "category-1-b",
|
handle: "category-1-b",
|
||||||
mpath: "category-0.category-1.category-1-b.",
|
mpath: "category-0.category-1.category-1-b",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "category-1",
|
id: "category-1",
|
||||||
handle: "category-1",
|
handle: "category-1",
|
||||||
mpath: "category-0.category-1.",
|
mpath: "category-0.category-1",
|
||||||
parent_category_id: "category-0",
|
parent_category_id: "category-0",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "category-0",
|
id: "category-0",
|
||||||
handle: "category-0",
|
handle: "category-0",
|
||||||
mpath: "category-0.",
|
mpath: "category-0",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
parent_category: null,
|
parent_category: null,
|
||||||
},
|
},
|
||||||
@@ -482,17 +477,17 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
{
|
{
|
||||||
id: "category-1-a",
|
id: "category-1-a",
|
||||||
handle: "category-1-a",
|
handle: "category-1-a",
|
||||||
mpath: "category-0.category-1.category-1-a.",
|
mpath: "category-0.category-1.category-1-a",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "category-1",
|
id: "category-1",
|
||||||
handle: "category-1",
|
handle: "category-1",
|
||||||
mpath: "category-0.category-1.",
|
mpath: "category-0.category-1",
|
||||||
parent_category_id: "category-0",
|
parent_category_id: "category-0",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "category-0",
|
id: "category-0",
|
||||||
handle: "category-0",
|
handle: "category-0",
|
||||||
mpath: "category-0.",
|
mpath: "category-0",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
parent_category: null,
|
parent_category: null,
|
||||||
},
|
},
|
||||||
@@ -502,17 +497,17 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
{
|
{
|
||||||
id: "category-1-b",
|
id: "category-1-b",
|
||||||
handle: "category-1-b",
|
handle: "category-1-b",
|
||||||
mpath: "category-0.category-1.category-1-b.",
|
mpath: "category-0.category-1.category-1-b",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "category-1",
|
id: "category-1",
|
||||||
handle: "category-1",
|
handle: "category-1",
|
||||||
mpath: "category-0.category-1.",
|
mpath: "category-0.category-1",
|
||||||
parent_category_id: "category-0",
|
parent_category_id: "category-0",
|
||||||
parent_category: {
|
parent_category: {
|
||||||
id: "category-0",
|
id: "category-0",
|
||||||
handle: "category-0",
|
handle: "category-0",
|
||||||
mpath: "category-0.",
|
mpath: "category-0",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
parent_category: null,
|
parent_category: null,
|
||||||
},
|
},
|
||||||
@@ -521,7 +516,7 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
{
|
{
|
||||||
id: "category-1-b-1",
|
id: "category-1-b-1",
|
||||||
handle: "category-1-b-1",
|
handle: "category-1-b-1",
|
||||||
mpath: "category-0.category-1.category-1-b.category-1-b-1.",
|
mpath: "category-0.category-1.category-1-b.category-1-b-1",
|
||||||
parent_category_id: "category-1-b",
|
parent_category_id: "category-1-b",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -549,19 +544,19 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-0",
|
id: "category-0",
|
||||||
handle: "category-0",
|
handle: "category-0",
|
||||||
mpath: "category-0.",
|
mpath: "category-0",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1",
|
id: "category-1",
|
||||||
handle: "category-1",
|
handle: "category-1",
|
||||||
mpath: "category-0.category-1.",
|
mpath: "category-0.category-1",
|
||||||
parent_category_id: "category-0",
|
parent_category_id: "category-0",
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-a",
|
id: "category-1-a",
|
||||||
handle: "category-1-a",
|
handle: "category-1-a",
|
||||||
mpath: "category-0.category-1.category-1-a.",
|
mpath: "category-0.category-1.category-1-a",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
@@ -577,10 +572,9 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
const categoryOneId = "category-1"
|
const categoryOneId = "category-1"
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await createProductCategories(
|
for (const entry of productCategoriesData) {
|
||||||
MikroOrmWrapper.forkManager(),
|
await service.create([entry])
|
||||||
productCategoriesData
|
}
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return category for the given id", async () => {
|
it("should return category for the given id", async () => {
|
||||||
@@ -659,10 +653,9 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
|
|
||||||
describe("listAndCount", () => {
|
describe("listAndCount", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await createProductCategories(
|
for (const entry of productCategoriesData) {
|
||||||
MikroOrmWrapper.forkManager(),
|
await service.create([entry])
|
||||||
productCategoriesData
|
}
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return categories and count based on take and skip", async () => {
|
it("should return categories and count based on take and skip", async () => {
|
||||||
@@ -800,33 +793,33 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-0",
|
id: "category-0",
|
||||||
handle: "category-0",
|
handle: "category-0",
|
||||||
mpath: "category-0.",
|
mpath: "category-0",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1",
|
id: "category-1",
|
||||||
handle: "category-1",
|
handle: "category-1",
|
||||||
mpath: "category-0.category-1.",
|
mpath: "category-0.category-1",
|
||||||
parent_category_id: "category-0",
|
parent_category_id: "category-0",
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-a",
|
id: "category-1-a",
|
||||||
handle: "category-1-a",
|
handle: "category-1-a",
|
||||||
mpath: "category-0.category-1.category-1-a.",
|
mpath: "category-0.category-1.category-1-a",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-b",
|
id: "category-1-b",
|
||||||
handle: "category-1-b",
|
handle: "category-1-b",
|
||||||
mpath: "category-0.category-1.category-1-b.",
|
mpath: "category-0.category-1.category-1-b",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-b-1",
|
id: "category-1-b-1",
|
||||||
handle: "category-1-b-1",
|
handle: "category-1-b-1",
|
||||||
mpath:
|
mpath:
|
||||||
"category-0.category-1.category-1-b.category-1-b-1.",
|
"category-0.category-1.category-1-b.category-1-b-1",
|
||||||
parent_category_id: "category-1-b",
|
parent_category_id: "category-1-b",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
@@ -861,19 +854,19 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-0",
|
id: "category-0",
|
||||||
handle: "category-0",
|
handle: "category-0",
|
||||||
mpath: "category-0.",
|
mpath: "category-0",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1",
|
id: "category-1",
|
||||||
handle: "category-1",
|
handle: "category-1",
|
||||||
mpath: "category-0.category-1.",
|
mpath: "category-0.category-1",
|
||||||
parent_category_id: "category-0",
|
parent_category_id: "category-0",
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-a",
|
id: "category-1-a",
|
||||||
handle: "category-1-a",
|
handle: "category-1-a",
|
||||||
mpath: "category-0.category-1.category-1-a.",
|
mpath: "category-0.category-1.category-1-a",
|
||||||
parent_category_id: "category-1",
|
parent_category_id: "category-1",
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
@@ -887,10 +880,12 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
it("should create a category successfully", async () => {
|
it("should create a category successfully", async () => {
|
||||||
await service.create({
|
await service.create([
|
||||||
name: "New Category",
|
{
|
||||||
parent_category_id: null,
|
name: "New Category",
|
||||||
})
|
parent_category_id: null,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const [productCategory] = await service.list(
|
const [productCategory] = await service.list(
|
||||||
{
|
{
|
||||||
@@ -910,16 +905,17 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
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.create({
|
await service.create([
|
||||||
name: "New Category",
|
{
|
||||||
parent_category_id: null,
|
name: "New Category",
|
||||||
rank: 0,
|
parent_category_id: null,
|
||||||
})
|
rank: 0,
|
||||||
|
},
|
||||||
await service.create({
|
{
|
||||||
name: "New Category 2",
|
name: "New Category 2",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
})
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const [productCategoryNew] = await service.list(
|
const [productCategoryNew] = await service.list(
|
||||||
{
|
{
|
||||||
@@ -930,17 +926,19 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(productCategoryNew).toEqual(
|
expect(JSON.parse(JSON.stringify(productCategoryNew))).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
name: "New Category 2",
|
name: "New Category 2",
|
||||||
rank: 1,
|
rank: 1,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
await service.create({
|
await service.create([
|
||||||
name: "New Category 2.1",
|
{
|
||||||
parent_category_id: productCategoryNew.id,
|
name: "New Category 2.1",
|
||||||
})
|
parent_category_id: productCategoryNew.id,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const [productCategoryWithParent] = await service.list(
|
const [productCategoryWithParent] = await service.list(
|
||||||
{
|
{
|
||||||
@@ -971,10 +969,10 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
let categories
|
let categories
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
categories = await createProductCategories(
|
categories = []
|
||||||
MikroOrmWrapper.forkManager(),
|
for (const entry of productCategoriesRankData) {
|
||||||
productCategoriesRankData
|
categories.push((await service.create([entry]))[0])
|
||||||
)
|
}
|
||||||
|
|
||||||
productCategoryZero = categories[0]
|
productCategoryZero = categories[0]
|
||||||
productCategoryOne = categories[1]
|
productCategoryOne = categories[1]
|
||||||
@@ -985,9 +983,12 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should update the name of the category successfully", async () => {
|
it("should update the name of the category successfully", async () => {
|
||||||
await service.update(productCategoryZero.id, {
|
await service.update([
|
||||||
name: "New Category",
|
{
|
||||||
})
|
id: productCategoryZero.id,
|
||||||
|
name: "New Category",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const productCategory = await service.retrieve(
|
const productCategory = await service.retrieve(
|
||||||
productCategoryZero.id,
|
productCategoryZero.id,
|
||||||
@@ -1003,9 +1004,12 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
let error
|
let error
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await service.update("does-not-exist", {
|
await service.update([
|
||||||
name: "New Category",
|
{
|
||||||
})
|
id: "does-not-exist",
|
||||||
|
name: "New Category",
|
||||||
|
},
|
||||||
|
])
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e
|
error = e
|
||||||
}
|
}
|
||||||
@@ -1016,9 +1020,12 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should reorder rank successfully in the same parent", async () => {
|
it("should reorder rank successfully in the same parent", async () => {
|
||||||
await service.update(productCategoryTwo.id, {
|
await service.update([
|
||||||
rank: 0,
|
{
|
||||||
})
|
id: productCategoryTwo.id,
|
||||||
|
rank: 0,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const productCategories = await service.list(
|
const productCategories = await service.list(
|
||||||
{
|
{
|
||||||
@@ -1048,10 +1055,13 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should reorder rank successfully when changing parent", async () => {
|
it("should reorder rank successfully when changing parent", async () => {
|
||||||
await service.update(productCategoryTwo.id, {
|
await service.update([
|
||||||
rank: 0,
|
{
|
||||||
parent_category_id: productCategoryZero.id,
|
id: productCategoryTwo.id,
|
||||||
})
|
rank: 0,
|
||||||
|
parent_category_id: productCategoryZero.id,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const productCategories = await service.list(
|
const productCategories = await service.list(
|
||||||
{
|
{
|
||||||
@@ -1085,10 +1095,13 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should reorder rank successfully when changing parent and in first position", async () => {
|
it("should reorder rank successfully when changing parent and in first position", async () => {
|
||||||
await service.update(productCategoryTwo.id, {
|
await service.update([
|
||||||
rank: 0,
|
{
|
||||||
parent_category_id: productCategoryZero.id,
|
id: productCategoryTwo.id,
|
||||||
})
|
rank: 0,
|
||||||
|
parent_category_id: productCategoryZero.id,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const productCategories = await service.list(
|
const productCategories = await service.list(
|
||||||
{
|
{
|
||||||
@@ -1129,10 +1142,10 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
let categories
|
let categories
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
categories = await createProductCategories(
|
categories = []
|
||||||
MikroOrmWrapper.forkManager(),
|
for (const entry of productCategoriesRankData) {
|
||||||
productCategoriesRankData
|
categories.push((await service.create([entry]))[0])
|
||||||
)
|
}
|
||||||
|
|
||||||
productCategoryZero = categories[0]
|
productCategoryZero = categories[0]
|
||||||
productCategoryOne = categories[1]
|
productCategoryOne = categories[1]
|
||||||
@@ -1143,7 +1156,7 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
let error
|
let error
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await service.delete("does-not-exist")
|
await service.delete(["does-not-exist"])
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e
|
error = e
|
||||||
}
|
}
|
||||||
@@ -1157,7 +1170,7 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
let error
|
let error
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await service.delete(productCategoryZero.id)
|
await service.delete([productCategoryZero.id])
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e
|
error = e
|
||||||
}
|
}
|
||||||
@@ -1168,7 +1181,7 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should reorder siblings rank successfully on deleting", async () => {
|
it("should reorder siblings rank successfully on deleting", async () => {
|
||||||
await service.delete(productCategoryOne.id)
|
await service.delete([productCategoryOne.id])
|
||||||
|
|
||||||
const productCategories = await service.list(
|
const productCategories = await service.list(
|
||||||
{
|
{
|
||||||
|
|||||||
+51
-51
@@ -6,7 +6,6 @@ import {
|
|||||||
MockEventBusService,
|
MockEventBusService,
|
||||||
moduleIntegrationTestRunner,
|
moduleIntegrationTestRunner,
|
||||||
} from "medusa-test-utils"
|
} from "medusa-test-utils"
|
||||||
import { createProductCategories } from "../../__fixtures__/product-category"
|
|
||||||
import { productCategoriesRankData } from "../../__fixtures__/product-category/data"
|
import { productCategoriesRankData } from "../../__fixtures__/product-category/data"
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
@@ -52,18 +51,12 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
productCategories = await createProductCategories(
|
productCategories = []
|
||||||
testManager,
|
for (const entry of productCategoriesData) {
|
||||||
productCategoriesData
|
productCategories.push(await service.createCategories(entry))
|
||||||
)
|
}
|
||||||
|
|
||||||
productCategoryOne = productCategories[0]
|
productCategoryOne = productCategories[0]
|
||||||
productCategoryTwo = productCategories[1]
|
productCategoryTwo = productCategories[1]
|
||||||
|
|
||||||
await testManager.persistAndFlush([
|
|
||||||
productCategoryOne,
|
|
||||||
productCategoryTwo,
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@@ -260,7 +253,7 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
|
|
||||||
describe("createCategory", () => {
|
describe("createCategory", () => {
|
||||||
it("should create a category successfully", async () => {
|
it("should create a category successfully", async () => {
|
||||||
await service.createCategory({
|
await service.createCategories({
|
||||||
name: "New Category",
|
name: "New Category",
|
||||||
parent_category_id: productCategoryOne.id,
|
parent_category_id: productCategoryOne.id,
|
||||||
})
|
})
|
||||||
@@ -285,26 +278,28 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
it("should emit events through event bus", async () => {
|
it("should emit events through event bus", async () => {
|
||||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||||
|
|
||||||
const category = await service.createCategory({
|
const category = await service.createCategories({
|
||||||
name: "New Category",
|
name: "New Category",
|
||||||
parent_category_id: productCategoryOne.id,
|
parent_category_id: productCategoryOne.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
expect(eventBusSpy).toHaveBeenCalledWith({
|
expect(eventBusSpy).toHaveBeenCalledWith([
|
||||||
data: { id: category.id },
|
expect.objectContaining({
|
||||||
eventName: "product-category.created",
|
data: { id: category.id },
|
||||||
})
|
eventName: "productService.product-category.created",
|
||||||
|
}),
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
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.createCategories({
|
||||||
name: "New Category",
|
name: "New Category",
|
||||||
parent_category_id: productCategoryOne.id,
|
parent_category_id: productCategoryOne.id,
|
||||||
rank: 0,
|
rank: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
await service.createCategory({
|
await service.createCategories({
|
||||||
name: "New Category 2",
|
name: "New Category 2",
|
||||||
parent_category_id: productCategoryOne.id,
|
parent_category_id: productCategoryOne.id,
|
||||||
})
|
})
|
||||||
@@ -325,7 +320,7 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
await service.createCategory({
|
await service.createCategories({
|
||||||
name: "New Category 2.1",
|
name: "New Category 2.1",
|
||||||
parent_category_id: productCategoryNew.id,
|
parent_category_id: productCategoryNew.id,
|
||||||
})
|
})
|
||||||
@@ -359,12 +354,10 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
let categories
|
let categories
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const testManager = await MikroOrmWrapper.forkManager()
|
categories = []
|
||||||
|
for (const entry of productCategoriesRankData) {
|
||||||
categories = await createProductCategories(
|
categories.push(await service.createCategories(entry))
|
||||||
testManager,
|
}
|
||||||
productCategoriesRankData
|
|
||||||
)
|
|
||||||
|
|
||||||
productCategoryZero = categories[0]
|
productCategoryZero = categories[0]
|
||||||
productCategoryOne = categories[1]
|
productCategoryOne = categories[1]
|
||||||
@@ -376,19 +369,23 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
|
|
||||||
it("should emit events through event bus", async () => {
|
it("should emit events through event bus", async () => {
|
||||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||||
await service.updateCategory(productCategoryZero.id, {
|
eventBusSpy.mockClear()
|
||||||
|
|
||||||
|
await service.updateCategories(productCategoryZero.id, {
|
||||||
name: "New Category",
|
name: "New Category",
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
expect(eventBusSpy).toHaveBeenCalledWith({
|
expect(eventBusSpy).toHaveBeenCalledWith([
|
||||||
data: { id: productCategoryZero.id },
|
expect.objectContaining({
|
||||||
eventName: "product-category.updated",
|
data: { id: productCategoryZero.id },
|
||||||
})
|
eventName: "productService.product-category.updated",
|
||||||
|
}),
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
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.updateCategories(productCategoryZero.id, {
|
||||||
name: "New Category",
|
name: "New Category",
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -406,7 +403,7 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
let error
|
let error
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await service.updateCategory("does-not-exist", {
|
await service.updateCategories("does-not-exist", {
|
||||||
name: "New Category",
|
name: "New Category",
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -414,12 +411,12 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect(error.message).toEqual(
|
expect(error.message).toEqual(
|
||||||
`ProductCategory not found ({ id: 'does-not-exist' })`
|
`ProductCategory with id: does-not-exist was not found`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should reorder rank successfully in the same parent", async () => {
|
it("should reorder rank successfully in the same parent", async () => {
|
||||||
await service.updateCategory(productCategoryTwo.id, {
|
await service.updateCategories(productCategoryTwo.id, {
|
||||||
rank: 0,
|
rank: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -451,7 +448,7 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should reorder rank successfully when changing parent", async () => {
|
it("should reorder rank successfully when changing parent", async () => {
|
||||||
await service.updateCategory(productCategoryTwo.id, {
|
await service.updateCategories(productCategoryTwo.id, {
|
||||||
rank: 0,
|
rank: 0,
|
||||||
parent_category_id: productCategoryZero.id,
|
parent_category_id: productCategoryZero.id,
|
||||||
})
|
})
|
||||||
@@ -488,7 +485,7 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should reorder rank successfully when changing parent and in first position", async () => {
|
it("should reorder rank successfully when changing parent and in first position", async () => {
|
||||||
await service.updateCategory(productCategoryTwo.id, {
|
await service.updateCategories(productCategoryTwo.id, {
|
||||||
rank: 0,
|
rank: 0,
|
||||||
parent_category_id: productCategoryZero.id,
|
parent_category_id: productCategoryZero.id,
|
||||||
})
|
})
|
||||||
@@ -532,34 +529,37 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
let categories
|
let categories
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const testManager = await MikroOrmWrapper.forkManager()
|
categories = []
|
||||||
|
for (const entry of productCategoriesRankData) {
|
||||||
categories = await createProductCategories(
|
categories.push(await service.createCategories(entry))
|
||||||
testManager,
|
}
|
||||||
productCategoriesRankData
|
|
||||||
)
|
|
||||||
|
|
||||||
productCategoryZero = categories[0]
|
productCategoryZero = categories[0]
|
||||||
productCategoryOne = categories[1]
|
productCategoryOne = categories[1]
|
||||||
productCategoryTwo = categories[2]
|
productCategoryTwo = categories[2]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO: Normalize delete events as well
|
||||||
it("should emit events through event bus", async () => {
|
it("should emit events through event bus", async () => {
|
||||||
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit")
|
||||||
await service.deleteCategory(productCategoryOne.id)
|
eventBusSpy.mockClear()
|
||||||
|
|
||||||
|
await service.deleteCategories([productCategoryOne.id])
|
||||||
|
|
||||||
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
expect(eventBusSpy).toHaveBeenCalledTimes(1)
|
||||||
expect(eventBusSpy).toHaveBeenCalledWith({
|
expect(eventBusSpy).toHaveBeenCalledWith([
|
||||||
data: { id: productCategoryOne.id },
|
expect.objectContaining({
|
||||||
eventName: "product-category.deleted",
|
data: { id: productCategoryOne.id },
|
||||||
})
|
eventName: "product-category.deleted",
|
||||||
|
}),
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await service.deleteCategory("does-not-exist")
|
await service.deleteCategories(["does-not-exist"])
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e
|
error = e
|
||||||
}
|
}
|
||||||
@@ -573,7 +573,7 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
let error
|
let error
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await service.deleteCategory(productCategoryZero.id)
|
await service.deleteCategories([productCategoryZero.id])
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e
|
error = e
|
||||||
}
|
}
|
||||||
@@ -584,7 +584,7 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should reorder siblings rank successfully on deleting", async () => {
|
it("should reorder siblings rank successfully on deleting", async () => {
|
||||||
await service.deleteCategory(productCategoryOne.id)
|
await service.deleteCategories([productCategoryOne.id])
|
||||||
|
|
||||||
const productCategories = await service.listCategories(
|
const productCategories = await service.listCategories(
|
||||||
{
|
{
|
||||||
|
|||||||
+5
-6
@@ -1,5 +1,5 @@
|
|||||||
import { Modules } from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
import { IProductModuleService } from "@medusajs/types"
|
import { IProductModuleService, ProductCategoryDTO } from "@medusajs/types"
|
||||||
import { kebabCase, ProductStatus } from "@medusajs/utils"
|
import { kebabCase, ProductStatus } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
Product,
|
Product,
|
||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
createCollections,
|
createCollections,
|
||||||
createTypes,
|
createTypes,
|
||||||
} from "../../__fixtures__/product"
|
} from "../../__fixtures__/product"
|
||||||
import { createProductCategories } from "../../__fixtures__/product-category"
|
|
||||||
|
|
||||||
jest.setTimeout(300000)
|
jest.setTimeout(300000)
|
||||||
|
|
||||||
@@ -101,10 +100,10 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
productTypeOne = types[0]
|
productTypeOne = types[0]
|
||||||
productTypeTwo = types[1]
|
productTypeTwo = types[1]
|
||||||
|
|
||||||
const categories = await createProductCategories(
|
const categories: ProductCategoryDTO[] = []
|
||||||
testManager,
|
for (const entry of productCategoriesData) {
|
||||||
productCategoriesData
|
categories.push(await service.createCategories(entry))
|
||||||
)
|
}
|
||||||
|
|
||||||
productCategoryOne = categories[0]
|
productCategoryOne = categories[0]
|
||||||
productCategoryTwo = categories[1]
|
productCategoryTwo = categories[1]
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ import { Modules } from "@medusajs/modules-sdk"
|
|||||||
import { IProductModuleService, ProductDTO } from "@medusajs/types"
|
import { IProductModuleService, ProductDTO } from "@medusajs/types"
|
||||||
import { kebabCase, ProductStatus } from "@medusajs/utils"
|
import { kebabCase, ProductStatus } from "@medusajs/utils"
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
import { ProductService } from "@services"
|
import { ProductService, ProductCategoryService } from "@services"
|
||||||
import { moduleIntegrationTestRunner } from "medusa-test-utils"
|
import { moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||||
import { createProductCategories } from "../__fixtures__/product-category"
|
|
||||||
import {
|
import {
|
||||||
categoriesData,
|
categoriesData,
|
||||||
productsData,
|
productsData,
|
||||||
@@ -25,15 +24,18 @@ jest.setTimeout(30000)
|
|||||||
|
|
||||||
type Service = IProductModuleService & {
|
type Service = IProductModuleService & {
|
||||||
productService_: ProductService
|
productService_: ProductService
|
||||||
|
productCategoryService_: ProductCategoryService
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleIntegrationTestRunner<Service>({
|
moduleIntegrationTestRunner<Service>({
|
||||||
moduleName: Modules.PRODUCT,
|
moduleName: Modules.PRODUCT,
|
||||||
testSuite: ({ MikroOrmWrapper, service: moduleService }) => {
|
testSuite: ({ MikroOrmWrapper, service: moduleService }) => {
|
||||||
let service: ProductService
|
let service: ProductService
|
||||||
|
let categoryService: ProductCategoryService
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service = moduleService.productService_
|
service = moduleService.productService_
|
||||||
|
categoryService = moduleService.productCategoryService_
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("Product Service", () => {
|
describe("Product Service", () => {
|
||||||
@@ -351,10 +353,11 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
|
|
||||||
products = await createProductAndTags(testManager, productsData)
|
products = await createProductAndTags(testManager, productsData)
|
||||||
workingProduct = products.find((p) => p.id === "test-1") as Product
|
workingProduct = products.find((p) => p.id === "test-1") as Product
|
||||||
categories = await createProductCategories(
|
categories = []
|
||||||
testManager,
|
for (const entry of categoriesData) {
|
||||||
categoriesData
|
categories.push((await categoryService.create([entry]))[0])
|
||||||
)
|
}
|
||||||
|
|
||||||
workingCategory = (await testManager.findOne(
|
workingCategory = (await testManager.findOne(
|
||||||
ProductCategory,
|
ProductCategory,
|
||||||
"category-1"
|
"category-1"
|
||||||
@@ -400,21 +403,21 @@ moduleIntegrationTestRunner<Service>({
|
|||||||
id: "category-0",
|
id: "category-0",
|
||||||
name: "category 0",
|
name: "category 0",
|
||||||
handle: "category-0",
|
handle: "category-0",
|
||||||
mpath: "category-0.",
|
mpath: "category-0",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "category-1",
|
id: "category-1",
|
||||||
name: "category 1",
|
name: "category 1",
|
||||||
handle: "category-1",
|
handle: "category-1",
|
||||||
mpath: "category-0.category-1.",
|
mpath: "category-0.category-1",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "category-1-a",
|
id: "category-1-a",
|
||||||
name: "category 1 a",
|
name: "category 1 a",
|
||||||
handle: "category-1-a",
|
handle: "category-1-a",
|
||||||
mpath: "category-0.category-1.category-1-a.",
|
mpath: "category-0.category-1.category-1-a",
|
||||||
parent_category_id: null,
|
parent_category_id: null,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class ProductCategory {
|
|||||||
nullable: false,
|
nullable: false,
|
||||||
default: 0,
|
default: 0,
|
||||||
})
|
})
|
||||||
rank?: number
|
rank: number
|
||||||
|
|
||||||
@ManyToOne(() => ProductCategory, {
|
@ManyToOne(() => ProductCategory, {
|
||||||
columnType: "text",
|
columnType: "text",
|
||||||
@@ -130,22 +130,7 @@ class ProductCategory {
|
|||||||
this.handle = kebabCase(this.name)
|
this.handle = kebabCase(this.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { em } = args
|
this.mpath = `${this.mpath ? this.mpath + "." : ""}${this.id}`
|
||||||
|
|
||||||
let parentCategory: ProductCategory | null = null
|
|
||||||
|
|
||||||
if (this.parent_category_id) {
|
|
||||||
parentCategory = await em.findOne(
|
|
||||||
ProductCategory,
|
|
||||||
this.parent_category_id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentCategory) {
|
|
||||||
this.mpath = `${parentCategory?.mpath}${this.id}.`
|
|
||||||
} else {
|
|
||||||
this.mpath = `${this.id}.`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,20 +12,7 @@ import {
|
|||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
import { ProductCategory } from "@models"
|
import { ProductCategory } from "@models"
|
||||||
|
import { UpdateCategoryInput } from "@types"
|
||||||
export type ReorderConditions = {
|
|
||||||
targetCategoryId: string
|
|
||||||
originalParentId: string | null
|
|
||||||
targetParentId: string | null | undefined
|
|
||||||
originalRank: number
|
|
||||||
targetRank: number | undefined
|
|
||||||
shouldChangeParent: boolean
|
|
||||||
shouldChangeRank: boolean
|
|
||||||
shouldIncrementRank: boolean
|
|
||||||
shouldDeleteElement: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export const tempReorderRank = 99999
|
|
||||||
|
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeRepository<ProductCategory> {
|
export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeRepository<ProductCategory> {
|
||||||
@@ -144,10 +131,7 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
|||||||
if (include.ancestors) {
|
if (include.ancestors) {
|
||||||
let parent = ""
|
let parent = ""
|
||||||
cat.mpath?.split(".").forEach((mpath) => {
|
cat.mpath?.split(".").forEach((mpath) => {
|
||||||
if (mpath === "") {
|
parentMpaths.add(parent + mpath)
|
||||||
return
|
|
||||||
}
|
|
||||||
parentMpaths.add(parent + mpath + ".")
|
|
||||||
parent += mpath + "."
|
parent += mpath + "."
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -257,249 +241,280 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(id: string, context: Context = {}): Promise<void> {
|
async delete(ids: string[], context: Context = {}): Promise<void> {
|
||||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
const productCategory = await manager.findOneOrFail(
|
await this.baseDelete(ids, context)
|
||||||
ProductCategory,
|
await manager.nativeDelete(ProductCategory, { id: ids }, {})
|
||||||
{ id },
|
}
|
||||||
{
|
|
||||||
populate: ["category_children"],
|
async softDelete(
|
||||||
}
|
ids: string[],
|
||||||
|
context: Context = {}
|
||||||
|
): Promise<[ProductCategory[], Record<string, unknown[]>]> {
|
||||||
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
|
await this.baseDelete(ids, context)
|
||||||
|
|
||||||
|
const categories = await Promise.all(
|
||||||
|
ids.map(async (id) => {
|
||||||
|
const productCategory = await manager.findOneOrFail(ProductCategory, {
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
manager.assign(productCategory, { deleted_at: new Date() })
|
||||||
|
return productCategory
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if (productCategory.category_children.length > 0) {
|
manager.persist(categories)
|
||||||
throw new MedusaError(
|
return [categories, {}]
|
||||||
MedusaError.Types.NOT_ALLOWED,
|
}
|
||||||
`Deleting ProductCategory (${id}) with category children is not allowed`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const conditions = this.fetchReorderConditions(
|
async restore(
|
||||||
productCategory,
|
ids: string[],
|
||||||
{
|
context: Context = {}
|
||||||
parent_category_id: productCategory.parent_category_id,
|
): Promise<[ProductCategory[], Record<string, unknown[]>]> {
|
||||||
rank: productCategory.rank,
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
},
|
const categories = await Promise.all(
|
||||||
true
|
ids.map(async (id) => {
|
||||||
|
const productCategory = await manager.findOneOrFail(ProductCategory, {
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
manager.assign(productCategory, { deleted_at: null })
|
||||||
|
return productCategory
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
await this.performReordering(manager, conditions)
|
manager.persist(categories)
|
||||||
await manager.nativeDelete(ProductCategory, { id: id }, {})
|
return [categories, {}]
|
||||||
|
}
|
||||||
|
|
||||||
|
async baseDelete(ids: string[], context: Context = {}): Promise<void> {
|
||||||
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
ids.map(async (id) => {
|
||||||
|
const productCategory = await manager.findOneOrFail(
|
||||||
|
ProductCategory,
|
||||||
|
{ id },
|
||||||
|
{
|
||||||
|
populate: ["category_children"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (productCategory.category_children.length > 0) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
|
`Deleting ProductCategory (${id}) with category children is not allowed`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.rerankSiblingsAfterDeletion(manager, productCategory)
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(
|
async create(
|
||||||
data: ProductTypes.CreateProductCategoryDTO,
|
data: ProductTypes.CreateProductCategoryDTO[],
|
||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<ProductCategory> {
|
): Promise<ProductCategory[]> {
|
||||||
const categoryData = { ...data }
|
|
||||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
const siblings = await manager.find(ProductCategory, {
|
|
||||||
parent_category_id: categoryData?.parent_category_id || null,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!isDefined(categoryData.rank)) {
|
const categories = await Promise.all(
|
||||||
categoryData.rank = siblings.length
|
data.map(async (entry, i) => {
|
||||||
}
|
const categoryData: Partial<ProductCategory> = { ...entry }
|
||||||
|
const siblingsCount = await manager.count(ProductCategory, {
|
||||||
|
parent_category_id: categoryData?.parent_category_id || null,
|
||||||
|
})
|
||||||
|
|
||||||
const productCategory = manager.create(ProductCategory, categoryData)
|
if (!isDefined(categoryData.rank)) {
|
||||||
|
categoryData.rank = siblingsCount + i
|
||||||
|
} else {
|
||||||
|
if (categoryData.rank > siblingsCount + i) {
|
||||||
|
categoryData.rank = siblingsCount + i
|
||||||
|
}
|
||||||
|
|
||||||
manager.persist(productCategory)
|
await this.rerankSiblingsAfterCreation(manager, categoryData)
|
||||||
|
}
|
||||||
|
|
||||||
return productCategory
|
// Set the base mpath if the category has a parent. The model `create` hook will append the own id to the base mpath.
|
||||||
|
const parentCategoryId =
|
||||||
|
categoryData.parent_category_id ?? categoryData.parent_category?.id
|
||||||
|
|
||||||
|
if (parentCategoryId) {
|
||||||
|
const parentCategory = await manager.findOne(
|
||||||
|
ProductCategory,
|
||||||
|
parentCategoryId
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!parentCategory) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
|
`Parent category with id: '${parentCategoryId}' does not exist`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryData.mpath = parentCategory.mpath
|
||||||
|
}
|
||||||
|
|
||||||
|
return manager.create(ProductCategory, categoryData as ProductCategory)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
manager.persist(categories)
|
||||||
|
return categories
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(
|
async update(
|
||||||
id: string,
|
data: UpdateCategoryInput[],
|
||||||
data: ProductTypes.UpdateProductCategoryDTO,
|
|
||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<ProductCategory> {
|
): Promise<ProductCategory[]> {
|
||||||
const categoryData = { ...data }
|
|
||||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
const productCategory = await manager.findOneOrFail(ProductCategory, { id })
|
const categories = await Promise.all(
|
||||||
|
data.map(async (entry, i) => {
|
||||||
|
const categoryData: Partial<ProductCategory> = { ...entry }
|
||||||
|
const productCategory = await manager.findOneOrFail(ProductCategory, {
|
||||||
|
id: categoryData.id,
|
||||||
|
})
|
||||||
|
|
||||||
const conditions = this.fetchReorderConditions(
|
if (
|
||||||
productCategory,
|
categoryData.parent_category_id &&
|
||||||
categoryData
|
categoryData.parent_category_id !== productCategory.parent_category_id
|
||||||
|
) {
|
||||||
|
const newParentCategory = await manager.findOne(
|
||||||
|
ProductCategory,
|
||||||
|
categoryData.parent_category_id
|
||||||
|
)
|
||||||
|
if (!newParentCategory) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
|
`Parent category with id: '${categoryData.parent_category_id}' does not exist`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryData.mpath = `${newParentCategory.mpath}.${productCategory.id}`
|
||||||
|
|
||||||
|
const siblingsCount = await manager.count(ProductCategory, {
|
||||||
|
parent_category_id: categoryData.parent_category_id,
|
||||||
|
})
|
||||||
|
if (!isDefined(categoryData.rank)) {
|
||||||
|
categoryData.rank = siblingsCount + i
|
||||||
|
} else {
|
||||||
|
if (categoryData.rank > siblingsCount + i) {
|
||||||
|
categoryData.rank = siblingsCount + i
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.rerankSiblingsAfterCreation(manager, categoryData)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.rerankSiblingsAfterDeletion(manager, productCategory)
|
||||||
|
}
|
||||||
|
// In the case of the parent being updated, we do a delete/create reranking. If only the rank was updated, we need to shift all siblings
|
||||||
|
else if (isDefined(categoryData.rank)) {
|
||||||
|
const siblingsCount = await manager.count(ProductCategory, {
|
||||||
|
parent_category_id: productCategory.parent_category_id,
|
||||||
|
})
|
||||||
|
|
||||||
|
// We don't cout the updated category itself.
|
||||||
|
if (categoryData.rank > siblingsCount - 1 + i) {
|
||||||
|
categoryData.rank = siblingsCount - 1 + i
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.rerankAllSiblings(
|
||||||
|
manager,
|
||||||
|
productCategory,
|
||||||
|
categoryData as ProductCategory
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in categoryData) {
|
||||||
|
if (isDefined(categoryData[key])) {
|
||||||
|
productCategory[key] = categoryData[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.assign(productCategory, categoryData)
|
||||||
|
return productCategory
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if (conditions.shouldChangeRank || conditions.shouldChangeParent) {
|
manager.persist(categories)
|
||||||
categoryData.rank = tempReorderRank
|
return categories
|
||||||
}
|
|
||||||
|
|
||||||
// await this.transformParentIdToEntity(categoryData)
|
|
||||||
|
|
||||||
for (const key in categoryData) {
|
|
||||||
if (isDefined(categoryData[key])) {
|
|
||||||
productCategory[key] = categoryData[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
manager.assign(productCategory, categoryData)
|
|
||||||
manager.persist(productCategory)
|
|
||||||
|
|
||||||
await this.performReordering(manager, conditions)
|
|
||||||
|
|
||||||
return productCategory
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fetchReorderConditions(
|
protected async rerankSiblingsAfterDeletion(
|
||||||
productCategory: ProductCategory,
|
|
||||||
data: ProductTypes.UpdateProductCategoryDTO,
|
|
||||||
shouldDeleteElement = false
|
|
||||||
): ReorderConditions {
|
|
||||||
const originalParentId = productCategory.parent_category_id || null
|
|
||||||
const targetParentId = data.parent_category_id
|
|
||||||
const originalRank = productCategory.rank || 0
|
|
||||||
const targetRank = data.rank
|
|
||||||
const shouldChangeParent =
|
|
||||||
targetParentId !== undefined && targetParentId !== originalParentId
|
|
||||||
const shouldChangeRank =
|
|
||||||
shouldChangeParent ||
|
|
||||||
(isDefined(targetRank) && originalRank !== targetRank)
|
|
||||||
|
|
||||||
return {
|
|
||||||
targetCategoryId: productCategory.id,
|
|
||||||
originalParentId,
|
|
||||||
targetParentId,
|
|
||||||
originalRank,
|
|
||||||
targetRank,
|
|
||||||
shouldChangeParent,
|
|
||||||
shouldChangeRank,
|
|
||||||
shouldIncrementRank: false,
|
|
||||||
shouldDeleteElement,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async performReordering(
|
|
||||||
manager: SqlEntityManager,
|
manager: SqlEntityManager,
|
||||||
conditions: ReorderConditions
|
removedSibling: Partial<ProductCategory>
|
||||||
): Promise<void> {
|
) {
|
||||||
const { shouldChangeParent, shouldChangeRank, shouldDeleteElement } =
|
const affectedSiblings = await manager.find(ProductCategory, {
|
||||||
conditions
|
parent_category_id: removedSibling.parent_category_id,
|
||||||
|
rank: { $gt: removedSibling.rank },
|
||||||
|
})
|
||||||
|
|
||||||
if (!(shouldChangeParent || shouldChangeRank || shouldDeleteElement)) {
|
const updatedSiblings = affectedSiblings.map((sibling) => {
|
||||||
|
manager.assign(sibling, { rank: sibling.rank - 1 })
|
||||||
|
return sibling
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.persist(updatedSiblings)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async rerankSiblingsAfterCreation(
|
||||||
|
manager: SqlEntityManager,
|
||||||
|
addedSibling: Partial<ProductCategory>
|
||||||
|
) {
|
||||||
|
const affectedSiblings = await manager.find(ProductCategory, {
|
||||||
|
parent_category_id: addedSibling.parent_category_id,
|
||||||
|
rank: { $gte: addedSibling.rank },
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedSiblings = affectedSiblings.map((sibling) => {
|
||||||
|
manager.assign(sibling, { rank: sibling.rank + 1 })
|
||||||
|
return sibling
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.persist(updatedSiblings)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async rerankAllSiblings(
|
||||||
|
manager: SqlEntityManager,
|
||||||
|
originalSibling: Partial<ProductCategory> & { rank: number },
|
||||||
|
updatedSibling: Partial<ProductCategory> & { rank: number }
|
||||||
|
) {
|
||||||
|
if (originalSibling.rank === updatedSibling.rank) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we change parent, we need to shift the siblings to eliminate the
|
if (originalSibling.rank < updatedSibling.rank) {
|
||||||
// rank occupied by the targetCategory in the original parent.
|
const siblings = await manager.find(
|
||||||
shouldChangeParent &&
|
ProductCategory,
|
||||||
(await this.shiftSiblings(manager, {
|
{
|
||||||
...conditions,
|
parent_category_id: originalSibling.parent_category_id,
|
||||||
targetRank: conditions.originalRank,
|
rank: { $gt: originalSibling.rank, $lte: updatedSibling.rank },
|
||||||
targetParentId: conditions.originalParentId,
|
},
|
||||||
}))
|
{ orderBy: { rank: "ASC" } }
|
||||||
|
)
|
||||||
|
|
||||||
// If we change parent, we need to shift the siblings of the new parent
|
const updatedSiblings = siblings.map((sibling) => {
|
||||||
// to create a rank that the targetCategory will occupy.
|
manager.assign(sibling, { rank: sibling.rank - 1 })
|
||||||
shouldChangeParent &&
|
return sibling
|
||||||
shouldChangeRank &&
|
})
|
||||||
(await this.shiftSiblings(manager, {
|
|
||||||
...conditions,
|
|
||||||
shouldIncrementRank: true,
|
|
||||||
}))
|
|
||||||
|
|
||||||
// If we only change rank, we need to shift the siblings
|
manager.persist(updatedSiblings)
|
||||||
// to create a rank that the targetCategory will occupy.
|
|
||||||
;((!shouldChangeParent && shouldChangeRank) || shouldDeleteElement) &&
|
|
||||||
(await this.shiftSiblings(manager, {
|
|
||||||
...conditions,
|
|
||||||
targetParentId: conditions.originalParentId,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async shiftSiblings(
|
|
||||||
manager: SqlEntityManager,
|
|
||||||
conditions: ReorderConditions
|
|
||||||
): Promise<void> {
|
|
||||||
let { shouldIncrementRank, targetRank } = conditions
|
|
||||||
const {
|
|
||||||
shouldChangeParent,
|
|
||||||
originalRank,
|
|
||||||
targetParentId,
|
|
||||||
targetCategoryId,
|
|
||||||
shouldDeleteElement,
|
|
||||||
} = conditions
|
|
||||||
|
|
||||||
// The current sibling count will replace targetRank if
|
|
||||||
// targetRank is greater than the count of siblings.
|
|
||||||
const siblingCount = await manager.count(ProductCategory, {
|
|
||||||
parent_category_id: targetParentId || null,
|
|
||||||
id: { $ne: targetCategoryId },
|
|
||||||
})
|
|
||||||
|
|
||||||
// The category record that will be placed at the requested rank
|
|
||||||
// We've temporarily placed it at a temporary rank that is
|
|
||||||
// beyond a reasonable value (tempReorderRank)
|
|
||||||
const targetCategory = await manager.findOne(ProductCategory, {
|
|
||||||
id: targetCategoryId,
|
|
||||||
parent_category_id: targetParentId || null,
|
|
||||||
rank: tempReorderRank,
|
|
||||||
})
|
|
||||||
|
|
||||||
// If the targetRank is not present, or if targetRank is beyond the
|
|
||||||
// rank of the last category, we set the rank as the last rank
|
|
||||||
if (targetRank === undefined || targetRank > siblingCount) {
|
|
||||||
targetRank = siblingCount
|
|
||||||
}
|
|
||||||
|
|
||||||
let rankCondition
|
|
||||||
|
|
||||||
// If parent doesn't change, we only need to get the ranks
|
|
||||||
// in between the original rank and the target rank.
|
|
||||||
if (shouldChangeParent || shouldDeleteElement) {
|
|
||||||
rankCondition = { $gte: targetRank }
|
|
||||||
} else if (originalRank > targetRank) {
|
|
||||||
shouldIncrementRank = true
|
|
||||||
rankCondition = { $gte: targetRank, $lte: originalRank }
|
|
||||||
} else {
|
} else {
|
||||||
shouldIncrementRank = false
|
const siblings = await manager.find(
|
||||||
rankCondition = { $gte: originalRank, $lte: targetRank }
|
ProductCategory,
|
||||||
|
{
|
||||||
|
parent_category_id: originalSibling.parent_category_id,
|
||||||
|
rank: { $gte: updatedSibling.rank, $lt: originalSibling.rank },
|
||||||
|
},
|
||||||
|
{ orderBy: { rank: "ASC" } }
|
||||||
|
)
|
||||||
|
|
||||||
|
const updatedSiblings = siblings.map((sibling) => {
|
||||||
|
manager.assign(sibling, { rank: sibling.rank + 1 })
|
||||||
|
return sibling
|
||||||
|
})
|
||||||
|
|
||||||
|
manager.persist(updatedSiblings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scope out the list of siblings that we need to shift up or down
|
|
||||||
const siblingsToShift = await manager.find(
|
|
||||||
ProductCategory,
|
|
||||||
{
|
|
||||||
parent_category_id: targetParentId || null,
|
|
||||||
rank: rankCondition,
|
|
||||||
id: { $ne: targetCategoryId },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
orderBy: { rank: shouldIncrementRank ? "DESC" : "ASC" },
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Depending on the conditions, we get a subset of the siblings
|
|
||||||
// and independently shift them up or down a rank
|
|
||||||
for (let index = 0; index < siblingsToShift.length; index++) {
|
|
||||||
const sibling = siblingsToShift[index]
|
|
||||||
|
|
||||||
// Depending on the condition, we could also have the targetCategory
|
|
||||||
// in the siblings list, we skip shifting the target until all other siblings
|
|
||||||
// have been shifted.
|
|
||||||
if (sibling.id === targetCategoryId) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDefined(sibling.rank)) {
|
|
||||||
throw new Error("sibling rank is not defined")
|
|
||||||
}
|
|
||||||
|
|
||||||
const rank = shouldIncrementRank ? ++sibling.rank! : --sibling.rank!
|
|
||||||
|
|
||||||
manager.assign(sibling, { rank })
|
|
||||||
manager.persist(sibling)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The targetCategory will not be present in the query when we are shifting
|
|
||||||
// siblings of the old parent of the targetCategory.
|
|
||||||
if (!targetCategory) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Place the targetCategory in the requested rank
|
|
||||||
manager.assign(targetCategory, { rank: targetRank })
|
|
||||||
manager.persist(targetCategory)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,11 +10,11 @@ import {
|
|||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { ProductCategory } from "@models"
|
import { ProductCategory } from "@models"
|
||||||
import { ProductCategoryRepository } from "@repositories"
|
import { ProductCategoryRepository } from "@repositories"
|
||||||
|
import { UpdateCategoryInput } from "@types"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
productCategoryRepository: DAL.TreeRepositoryService
|
productCategoryRepository: DAL.TreeRepositoryService
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ProductCategoryService<
|
export default class ProductCategoryService<
|
||||||
TEntity extends ProductCategory = ProductCategory
|
TEntity extends ProductCategory = ProductCategory
|
||||||
> {
|
> {
|
||||||
@@ -24,6 +24,7 @@ export default class ProductCategoryService<
|
|||||||
this.productCategoryRepository_ = productCategoryRepository
|
this.productCategoryRepository_ = productCategoryRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add support for object filter
|
||||||
@InjectManager("productCategoryRepository_")
|
@InjectManager("productCategoryRepository_")
|
||||||
async retrieve(
|
async retrieve(
|
||||||
productCategoryId: string,
|
productCategoryId: string,
|
||||||
@@ -44,6 +45,8 @@ export default class ProductCategoryService<
|
|||||||
config
|
config
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Currently remoteQuery doesn't allow passing custom objects, so the `include*` are part of the filters
|
||||||
|
// Modify remoteQuery to allow passing custom objects
|
||||||
const transformOptions = {
|
const transformOptions = {
|
||||||
includeDescendantsTree: true,
|
includeDescendantsTree: true,
|
||||||
}
|
}
|
||||||
@@ -140,30 +143,47 @@ export default class ProductCategoryService<
|
|||||||
|
|
||||||
@InjectTransactionManager("productCategoryRepository_")
|
@InjectTransactionManager("productCategoryRepository_")
|
||||||
async create(
|
async create(
|
||||||
data: ProductTypes.CreateProductCategoryDTO,
|
data: ProductTypes.CreateProductCategoryDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<TEntity> {
|
): Promise<TEntity[]> {
|
||||||
return (await (
|
return (await (
|
||||||
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
||||||
).create(data, sharedContext)) as TEntity
|
).create(data, sharedContext)) as TEntity[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("productCategoryRepository_")
|
@InjectTransactionManager("productCategoryRepository_")
|
||||||
async update(
|
async update(
|
||||||
id: string,
|
data: UpdateCategoryInput[],
|
||||||
data: ProductTypes.UpdateProductCategoryDTO,
|
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<TEntity> {
|
): Promise<TEntity[]> {
|
||||||
return (await (
|
return (await (
|
||||||
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
||||||
).update(id, data, sharedContext)) as TEntity
|
).update(data, sharedContext)) as TEntity[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager("productCategoryRepository_")
|
@InjectTransactionManager("productCategoryRepository_")
|
||||||
async delete(
|
async delete(
|
||||||
id: string,
|
ids: string[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.productCategoryRepository_.delete(id, sharedContext)
|
await this.productCategoryRepository_.delete(ids, sharedContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
async softDelete(
|
||||||
|
ids: string[],
|
||||||
|
@MedusaContext() sharedContext?: Context
|
||||||
|
): Promise<Record<string, string[]> | void> {
|
||||||
|
return (await (
|
||||||
|
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
||||||
|
).softDelete(ids, sharedContext)) as any
|
||||||
|
}
|
||||||
|
|
||||||
|
async restore(
|
||||||
|
ids: string[],
|
||||||
|
@MedusaContext() sharedContext?: Context
|
||||||
|
): Promise<Record<string, string[]> | void> {
|
||||||
|
return (await (
|
||||||
|
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
||||||
|
).restore(ids, sharedContext)) as any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,7 @@ import {
|
|||||||
ProductCollectionEvents,
|
ProductCollectionEvents,
|
||||||
ProductEventData,
|
ProductEventData,
|
||||||
ProductEvents,
|
ProductEvents,
|
||||||
|
UpdateCategoryInput,
|
||||||
UpdateCollectionInput,
|
UpdateCollectionInput,
|
||||||
UpdateProductInput,
|
UpdateProductInput,
|
||||||
UpdateProductOptionInput,
|
UpdateProductOptionInput,
|
||||||
@@ -1114,67 +1115,166 @@ export default class ProductModuleService<
|
|||||||
return collections
|
return collections
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createCategories(
|
||||||
|
data: ProductTypes.CreateProductCategoryDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductTypes.ProductCategoryDTO[]>
|
||||||
|
createCategories(
|
||||||
|
data: ProductTypes.CreateProductCategoryDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductTypes.ProductCategoryDTO>
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
async createCategory(
|
@EmitEvents()
|
||||||
data: ProductTypes.CreateProductCategoryDTO,
|
async createCategories(
|
||||||
|
data:
|
||||||
|
| ProductTypes.CreateProductCategoryDTO[]
|
||||||
|
| ProductTypes.CreateProductCategoryDTO,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<ProductTypes.ProductCategoryDTO> {
|
): Promise<
|
||||||
const result = await this.createCategory_(data, sharedContext)
|
ProductTypes.ProductCategoryDTO[] | ProductTypes.ProductCategoryDTO
|
||||||
|
> {
|
||||||
|
const input = Array.isArray(data) ? data : [data]
|
||||||
|
|
||||||
return await this.baseRepository_.serialize(result)
|
const categories = await this.productCategoryService_.create(
|
||||||
}
|
input,
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
|
||||||
async createCategory_(
|
|
||||||
data: ProductTypes.CreateProductCategoryDTO,
|
|
||||||
@MedusaContext() sharedContext: Context = {}
|
|
||||||
): Promise<ProductCategory> {
|
|
||||||
const productCategory = await this.productCategoryService_.create(
|
|
||||||
data,
|
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
await this.eventBusModuleService_?.emit<ProductCategoryEventData>({
|
const createdCategories = await this.baseRepository_.serialize<
|
||||||
eventName: ProductCategoryEvents.CATEGORY_CREATED,
|
ProductTypes.ProductCategoryDTO[]
|
||||||
data: { id: productCategory.id },
|
>(categories)
|
||||||
|
|
||||||
|
eventBuilders.createdProductCategory({
|
||||||
|
data: createdCategories,
|
||||||
|
sharedContext,
|
||||||
})
|
})
|
||||||
|
|
||||||
return productCategory
|
return Array.isArray(data) ? createdCategories : createdCategories[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async upsertCategories(
|
||||||
|
data: ProductTypes.UpsertProductCategoryDTO[],
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductTypes.ProductCategoryDTO[]>
|
||||||
|
async upsertCategories(
|
||||||
|
data: ProductTypes.UpsertProductCategoryDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductTypes.ProductCategoryDTO>
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
@InjectTransactionManager("baseRepository_")
|
||||||
async updateCategory(
|
@EmitEvents()
|
||||||
categoryId: string,
|
async upsertCategories(
|
||||||
|
data:
|
||||||
|
| ProductTypes.UpsertProductCategoryDTO[]
|
||||||
|
| ProductTypes.UpsertProductCategoryDTO,
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<
|
||||||
|
ProductTypes.ProductCategoryDTO[] | ProductTypes.ProductCategoryDTO
|
||||||
|
> {
|
||||||
|
const input = Array.isArray(data) ? data : [data]
|
||||||
|
const forUpdate = input.filter(
|
||||||
|
(category): category is UpdateCategoryInput => !!category.id
|
||||||
|
)
|
||||||
|
const forCreate = input.filter(
|
||||||
|
(category): category is ProductTypes.CreateProductCategoryDTO =>
|
||||||
|
!category.id
|
||||||
|
)
|
||||||
|
|
||||||
|
let created: ProductCategory[] = []
|
||||||
|
let updated: ProductCategory[] = []
|
||||||
|
|
||||||
|
if (forCreate.length) {
|
||||||
|
created = await this.productCategoryService_.create(
|
||||||
|
forCreate,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (forUpdate.length) {
|
||||||
|
updated = await this.productCategoryService_.update(
|
||||||
|
forUpdate,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdCategories = await this.baseRepository_.serialize<
|
||||||
|
ProductTypes.ProductCategoryDTO[]
|
||||||
|
>(created)
|
||||||
|
const updatedCategories = await this.baseRepository_.serialize<
|
||||||
|
ProductTypes.ProductCategoryDTO[]
|
||||||
|
>(updated)
|
||||||
|
|
||||||
|
eventBuilders.createdProductCategory({
|
||||||
|
data: createdCategories,
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
|
||||||
|
eventBuilders.updatedProductCategory({
|
||||||
|
data: updatedCategories,
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = [...createdCategories, ...updatedCategories]
|
||||||
|
return Array.isArray(data) ? result : result[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCategories(
|
||||||
|
id: string,
|
||||||
|
data: ProductTypes.UpdateProductCategoryDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductTypes.ProductCategoryDTO>
|
||||||
|
updateCategories(
|
||||||
|
selector: ProductTypes.FilterableProductTypeProps,
|
||||||
|
data: ProductTypes.UpdateProductCategoryDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ProductTypes.ProductCategoryDTO[]>
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
@EmitEvents()
|
||||||
|
async updateCategories(
|
||||||
|
idOrSelector: string | ProductTypes.FilterableProductTypeProps,
|
||||||
data: ProductTypes.UpdateProductCategoryDTO,
|
data: ProductTypes.UpdateProductCategoryDTO,
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<ProductTypes.ProductCategoryDTO> {
|
): Promise<
|
||||||
const productCategory = await this.productCategoryService_.update(
|
ProductTypes.ProductCategoryDTO[] | ProductTypes.ProductCategoryDTO
|
||||||
categoryId,
|
> {
|
||||||
data,
|
let normalizedInput: UpdateCategoryInput[] = []
|
||||||
|
if (isString(idOrSelector)) {
|
||||||
|
// Check if the type exists in the first place
|
||||||
|
await this.productCategoryService_.retrieve(
|
||||||
|
idOrSelector,
|
||||||
|
{},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
normalizedInput = [{ id: idOrSelector, ...data }]
|
||||||
|
} else {
|
||||||
|
const categories = await this.productCategoryService_.list(
|
||||||
|
idOrSelector,
|
||||||
|
{},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
normalizedInput = categories.map((type) => ({
|
||||||
|
id: type.id,
|
||||||
|
...data,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const categories = await this.productCategoryService_.update(
|
||||||
|
normalizedInput,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
await this.eventBusModuleService_?.emit<ProductCategoryEventData>({
|
const updatedCategories = await this.baseRepository_.serialize<
|
||||||
eventName: ProductCategoryEvents.CATEGORY_UPDATED,
|
ProductTypes.ProductCategoryDTO[]
|
||||||
data: { id: productCategory.id },
|
>(categories)
|
||||||
|
|
||||||
|
eventBuilders.updatedProductCategory({
|
||||||
|
data: updatedCategories,
|
||||||
|
sharedContext,
|
||||||
})
|
})
|
||||||
|
|
||||||
return await this.baseRepository_.serialize(productCategory, {
|
return isString(idOrSelector) ? updatedCategories[0] : updatedCategories
|
||||||
populate: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@InjectTransactionManager("baseRepository_")
|
|
||||||
async deleteCategory(
|
|
||||||
categoryId: string,
|
|
||||||
@MedusaContext() sharedContext: Context = {}
|
|
||||||
): Promise<void> {
|
|
||||||
await this.productCategoryService_.delete(categoryId, sharedContext)
|
|
||||||
|
|
||||||
await this.eventBusModuleService_?.emit<ProductCategoryEventData>({
|
|
||||||
eventName: ProductCategoryEvents.CATEGORY_DELETED,
|
|
||||||
data: { id: categoryId },
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create(
|
create(
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ export type UpdateTypeInput = ProductTypes.UpdateProductTypeDTO & {
|
|||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UpdateCategoryInput = ProductTypes.UpdateProductCategoryDTO & {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
export type UpdateTagInput = ProductTypes.UpdateProductTagDTO & {
|
export type UpdateTagInput = ProductTypes.UpdateProductTagDTO & {
|
||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,4 +99,22 @@ export const eventBuilders = {
|
|||||||
object: "product_tag",
|
object: "product_tag",
|
||||||
eventsEnum: ProductEvents,
|
eventsEnum: ProductEvents,
|
||||||
}),
|
}),
|
||||||
|
createdProductCategory: eventBuilderFactory({
|
||||||
|
source: Modules.PRODUCT,
|
||||||
|
action: CommonEvents.CREATED,
|
||||||
|
object: "product_category",
|
||||||
|
eventsEnum: ProductEvents,
|
||||||
|
}),
|
||||||
|
updatedProductCategory: eventBuilderFactory({
|
||||||
|
source: Modules.PRODUCT,
|
||||||
|
action: CommonEvents.UPDATED,
|
||||||
|
object: "product_category",
|
||||||
|
eventsEnum: ProductEvents,
|
||||||
|
}),
|
||||||
|
deletedProductCategory: eventBuilderFactory({
|
||||||
|
source: Modules.PRODUCT,
|
||||||
|
action: CommonEvents.DELETED,
|
||||||
|
object: "product_category",
|
||||||
|
eventsEnum: ProductEvents,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user