feat(product): return parents tree (#6944)
*What* include option `include_ancestors_tree` to list products including the parent tree.
This commit is contained in:
1 parent
8ecfa4b6f5
commit
09a2220569
7 files changed
+618
-67
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/product": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add parents to product categories
|
||||||
@@ -65,3 +65,127 @@ export const productCategoriesRankData = [
|
|||||||
rank: 2,
|
rank: 2,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const eletronicsCategoriesData = eval(`[
|
||||||
|
{
|
||||||
|
id: "electronics",
|
||||||
|
name: "Electronics",
|
||||||
|
parent_category_id: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "computers",
|
||||||
|
name: "Computers & Accessories",
|
||||||
|
parent_category_id: "electronics",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "desktops",
|
||||||
|
name: "Desktops",
|
||||||
|
parent_category_id: "computers",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "gaming-desktops",
|
||||||
|
name: "Gaming Desktops",
|
||||||
|
parent_category_id: "desktops",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "office-desktops",
|
||||||
|
name: "Office Desktops",
|
||||||
|
parent_category_id: "desktops",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "laptops",
|
||||||
|
name: "Laptops",
|
||||||
|
parent_category_id: "computers",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "gaming-laptops",
|
||||||
|
name: "Gaming Laptops",
|
||||||
|
parent_category_id: "laptops",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "budget-gaming",
|
||||||
|
name: "Budget Gaming Laptops",
|
||||||
|
parent_category_id: "gaming-laptops",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "high-performance",
|
||||||
|
name: "High Performance Gaming Laptops",
|
||||||
|
parent_category_id: "gaming-laptops",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "vr-ready",
|
||||||
|
name: "VR-Ready High Performance Gaming Laptops",
|
||||||
|
parent_category_id: "high-performance",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "4k-gaming",
|
||||||
|
name: "4K Gaming Laptops",
|
||||||
|
parent_category_id: "high-performance",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "ultrabooks",
|
||||||
|
name: "Ultrabooks",
|
||||||
|
parent_category_id: "laptops",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "thin-light",
|
||||||
|
name: "Thin & Light Ultrabooks",
|
||||||
|
parent_category_id: "ultrabooks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "convertible-ultrabooks",
|
||||||
|
name: "Convertible Ultrabooks",
|
||||||
|
parent_category_id: "ultrabooks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "touchscreen-ultrabooks",
|
||||||
|
name: "Touchscreen Ultrabooks",
|
||||||
|
parent_category_id: "convertible-ultrabooks",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "detachable-ultrabooks",
|
||||||
|
name: "Detachable Ultrabooks",
|
||||||
|
parent_category_id: "convertible-ultrabooks",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: "mobile",
|
||||||
|
name: "Mobile Phones & Accessories",
|
||||||
|
parent_category_id: "electronics",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "smartphones",
|
||||||
|
name: "Smartphones",
|
||||||
|
parent_category_id: "mobile",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "android-phones",
|
||||||
|
name: "Android Phones",
|
||||||
|
parent_category_id: "smartphones",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "flagship-phones",
|
||||||
|
name: "Flagship Smartphones",
|
||||||
|
parent_category_id: "android-phones",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "budget-phones",
|
||||||
|
name: "Budget Smartphones",
|
||||||
|
parent_category_id: "android-phones",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "iphones",
|
||||||
|
name: "iPhones",
|
||||||
|
parent_category_id: "smartphones",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "pro-phones",
|
||||||
|
name: "Pro Models",
|
||||||
|
parent_category_id: "iphones",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "mini-phones",
|
||||||
|
name: "Mini Models",
|
||||||
|
parent_category_id: "iphones",
|
||||||
|
},
|
||||||
|
]`)
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import { ProductCategoryService } from "@services"
|
import { ProductCategoryService } from "@services"
|
||||||
|
|
||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { IProductModuleService } from "@medusajs/types"
|
||||||
|
import { SuiteOptions, moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||||
import { createProductCategories } from "../../../__fixtures__/product-category"
|
import { createProductCategories } from "../../../__fixtures__/product-category"
|
||||||
import {
|
import {
|
||||||
|
eletronicsCategoriesData,
|
||||||
productCategoriesData,
|
productCategoriesData,
|
||||||
productCategoriesRankData,
|
productCategoriesRankData,
|
||||||
} from "../../../__fixtures__/product-category/data"
|
} from "../../../__fixtures__/product-category/data"
|
||||||
import { Modules } from "@medusajs/modules-sdk"
|
|
||||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
|
||||||
import { IProductModuleService } from "@medusajs/types"
|
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
@@ -138,14 +139,12 @@ moduleIntegrationTestRunner({
|
|||||||
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: "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",
|
||||||
parent_category: "category-1",
|
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -153,7 +152,6 @@ moduleIntegrationTestRunner({
|
|||||||
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: "category-1",
|
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-b-1",
|
id: "category-1-b-1",
|
||||||
@@ -161,7 +159,6 @@ moduleIntegrationTestRunner({
|
|||||||
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",
|
||||||
parent_category: "category-1-b",
|
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@@ -173,6 +170,365 @@ moduleIntegrationTestRunner({
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("includes the entire list of descendants when include_descendants_tree is true for multiple results", async () => {
|
||||||
|
const productCategoryResults = await service.list(
|
||||||
|
{
|
||||||
|
parent_category_id: "category-1",
|
||||||
|
include_descendants_tree: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["id", "handle"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const serializedObject = JSON.parse(
|
||||||
|
JSON.stringify(productCategoryResults)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedObject).toEqual([
|
||||||
|
{
|
||||||
|
id: "category-1-a",
|
||||||
|
handle: "category-1-a",
|
||||||
|
mpath: "category-0.category-1.category-1-a.",
|
||||||
|
parent_category_id: "category-1",
|
||||||
|
category_children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "category-1-b",
|
||||||
|
handle: "category-1-b",
|
||||||
|
mpath: "category-0.category-1.category-1-b.",
|
||||||
|
parent_category_id: "category-1",
|
||||||
|
category_children: [
|
||||||
|
{
|
||||||
|
id: "category-1-b-1",
|
||||||
|
handle: "category-1-b-1",
|
||||||
|
mpath: "category-0.category-1.category-1-b.category-1-b-1.",
|
||||||
|
parent_category_id: "category-1-b",
|
||||||
|
category_children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("includes the entire list of parents when include_ancestors_tree is true", async () => {
|
||||||
|
await createProductCategories(
|
||||||
|
MikroOrmWrapper.forkManager(),
|
||||||
|
eletronicsCategoriesData
|
||||||
|
)
|
||||||
|
|
||||||
|
const productCategoryResults = await service.list(
|
||||||
|
{
|
||||||
|
id: "4k-gaming",
|
||||||
|
include_ancestors_tree: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["id", "handle"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const serializedObject = JSON.parse(
|
||||||
|
JSON.stringify(productCategoryResults)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedObject).toEqual([
|
||||||
|
{
|
||||||
|
id: "4k-gaming",
|
||||||
|
handle: "4k-gaming-laptops",
|
||||||
|
mpath:
|
||||||
|
"electronics.computers.laptops.gaming-laptops.high-performance.4k-gaming.",
|
||||||
|
parent_category_id: "high-performance",
|
||||||
|
parent_category: {
|
||||||
|
id: "high-performance",
|
||||||
|
parent_category_id: "gaming-laptops",
|
||||||
|
handle: "high-performance-gaming-laptops",
|
||||||
|
mpath:
|
||||||
|
"electronics.computers.laptops.gaming-laptops.high-performance.",
|
||||||
|
parent_category: {
|
||||||
|
id: "gaming-laptops",
|
||||||
|
handle: "gaming-laptops",
|
||||||
|
mpath: "electronics.computers.laptops.gaming-laptops.",
|
||||||
|
parent_category_id: "laptops",
|
||||||
|
parent_category: {
|
||||||
|
id: "laptops",
|
||||||
|
parent_category_id: "computers",
|
||||||
|
handle: "laptops",
|
||||||
|
mpath: "electronics.computers.laptops.",
|
||||||
|
parent_category: {
|
||||||
|
id: "computers",
|
||||||
|
handle: "computers-&-accessories",
|
||||||
|
mpath: "electronics.computers.",
|
||||||
|
parent_category_id: "electronics",
|
||||||
|
parent_category: {
|
||||||
|
id: "electronics",
|
||||||
|
parent_category_id: null,
|
||||||
|
handle: "electronics",
|
||||||
|
mpath: "electronics.",
|
||||||
|
parent_category: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("includes the entire list of descendants when include_descendants_tree is true", async () => {
|
||||||
|
await createProductCategories(
|
||||||
|
MikroOrmWrapper.forkManager(),
|
||||||
|
eletronicsCategoriesData
|
||||||
|
)
|
||||||
|
|
||||||
|
const productCategoryResults = await service.list(
|
||||||
|
{
|
||||||
|
id: "gaming-laptops",
|
||||||
|
include_descendants_tree: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["id", "handle"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const serializedObject = JSON.parse(
|
||||||
|
JSON.stringify(productCategoryResults)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedObject).toEqual([
|
||||||
|
{
|
||||||
|
id: "gaming-laptops",
|
||||||
|
handle: "gaming-laptops",
|
||||||
|
mpath: "electronics.computers.laptops.gaming-laptops.",
|
||||||
|
parent_category_id: "laptops",
|
||||||
|
category_children: [
|
||||||
|
{
|
||||||
|
id: "budget-gaming",
|
||||||
|
handle: "budget-gaming-laptops",
|
||||||
|
mpath:
|
||||||
|
"electronics.computers.laptops.gaming-laptops.budget-gaming.",
|
||||||
|
parent_category_id: "gaming-laptops",
|
||||||
|
category_children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "high-performance",
|
||||||
|
handle: "high-performance-gaming-laptops",
|
||||||
|
mpath:
|
||||||
|
"electronics.computers.laptops.gaming-laptops.high-performance.",
|
||||||
|
parent_category_id: "gaming-laptops",
|
||||||
|
category_children: [
|
||||||
|
{
|
||||||
|
id: "4k-gaming",
|
||||||
|
handle: "4k-gaming-laptops",
|
||||||
|
mpath:
|
||||||
|
"electronics.computers.laptops.gaming-laptops.high-performance.4k-gaming.",
|
||||||
|
parent_category_id: "high-performance",
|
||||||
|
category_children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "vr-ready",
|
||||||
|
handle: "vr-ready-high-performance-gaming-laptops",
|
||||||
|
mpath:
|
||||||
|
"electronics.computers.laptops.gaming-laptops.high-performance.vr-ready.",
|
||||||
|
parent_category_id: "high-performance",
|
||||||
|
category_children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("includes the entire list of descendants an parents when include_descendants_tree and include_ancestors_tree are true", async () => {
|
||||||
|
await createProductCategories(
|
||||||
|
MikroOrmWrapper.forkManager(),
|
||||||
|
eletronicsCategoriesData
|
||||||
|
)
|
||||||
|
|
||||||
|
const productCategoryResults = await service.list(
|
||||||
|
{
|
||||||
|
id: "gaming-laptops",
|
||||||
|
include_descendants_tree: true,
|
||||||
|
include_ancestors_tree: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["id", "handle"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const serializedObject = JSON.parse(
|
||||||
|
JSON.stringify(productCategoryResults)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedObject).toEqual([
|
||||||
|
{
|
||||||
|
id: "gaming-laptops",
|
||||||
|
handle: "gaming-laptops",
|
||||||
|
mpath: "electronics.computers.laptops.gaming-laptops.",
|
||||||
|
parent_category_id: "laptops",
|
||||||
|
parent_category: {
|
||||||
|
id: "laptops",
|
||||||
|
handle: "laptops",
|
||||||
|
mpath: "electronics.computers.laptops.",
|
||||||
|
parent_category_id: "computers",
|
||||||
|
parent_category: {
|
||||||
|
id: "computers",
|
||||||
|
handle: "computers-&-accessories",
|
||||||
|
mpath: "electronics.computers.",
|
||||||
|
parent_category_id: "electronics",
|
||||||
|
parent_category: {
|
||||||
|
id: "electronics",
|
||||||
|
handle: "electronics",
|
||||||
|
mpath: "electronics.",
|
||||||
|
parent_category_id: null,
|
||||||
|
parent_category: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
category_children: [
|
||||||
|
{
|
||||||
|
id: "budget-gaming",
|
||||||
|
handle: "budget-gaming-laptops",
|
||||||
|
mpath:
|
||||||
|
"electronics.computers.laptops.gaming-laptops.budget-gaming.",
|
||||||
|
parent_category_id: "gaming-laptops",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "high-performance",
|
||||||
|
handle: "high-performance-gaming-laptops",
|
||||||
|
mpath:
|
||||||
|
"electronics.computers.laptops.gaming-laptops.high-performance.",
|
||||||
|
parent_category_id: "gaming-laptops",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("includes the entire list of parents when include_ancestors_tree is true for multiple results", async () => {
|
||||||
|
const productCategoryResults = await service.list(
|
||||||
|
{
|
||||||
|
parent_category_id: "category-1",
|
||||||
|
include_ancestors_tree: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["id", "handle"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const serializedObject = JSON.parse(
|
||||||
|
JSON.stringify(productCategoryResults)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedObject).toEqual([
|
||||||
|
{
|
||||||
|
id: "category-1-a",
|
||||||
|
handle: "category-1-a",
|
||||||
|
mpath: "category-0.category-1.category-1-a.",
|
||||||
|
parent_category_id: "category-1",
|
||||||
|
parent_category: {
|
||||||
|
id: "category-1",
|
||||||
|
handle: "category-1",
|
||||||
|
mpath: "category-0.category-1.",
|
||||||
|
parent_category_id: "category-0",
|
||||||
|
parent_category: {
|
||||||
|
id: "category-0",
|
||||||
|
handle: "category-0",
|
||||||
|
mpath: "category-0.",
|
||||||
|
parent_category_id: null,
|
||||||
|
parent_category: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "category-1-b",
|
||||||
|
handle: "category-1-b",
|
||||||
|
mpath: "category-0.category-1.category-1-b.",
|
||||||
|
parent_category_id: "category-1",
|
||||||
|
parent_category: {
|
||||||
|
id: "category-1",
|
||||||
|
handle: "category-1",
|
||||||
|
mpath: "category-0.category-1.",
|
||||||
|
parent_category_id: "category-0",
|
||||||
|
parent_category: {
|
||||||
|
id: "category-0",
|
||||||
|
handle: "category-0",
|
||||||
|
mpath: "category-0.",
|
||||||
|
parent_category_id: null,
|
||||||
|
parent_category: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("includes the entire list of descendants an parents when include_descendants_tree and include_ancestors_tree are true for multiple results", async () => {
|
||||||
|
const productCategoryResults = await service.list(
|
||||||
|
{
|
||||||
|
parent_category_id: "category-1",
|
||||||
|
include_descendants_tree: true,
|
||||||
|
include_ancestors_tree: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: ["id", "handle"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const serializedObject = JSON.parse(
|
||||||
|
JSON.stringify(productCategoryResults)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedObject).toEqual([
|
||||||
|
{
|
||||||
|
id: "category-1-a",
|
||||||
|
handle: "category-1-a",
|
||||||
|
mpath: "category-0.category-1.category-1-a.",
|
||||||
|
parent_category_id: "category-1",
|
||||||
|
parent_category: {
|
||||||
|
id: "category-1",
|
||||||
|
handle: "category-1",
|
||||||
|
mpath: "category-0.category-1.",
|
||||||
|
parent_category_id: "category-0",
|
||||||
|
parent_category: {
|
||||||
|
id: "category-0",
|
||||||
|
handle: "category-0",
|
||||||
|
mpath: "category-0.",
|
||||||
|
parent_category_id: null,
|
||||||
|
parent_category: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
category_children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "category-1-b",
|
||||||
|
handle: "category-1-b",
|
||||||
|
mpath: "category-0.category-1.category-1-b.",
|
||||||
|
parent_category_id: "category-1",
|
||||||
|
parent_category: {
|
||||||
|
id: "category-1",
|
||||||
|
handle: "category-1",
|
||||||
|
mpath: "category-0.category-1.",
|
||||||
|
parent_category_id: "category-0",
|
||||||
|
parent_category: {
|
||||||
|
id: "category-0",
|
||||||
|
handle: "category-0",
|
||||||
|
mpath: "category-0.",
|
||||||
|
parent_category_id: null,
|
||||||
|
parent_category: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
category_children: [
|
||||||
|
{
|
||||||
|
id: "category-1-b-1",
|
||||||
|
handle: "category-1-b-1",
|
||||||
|
mpath: "category-0.category-1.category-1-b.category-1-b-1.",
|
||||||
|
parent_category_id: "category-1-b",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
it("scopes children when include_descendants_tree is true", async () => {
|
it("scopes children when include_descendants_tree is true", async () => {
|
||||||
const productCategoryResults = await service.list(
|
const productCategoryResults = await service.list(
|
||||||
{
|
{
|
||||||
@@ -202,14 +558,12 @@ moduleIntegrationTestRunner({
|
|||||||
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: "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",
|
||||||
parent_category: "category-1",
|
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@@ -456,14 +810,12 @@ moduleIntegrationTestRunner({
|
|||||||
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: "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",
|
||||||
parent_category: "category-1",
|
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -471,7 +823,6 @@ moduleIntegrationTestRunner({
|
|||||||
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: "category-1",
|
|
||||||
category_children: [
|
category_children: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "category-1-b-1",
|
id: "category-1-b-1",
|
||||||
@@ -479,7 +830,6 @@ moduleIntegrationTestRunner({
|
|||||||
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",
|
||||||
parent_category: "category-1-b",
|
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@@ -522,14 +872,12 @@ moduleIntegrationTestRunner({
|
|||||||
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: "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",
|
||||||
parent_category: "category-1",
|
|
||||||
category_children: [],
|
category_children: [],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
|
Context,
|
||||||
|
DAL,
|
||||||
|
ProductCategoryTransformOptions,
|
||||||
|
ProductTypes,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { DALUtils, MedusaError, isDefined } from "@medusajs/utils"
|
||||||
|
import {
|
||||||
|
LoadStrategy,
|
||||||
FilterQuery as MikroFilterQuery,
|
FilterQuery as MikroFilterQuery,
|
||||||
FindOptions as MikroOptions,
|
FindOptions as MikroOptions,
|
||||||
LoadStrategy,
|
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import { ProductCategory } from "@models"
|
|
||||||
import { Context, DAL, ProductCategoryTransformOptions } from "@medusajs/types"
|
|
||||||
import groupBy from "lodash/groupBy"
|
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
import { DALUtils, isDefined, MedusaError } from "@medusajs/utils"
|
import { ProductCategory } from "@models"
|
||||||
import { ProductTypes } from "@medusajs/types"
|
|
||||||
|
|
||||||
export type ReorderConditions = {
|
export type ReorderConditions = {
|
||||||
targetCategoryId: string
|
targetCategoryId: string
|
||||||
@@ -34,13 +37,13 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
|||||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
|
|
||||||
const findOptions_ = { ...findOptions }
|
const findOptions_ = { ...findOptions }
|
||||||
const { includeDescendantsTree } = transformOptions
|
const { includeDescendantsTree, includeParentsTree } = transformOptions
|
||||||
findOptions_.options ??= {}
|
findOptions_.options ??= {}
|
||||||
const fields = (findOptions_.options.fields ??= [])
|
const fields = (findOptions_.options.fields ??= [])
|
||||||
|
|
||||||
// Ref: Building descendants
|
// Ref: Building descendants
|
||||||
// mpath and parent_category_id needs to be added to the query for the tree building to be done accurately
|
// mpath and parent_category_id needs to be added to the query for the tree building to be done accurately
|
||||||
if (includeDescendantsTree) {
|
if (includeDescendantsTree || includeParentsTree) {
|
||||||
fields.indexOf("mpath") === -1 && fields.push("mpath")
|
fields.indexOf("mpath") === -1 && fields.push("mpath")
|
||||||
fields.indexOf("parent_category_id") === -1 &&
|
fields.indexOf("parent_category_id") === -1 &&
|
||||||
fields.push("parent_category_id")
|
fields.push("parent_category_id")
|
||||||
@@ -56,65 +59,118 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
|||||||
findOptions_.options as MikroOptions<ProductCategory>
|
findOptions_.options as MikroOptions<ProductCategory>
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!includeDescendantsTree) {
|
if (!includeDescendantsTree && !includeParentsTree) {
|
||||||
return productCategories
|
return productCategories
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.buildProductCategoriesWithDescendants(
|
return this.buildProductCategoriesWithTree(
|
||||||
|
{
|
||||||
|
descendants: includeDescendantsTree,
|
||||||
|
parents: includeParentsTree,
|
||||||
|
},
|
||||||
productCategories,
|
productCategories,
|
||||||
findOptions_
|
findOptions_
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async buildProductCategoriesWithDescendants(
|
async buildProductCategoriesWithTree(
|
||||||
|
include: {
|
||||||
|
descendants?: boolean
|
||||||
|
parents?: boolean
|
||||||
|
},
|
||||||
productCategories: ProductCategory[],
|
productCategories: ProductCategory[],
|
||||||
findOptions: DAL.FindOptions<ProductCategory> = { where: {} },
|
findOptions: DAL.FindOptions<ProductCategory> = { where: {} },
|
||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<ProductCategory[]> {
|
): Promise<ProductCategory[]> {
|
||||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
|
|
||||||
for (let productCategory of productCategories) {
|
const hasPopulateParentCategory = (
|
||||||
const whereOptions = {
|
findOptions.options?.populate ?? ([] as any)
|
||||||
...findOptions.where,
|
).find((pop) => pop.field === "parent_category")
|
||||||
mpath: {
|
|
||||||
$like: `${productCategory.mpath}%`,
|
include.parents = include.parents || hasPopulateParentCategory
|
||||||
},
|
|
||||||
|
const mpaths: any[] = []
|
||||||
|
const parentMpaths = new Set()
|
||||||
|
for (const cat of productCategories) {
|
||||||
|
if (include.descendants) {
|
||||||
|
mpaths.push({ mpath: { $like: `${cat.mpath}%` } })
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("parent_category_id" in whereOptions) {
|
if (include.parents) {
|
||||||
delete whereOptions.parent_category_id
|
let parent = ""
|
||||||
}
|
cat.mpath?.split(".").forEach((mpath) => {
|
||||||
|
if (mpath === "") {
|
||||||
if ("id" in whereOptions) {
|
return
|
||||||
delete whereOptions.id
|
}
|
||||||
}
|
parentMpaths.add(parent + mpath + ".")
|
||||||
|
parent += mpath + "."
|
||||||
const descendantsForCategory = await manager.find(
|
|
||||||
ProductCategory,
|
|
||||||
whereOptions as MikroFilterQuery<ProductCategory>,
|
|
||||||
findOptions.options as MikroOptions<ProductCategory>
|
|
||||||
)
|
|
||||||
|
|
||||||
const descendantsByParentId = groupBy(
|
|
||||||
descendantsForCategory,
|
|
||||||
(pc) => pc.parent_category_id
|
|
||||||
)
|
|
||||||
|
|
||||||
const addChildrenToCategory = (category, children) => {
|
|
||||||
category.category_children = (children || []).map((categoryChild) => {
|
|
||||||
const moreChildren = descendantsByParentId[categoryChild.id] || []
|
|
||||||
|
|
||||||
return addChildrenToCategory(categoryChild, moreChildren)
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mpaths.push({ mpath: Array.from(parentMpaths) })
|
||||||
|
|
||||||
|
const whereOptions = {
|
||||||
|
...findOptions.where,
|
||||||
|
$or: mpaths,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("parent_category_id" in whereOptions) {
|
||||||
|
delete whereOptions.parent_category_id
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("id" in whereOptions) {
|
||||||
|
delete whereOptions.id
|
||||||
|
}
|
||||||
|
|
||||||
|
let allCategories = await manager.find(
|
||||||
|
ProductCategory,
|
||||||
|
whereOptions as MikroFilterQuery<ProductCategory>,
|
||||||
|
findOptions.options as MikroOptions<ProductCategory>
|
||||||
|
)
|
||||||
|
|
||||||
|
allCategories = JSON.parse(JSON.stringify(allCategories))
|
||||||
|
|
||||||
|
const categoriesById = new Map(allCategories.map((cat) => [cat.id, cat]))
|
||||||
|
|
||||||
|
allCategories.forEach((cat: any) => {
|
||||||
|
if (cat.parent_category_id) {
|
||||||
|
cat.parent_category = categoriesById.get(cat.parent_category_id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const populateChildren = (category, level = 0) => {
|
||||||
|
const categories = allCategories.filter(
|
||||||
|
(child) => child.parent_category_id === category.id
|
||||||
|
)
|
||||||
|
|
||||||
|
if (include.descendants) {
|
||||||
|
category.category_children = categories.map((child) => {
|
||||||
|
return populateChildren(categoriesById.get(child.id), level + 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level === 0) {
|
||||||
return category
|
return category
|
||||||
}
|
}
|
||||||
|
|
||||||
const children = descendantsByParentId[productCategory.id] || []
|
if (include.parents) {
|
||||||
productCategory = addChildrenToCategory(productCategory, children)
|
delete category.category_children
|
||||||
|
}
|
||||||
|
if (include.descendants) {
|
||||||
|
delete category.parent_category
|
||||||
|
}
|
||||||
|
|
||||||
|
return category
|
||||||
}
|
}
|
||||||
|
|
||||||
return productCategories
|
const populatedProductCategories = productCategories.map((cat) => {
|
||||||
|
const fullCategory = categoriesById.get(cat.id)
|
||||||
|
return populateChildren(fullCategory)
|
||||||
|
})
|
||||||
|
|
||||||
|
return populatedProductCategories
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAndCount(
|
async findAndCount(
|
||||||
@@ -125,7 +181,7 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
|||||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
|
|
||||||
const findOptions_ = { ...findOptions }
|
const findOptions_ = { ...findOptions }
|
||||||
const { includeDescendantsTree } = transformOptions
|
const { includeDescendantsTree, includeParentsTree } = transformOptions
|
||||||
findOptions_.options ??= {}
|
findOptions_.options ??= {}
|
||||||
const fields = (findOptions_.options.fields ??= [])
|
const fields = (findOptions_.options.fields ??= [])
|
||||||
|
|
||||||
@@ -146,13 +202,20 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
|||||||
findOptions_.where as MikroFilterQuery<ProductCategory>,
|
findOptions_.where as MikroFilterQuery<ProductCategory>,
|
||||||
findOptions_.options as MikroOptions<ProductCategory>
|
findOptions_.options as MikroOptions<ProductCategory>
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!includeDescendantsTree) {
|
if (!includeDescendantsTree) {
|
||||||
return [productCategories, count]
|
return [productCategories, count]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!includeDescendantsTree && !includeParentsTree) {
|
||||||
|
return [productCategories, count]
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
await this.buildProductCategoriesWithDescendants(
|
await this.buildProductCategoriesWithTree(
|
||||||
|
{
|
||||||
|
descendants: includeDescendantsTree,
|
||||||
|
parents: includeParentsTree,
|
||||||
|
},
|
||||||
productCategories,
|
productCategories,
|
||||||
findOptions_
|
findOptions_
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { ProductCategory } from "@models"
|
|
||||||
import { ProductCategoryRepository } from "@repositories"
|
|
||||||
import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types"
|
import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
InjectManager,
|
InjectManager,
|
||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
isDefined,
|
|
||||||
MedusaContext,
|
MedusaContext,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
ModulesSdkUtils,
|
ModulesSdkUtils,
|
||||||
|
isDefined,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
|
import { ProductCategory } from "@models"
|
||||||
|
import { ProductCategoryRepository } from "@repositories"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
productCategoryRepository: DAL.TreeRepositoryService
|
productCategoryRepository: DAL.TreeRepositoryService
|
||||||
@@ -71,8 +71,10 @@ export default class ProductCategoryService<
|
|||||||
): Promise<TEntity[]> {
|
): Promise<TEntity[]> {
|
||||||
const transformOptions = {
|
const transformOptions = {
|
||||||
includeDescendantsTree: filters?.include_descendants_tree || false,
|
includeDescendantsTree: filters?.include_descendants_tree || false,
|
||||||
|
includeParentsTree: filters?.include_ancestors_tree || false,
|
||||||
}
|
}
|
||||||
delete filters.include_descendants_tree
|
delete filters.include_descendants_tree
|
||||||
|
delete filters.include_ancestors_tree
|
||||||
|
|
||||||
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>(
|
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>(
|
||||||
filters,
|
filters,
|
||||||
@@ -95,8 +97,10 @@ export default class ProductCategoryService<
|
|||||||
): Promise<[TEntity[], number]> {
|
): Promise<[TEntity[], number]> {
|
||||||
const transformOptions = {
|
const transformOptions = {
|
||||||
includeDescendantsTree: filters?.include_descendants_tree || false,
|
includeDescendantsTree: filters?.include_descendants_tree || false,
|
||||||
|
includeParentsTree: filters?.include_ancestors_tree || false,
|
||||||
}
|
}
|
||||||
delete filters.include_descendants_tree
|
delete filters.include_descendants_tree
|
||||||
|
delete filters.include_ancestors_tree
|
||||||
|
|
||||||
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>(
|
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>(
|
||||||
filters,
|
filters,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { RepositoryTransformOptions } from '../common'
|
import { RepositoryTransformOptions } from "../common"
|
||||||
|
|
||||||
export interface ProductCategoryTransformOptions extends RepositoryTransformOptions {
|
export interface ProductCategoryTransformOptions
|
||||||
|
extends RepositoryTransformOptions {
|
||||||
includeDescendantsTree?: boolean
|
includeDescendantsTree?: boolean
|
||||||
|
includeParentsTree?: boolean
|
||||||
}
|
}
|
||||||
@@ -915,6 +915,10 @@ export interface FilterableProductCategoryProps
|
|||||||
* Whether to include children of retrieved product categories.
|
* Whether to include children of retrieved product categories.
|
||||||
*/
|
*/
|
||||||
include_descendants_tree?: boolean
|
include_descendants_tree?: boolean
|
||||||
|
/**
|
||||||
|
* Whether to include parents of retrieved product categories.
|
||||||
|
*/
|
||||||
|
include_ancestors_tree?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in new issue
Block a user