fix(medusa): fixes bug for mpath incorrectly updated for nested categories (#3311)

* chore: fix issue with mpath being incorrectly set

* chore: address review changes
This commit is contained in:
Riqwan Thamir authored and GitHub committed 2023-02-22 15:43:58 +01:00
1 parent e8e7d7bb53
commit 68496ffe60
4 files changed
+152 -17

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---
fix(medusa): fixes bug for mpath incorrectly updated for nested categories
@@ -243,6 +243,17 @@ describe("/admin/product-categories", () => {
describe("POST /admin/product-categories", () => { describe("POST /admin/product-categories", () => {
beforeEach(async () => { beforeEach(async () => {
await adminSeeder(dbConnection) await adminSeeder(dbConnection)
productCategoryParent = await simpleProductCategoryFactory(dbConnection, {
name: "category parent",
handle: "category-parent",
})
productCategory = await simpleProductCategoryFactory(dbConnection, {
name: "category",
handle: "category",
parent_category: productCategoryParent,
})
}) })
afterEach(async () => { afterEach(async () => {
@@ -267,11 +278,6 @@ describe("/admin/product-categories", () => {
}) })
it("successfully creates a product category", async () => { it("successfully creates a product category", async () => {
productCategoryParent = await simpleProductCategoryFactory(dbConnection, {
name: "category parent",
handle: "category-parent",
})
const api = useApi() const api = useApi()
const response = await api.post( const response = await api.post(
@@ -280,7 +286,7 @@ describe("/admin/product-categories", () => {
name: "test", name: "test",
handle: "test", handle: "test",
is_internal: true, is_internal: true,
parent_category_id: productCategoryParent.id, parent_category_id: productCategory.id,
}, },
adminHeaders adminHeaders
) )
@@ -296,13 +302,49 @@ describe("/admin/product-categories", () => {
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
parent_category: expect.objectContaining({ parent_category: expect.objectContaining({
id: productCategoryParent.id id: productCategory.id
}), }),
category_children: [] category_children: []
}), }),
}) })
) )
}) })
it("root parent returns children correctly on creating new category", async () => {
const api = useApi()
const response = await api.post(
`/admin/product-categories`,
{
name: "last descendant",
parent_category_id: productCategory.id,
},
adminHeaders
)
const lastDescendant = response.data.product_category
const parentResponse = await api.get(
`/admin/product-categories/${productCategoryParent.id}`,
adminHeaders
)
expect(parentResponse.data.product_category).toEqual(
expect.objectContaining({
id: productCategoryParent.id,
category_children: [
expect.objectContaining({
id: productCategory.id,
category_children: [
expect.objectContaining({
id: lastDescendant.id,
category_children: []
})
]
})
]
})
)
})
}) })
describe("DELETE /admin/product-categories/:id", () => { describe("DELETE /admin/product-categories/:id", () => {
@@ -381,14 +423,27 @@ describe("/admin/product-categories", () => {
beforeEach(async () => { beforeEach(async () => {
await adminSeeder(dbConnection) await adminSeeder(dbConnection)
productCategory = await simpleProductCategoryFactory(dbConnection, { productCategoryParent = await simpleProductCategoryFactory(dbConnection, {
name: "skinny jeans", name: "category parent",
handle: "skinny-jeans", handle: "category-parent",
}) })
productCategory2 = await simpleProductCategoryFactory(dbConnection, { productCategory = await simpleProductCategoryFactory(dbConnection, {
name: "sweater", name: "category",
handle: "sweater", handle: "category",
parent_category: productCategoryParent,
})
productCategoryChild = await simpleProductCategoryFactory(dbConnection, {
name: "category child",
handle: "category-child",
parent_category: productCategory,
})
productCategoryChild2 = await simpleProductCategoryFactory(dbConnection, {
name: "category child 2",
handle: "category-child-2",
parent_category: productCategoryChild,
}) })
}) })
@@ -437,13 +492,13 @@ describe("/admin/product-categories", () => {
const api = useApi() const api = useApi()
const response = await api.post( const response = await api.post(
`/admin/product-categories/${productCategory.id}`, `/admin/product-categories/${productCategoryChild2.id}`,
{ {
name: "test", name: "test",
handle: "test", handle: "test",
is_internal: true, is_internal: true,
is_active: true, is_active: true,
parent_category_id: productCategory2.id, parent_category_id: productCategory.id,
}, },
adminHeaders adminHeaders
) )
@@ -459,13 +514,52 @@ describe("/admin/product-categories", () => {
created_at: expect.any(String), created_at: expect.any(String),
updated_at: expect.any(String), updated_at: expect.any(String),
parent_category: expect.objectContaining({ parent_category: expect.objectContaining({
id: productCategory2.id, id: productCategory.id,
}), }),
category_children: [] category_children: []
}), }),
}) })
) )
}) })
it("root parent returns children correctly on updating new category", async () => {
const api = useApi()
const response = await api.post(
`/admin/product-categories/${productCategoryChild2.id}`,
{
parent_category_id: productCategory.id,
},
adminHeaders
)
const lastDescendant = response.data.product_category
const parentResponse = await api.get(
`/admin/product-categories/${productCategoryParent.id}`,
adminHeaders
)
expect(parentResponse.data.product_category).toEqual(
expect.objectContaining({
id: productCategoryParent.id,
category_children: [
expect.objectContaining({
id: productCategory.id,
category_children: [
expect.objectContaining({
id: productCategoryChild.id,
category_children: []
}),
expect.objectContaining({
id: productCategoryChild2.id,
category_children: []
})
]
})
]
})
)
})
}) })
describe("POST /admin/product-categories/:id/products/batch", () => { describe("POST /admin/product-categories/:id/products/batch", () => {
@@ -121,7 +121,7 @@ class ProductCategoryService extends TransactionBaseService {
/** /**
* Creates a product category * Creates a product category
* @param productCategory - params used to create * @param productCategoryInput - parameters to create a product category
* @return created product category * @return created product category
*/ */
async create( async create(
@@ -129,6 +129,9 @@ class ProductCategoryService extends TransactionBaseService {
): Promise<ProductCategory> { ): Promise<ProductCategory> {
return await this.atomicPhase_(async (manager) => { return await this.atomicPhase_(async (manager) => {
const pcRepo = manager.withRepository(this.productCategoryRepo_) const pcRepo = manager.withRepository(this.productCategoryRepo_)
await this.transformParentIdToEntity(productCategoryInput)
let productCategory = pcRepo.create(productCategoryInput) let productCategory = pcRepo.create(productCategoryInput)
productCategory = await pcRepo.save(productCategory) productCategory = await pcRepo.save(productCategory)
@@ -157,6 +160,8 @@ class ProductCategoryService extends TransactionBaseService {
this.productCategoryRepo_ this.productCategoryRepo_
) )
await this.transformParentIdToEntity(productCategoryInput)
let productCategory = await this.retrieve(productCategoryId) let productCategory = await this.retrieve(productCategoryId)
for (const key in productCategoryInput) { for (const key in productCategoryInput) {
@@ -253,6 +258,34 @@ class ProductCategoryService extends TransactionBaseService {
) )
}) })
} }
/**
* Accepts an input object and transforms product_category_id
* into product_category entity.
* @param productCategoryInput - params used to create/update
* @return transformed productCategoryInput
*/
protected async transformParentIdToEntity(
productCategoryInput:
| CreateProductCategoryInput
| UpdateProductCategoryInput
): Promise<CreateProductCategoryInput | UpdateProductCategoryInput> {
// Typeorm only updates mpath when the category entity of the parent
// is passed into create/save. For this reason, everytime we create a
// category, we must fetch the entity and push to create
const parentCategoryId = productCategoryInput.parent_category_id
if (!parentCategoryId) {
return productCategoryInput
}
const parentCategory = await this.retrieve(parentCategoryId)
productCategoryInput.parent_category = parentCategory
delete productCategoryInput.parent_category_id
return productCategoryInput
}
} }
export default ProductCategoryService export default ProductCategoryService
@@ -1,5 +1,6 @@
import { Transform } from "class-transformer" import { Transform } from "class-transformer"
import { IsNotEmpty, IsOptional, IsString, IsBoolean } from "class-validator" import { IsNotEmpty, IsOptional, IsString, IsBoolean } from "class-validator"
import { ProductCategory } from "../models"
export type CreateProductCategoryInput = { export type CreateProductCategoryInput = {
name: string name: string
@@ -7,6 +8,7 @@ export type CreateProductCategoryInput = {
is_internal?: boolean is_internal?: boolean
is_active?: boolean is_active?: boolean
parent_category_id?: string | null parent_category_id?: string | null
parent_category?: ProductCategory | null
} }
export type UpdateProductCategoryInput = { export type UpdateProductCategoryInput = {
@@ -15,6 +17,7 @@ export type UpdateProductCategoryInput = {
is_internal?: boolean is_internal?: boolean
is_active?: boolean is_active?: boolean
parent_category_id?: string | null parent_category_id?: string | null
parent_category?: ProductCategory | null
} }
export class AdminProductCategoriesReqBase { export class AdminProductCategoriesReqBase {