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:
@@ -10,11 +10,11 @@ import {
|
||||
} from "@medusajs/utils"
|
||||
import { ProductCategory } from "@models"
|
||||
import { ProductCategoryRepository } from "@repositories"
|
||||
import { UpdateCategoryInput } from "@types"
|
||||
|
||||
type InjectedDependencies = {
|
||||
productCategoryRepository: DAL.TreeRepositoryService
|
||||
}
|
||||
|
||||
export default class ProductCategoryService<
|
||||
TEntity extends ProductCategory = ProductCategory
|
||||
> {
|
||||
@@ -24,6 +24,7 @@ export default class ProductCategoryService<
|
||||
this.productCategoryRepository_ = productCategoryRepository
|
||||
}
|
||||
|
||||
// TODO: Add support for object filter
|
||||
@InjectManager("productCategoryRepository_")
|
||||
async retrieve(
|
||||
productCategoryId: string,
|
||||
@@ -44,6 +45,8 @@ export default class ProductCategoryService<
|
||||
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 = {
|
||||
includeDescendantsTree: true,
|
||||
}
|
||||
@@ -140,30 +143,47 @@ export default class ProductCategoryService<
|
||||
|
||||
@InjectTransactionManager("productCategoryRepository_")
|
||||
async create(
|
||||
data: ProductTypes.CreateProductCategoryDTO,
|
||||
data: ProductTypes.CreateProductCategoryDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity> {
|
||||
): Promise<TEntity[]> {
|
||||
return (await (
|
||||
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
||||
).create(data, sharedContext)) as TEntity
|
||||
).create(data, sharedContext)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("productCategoryRepository_")
|
||||
async update(
|
||||
id: string,
|
||||
data: ProductTypes.UpdateProductCategoryDTO,
|
||||
data: UpdateCategoryInput[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity> {
|
||||
): Promise<TEntity[]> {
|
||||
return (await (
|
||||
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
||||
).update(id, data, sharedContext)) as TEntity
|
||||
).update(data, sharedContext)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("productCategoryRepository_")
|
||||
async delete(
|
||||
id: string,
|
||||
ids: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): 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,
|
||||
ProductEventData,
|
||||
ProductEvents,
|
||||
UpdateCategoryInput,
|
||||
UpdateCollectionInput,
|
||||
UpdateProductInput,
|
||||
UpdateProductOptionInput,
|
||||
@@ -1114,67 +1115,166 @@ export default class ProductModuleService<
|
||||
return collections
|
||||
}
|
||||
|
||||
createCategories(
|
||||
data: ProductTypes.CreateProductCategoryDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<ProductTypes.ProductCategoryDTO[]>
|
||||
createCategories(
|
||||
data: ProductTypes.CreateProductCategoryDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<ProductTypes.ProductCategoryDTO>
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async createCategory(
|
||||
data: ProductTypes.CreateProductCategoryDTO,
|
||||
@EmitEvents()
|
||||
async createCategories(
|
||||
data:
|
||||
| ProductTypes.CreateProductCategoryDTO[]
|
||||
| ProductTypes.CreateProductCategoryDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<ProductTypes.ProductCategoryDTO> {
|
||||
const result = await this.createCategory_(data, sharedContext)
|
||||
): Promise<
|
||||
ProductTypes.ProductCategoryDTO[] | ProductTypes.ProductCategoryDTO
|
||||
> {
|
||||
const input = Array.isArray(data) ? data : [data]
|
||||
|
||||
return await this.baseRepository_.serialize(result)
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async createCategory_(
|
||||
data: ProductTypes.CreateProductCategoryDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<ProductCategory> {
|
||||
const productCategory = await this.productCategoryService_.create(
|
||||
data,
|
||||
const categories = await this.productCategoryService_.create(
|
||||
input,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
await this.eventBusModuleService_?.emit<ProductCategoryEventData>({
|
||||
eventName: ProductCategoryEvents.CATEGORY_CREATED,
|
||||
data: { id: productCategory.id },
|
||||
const createdCategories = await this.baseRepository_.serialize<
|
||||
ProductTypes.ProductCategoryDTO[]
|
||||
>(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_")
|
||||
async updateCategory(
|
||||
categoryId: string,
|
||||
@EmitEvents()
|
||||
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,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<ProductTypes.ProductCategoryDTO> {
|
||||
const productCategory = await this.productCategoryService_.update(
|
||||
categoryId,
|
||||
data,
|
||||
): Promise<
|
||||
ProductTypes.ProductCategoryDTO[] | ProductTypes.ProductCategoryDTO
|
||||
> {
|
||||
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
|
||||
)
|
||||
|
||||
await this.eventBusModuleService_?.emit<ProductCategoryEventData>({
|
||||
eventName: ProductCategoryEvents.CATEGORY_UPDATED,
|
||||
data: { id: productCategory.id },
|
||||
const updatedCategories = await this.baseRepository_.serialize<
|
||||
ProductTypes.ProductCategoryDTO[]
|
||||
>(categories)
|
||||
|
||||
eventBuilders.updatedProductCategory({
|
||||
data: updatedCategories,
|
||||
sharedContext,
|
||||
})
|
||||
|
||||
return await this.baseRepository_.serialize(productCategory, {
|
||||
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 },
|
||||
})
|
||||
return isString(idOrSelector) ? updatedCategories[0] : updatedCategories
|
||||
}
|
||||
|
||||
create(
|
||||
|
||||
Reference in New Issue
Block a user