Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/warm-cobras-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/medusa": patch
---

fix(medusa): fixes bug for mpath incorrectly updated for nested categories
124 changes: 109 additions & 15 deletions integration-tests/api/__tests__/admin/product-category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,17 @@ describe("/admin/product-categories", () => {
describe("POST /admin/product-categories", () => {
beforeEach(async () => {
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 () => {
Expand All @@ -267,11 +278,6 @@ describe("/admin/product-categories", () => {
})

it("successfully creates a product category", async () => {
productCategoryParent = await simpleProductCategoryFactory(dbConnection, {
name: "category parent",
handle: "category-parent",
})

const api = useApi()

const response = await api.post(
Expand All @@ -280,7 +286,7 @@ describe("/admin/product-categories", () => {
name: "test",
handle: "test",
is_internal: true,
parent_category_id: productCategoryParent.id,
parent_category_id: productCategory.id,
},
adminHeaders
)
Expand All @@ -296,13 +302,49 @@ describe("/admin/product-categories", () => {
created_at: expect.any(String),
updated_at: expect.any(String),
parent_category: expect.objectContaining({
id: productCategoryParent.id
id: productCategory.id
}),
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", () => {
Expand Down Expand Up @@ -381,14 +423,27 @@ describe("/admin/product-categories", () => {
beforeEach(async () => {
await adminSeeder(dbConnection)

productCategoryParent = await simpleProductCategoryFactory(dbConnection, {
name: "category parent",
handle: "category-parent",
})

productCategory = await simpleProductCategoryFactory(dbConnection, {
name: "skinny jeans",
handle: "skinny-jeans",
name: "category",
handle: "category",
parent_category: productCategoryParent,
})

productCategory2 = await simpleProductCategoryFactory(dbConnection, {
name: "sweater",
handle: "sweater",
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,
})
})

Expand Down Expand Up @@ -437,13 +492,13 @@ describe("/admin/product-categories", () => {
const api = useApi()

const response = await api.post(
`/admin/product-categories/${productCategory.id}`,
`/admin/product-categories/${productCategoryChild2.id}`,
{
name: "test",
handle: "test",
is_internal: true,
is_active: true,
parent_category_id: productCategory2.id,
parent_category_id: productCategory.id,
},
adminHeaders
)
Expand All @@ -459,13 +514,52 @@ describe("/admin/product-categories", () => {
created_at: expect.any(String),
updated_at: expect.any(String),
parent_category: expect.objectContaining({
id: productCategory2.id,
id: productCategory.id,
}),
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", () => {
Expand Down
35 changes: 34 additions & 1 deletion packages/medusa/src/services/product-category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,17 @@ class ProductCategoryService extends TransactionBaseService {

/**
* Creates a product category
* @param productCategory - params used to create
* @param productCategoryInput - parameters to create a product category
* @return created product category
*/
async create(
productCategoryInput: CreateProductCategoryInput
): Promise<ProductCategory> {
return await this.atomicPhase_(async (manager) => {
const pcRepo = manager.withRepository(this.productCategoryRepo_)

await this.transformParentIdToEntity(productCategoryInput)

let productCategory = pcRepo.create(productCategoryInput)
productCategory = await pcRepo.save(productCategory)

Expand Down Expand Up @@ -157,6 +160,8 @@ class ProductCategoryService extends TransactionBaseService {
this.productCategoryRepo_
)

await this.transformParentIdToEntity(productCategoryInput)

let productCategory = await this.retrieve(productCategoryId)

for (const key in productCategoryInput) {
Expand Down Expand Up @@ -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
3 changes: 3 additions & 0 deletions packages/medusa/src/types/product-category.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Transform } from "class-transformer"
import { IsNotEmpty, IsOptional, IsString, IsBoolean } from "class-validator"
import { ProductCategory } from "../models"

export type CreateProductCategoryInput = {
name: string
handle?: string
is_internal?: boolean
is_active?: boolean
parent_category_id?: string | null
parent_category?: ProductCategory | null
}

export type UpdateProductCategoryInput = {
Expand All @@ -15,6 +17,7 @@ export type UpdateProductCategoryInput = {
is_internal?: boolean
is_active?: boolean
parent_category_id?: string | null
fPolic marked this conversation as resolved.
Show resolved Hide resolved
parent_category?: ProductCategory | null
}

export class AdminProductCategoriesReqBase {
Expand Down