fix: Retrieve ancestors and/or descendants on product categories (#7226)
This commit is contained in:
1 parent
520867b074
commit
1366e2efad
11 files changed
+158
-390
No files matched your search
@@ -1,7 +1,6 @@
|
|||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
import { IProductModuleService } from "@medusajs/types"
|
import { IProductModuleService } from "@medusajs/types"
|
||||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
import { In } from "typeorm"
|
|
||||||
import { breaking } from "../../../helpers/breaking"
|
import { breaking } from "../../../helpers/breaking"
|
||||||
import {
|
import {
|
||||||
adminHeaders,
|
adminHeaders,
|
||||||
@@ -16,7 +15,7 @@ let { Product } = {}
|
|||||||
medusaIntegrationTestRunner({
|
medusaIntegrationTestRunner({
|
||||||
env: {
|
env: {
|
||||||
MEDUSA_FF_PRODUCT_CATEGORIES: true,
|
MEDUSA_FF_PRODUCT_CATEGORIES: true,
|
||||||
// MEDUSA_FF_MEDUSA_V2: true,
|
MEDUSA_FF_MEDUSA_V2: true,
|
||||||
},
|
},
|
||||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||||
let appContainer
|
let appContainer
|
||||||
@@ -478,6 +477,37 @@ medusaIntegrationTestRunner({
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("adds all ancestors to categories in a nested way", async () => {
|
||||||
|
const response = await api.get(
|
||||||
|
`/admin/product-categories/${productCategoryChild1.id}?include_ancestors_tree=true`,
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.product_category).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: productCategoryChild1.id,
|
||||||
|
name: "rank 1",
|
||||||
|
rank: 1,
|
||||||
|
parent_category: expect.objectContaining({
|
||||||
|
id: productCategoryChild.id,
|
||||||
|
name: "cashmere",
|
||||||
|
rank: 0,
|
||||||
|
parent_category: expect.objectContaining({
|
||||||
|
id: productCategory.id,
|
||||||
|
name: "sweater",
|
||||||
|
rank: 0,
|
||||||
|
parent_category: expect.objectContaining({
|
||||||
|
id: productCategoryParent.id,
|
||||||
|
name: "Mens",
|
||||||
|
rank: 0,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("POST /admin/product-categories", () => {
|
describe("POST /admin/product-categories", () => {
|
||||||
@@ -525,25 +555,6 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: Remove in V2, unnecessary test
|
|
||||||
it("throws an error when description is not a string", async () => {
|
|
||||||
const payload = {
|
|
||||||
name: "test",
|
|
||||||
handle: "test",
|
|
||||||
description: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
const error = await api
|
|
||||||
.post(`/admin/product-categories`, payload, adminHeaders)
|
|
||||||
.catch((e) => e)
|
|
||||||
|
|
||||||
expect(error.response.status).toEqual(400)
|
|
||||||
expect(error.response.data.type).toEqual("invalid_data")
|
|
||||||
// expect(error.response.data.message).toEqual(
|
|
||||||
// "description must be a string"
|
|
||||||
// )
|
|
||||||
})
|
|
||||||
|
|
||||||
it("successfully creates a product category", async () => {
|
it("successfully creates a product category", async () => {
|
||||||
const payload = {
|
const payload = {
|
||||||
name: "test",
|
name: "test",
|
||||||
@@ -570,16 +581,9 @@ medusaIntegrationTestRunner({
|
|||||||
is_active: false,
|
is_active: false,
|
||||||
created_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
updated_at: expect.any(String),
|
updated_at: expect.any(String),
|
||||||
...breaking(
|
parent_category: expect.objectContaining({
|
||||||
() => ({
|
id: productCategory.id,
|
||||||
parent_category: expect.objectContaining({
|
}),
|
||||||
id: productCategory.id,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
() => ({
|
|
||||||
parent_category_id: productCategory.id,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
category_children: [],
|
category_children: [],
|
||||||
rank: 0,
|
rank: 0,
|
||||||
}),
|
}),
|
||||||
@@ -671,7 +675,8 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("DELETE /admin/product-categories/:id", () => {
|
// TODO: Should be migrate to V2
|
||||||
|
describe.skip("DELETE /admin/product-categories/:id", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
productCategoryParent = await simpleProductCategoryFactory(
|
productCategoryParent = await simpleProductCategoryFactory(
|
||||||
dbConnection,
|
dbConnection,
|
||||||
@@ -1221,245 +1226,7 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: Remove in V2, endpoint changed
|
describe("POST /admin/product-categories/:id/products", () => {
|
||||||
describe("POST /admin/product-categories/:id/products/batch", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
productCategory = await simpleProductCategoryFactory(dbConnection, {
|
|
||||||
id: "test-category",
|
|
||||||
name: "test category",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should add products to a product category", async () => {
|
|
||||||
const testProduct1 = await simpleProductFactory(dbConnection, {
|
|
||||||
id: "test-product-1",
|
|
||||||
title: "test product 1",
|
|
||||||
})
|
|
||||||
|
|
||||||
const testProduct2 = await simpleProductFactory(dbConnection, {
|
|
||||||
id: "test-product-2",
|
|
||||||
title: "test product 2",
|
|
||||||
})
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
product_ids: [{ id: testProduct1.id }, { id: testProduct2.id }],
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await api.post(
|
|
||||||
`/admin/product-categories/${productCategory.id}/products/batch`,
|
|
||||||
payload,
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
|
||||||
expect(response.data.product_category).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: productCategory.id,
|
|
||||||
created_at: expect.any(String),
|
|
||||||
updated_at: expect.any(String),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const products = await dbConnection.manager.find(Product, {
|
|
||||||
where: { id: In([testProduct1.id, testProduct2.id]) },
|
|
||||||
relations: ["categories"],
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(products[0].categories).toEqual([
|
|
||||||
expect.objectContaining({
|
|
||||||
id: productCategory.id,
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
|
|
||||||
expect(products[1].categories).toEqual([
|
|
||||||
expect.objectContaining({
|
|
||||||
id: productCategory.id,
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("throws error when product ID is invalid", async () => {
|
|
||||||
const payload = {
|
|
||||||
product_ids: [{ id: "product-id-invalid" }],
|
|
||||||
}
|
|
||||||
|
|
||||||
const error = await api
|
|
||||||
.post(
|
|
||||||
`/admin/product-categories/${productCategory.id}/products/batch`,
|
|
||||||
payload,
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
.catch((e) => e)
|
|
||||||
|
|
||||||
expect(error.response.status).toEqual(400)
|
|
||||||
expect(error.response.data).toEqual({
|
|
||||||
errors: ["Products product-id-invalid do not exist"],
|
|
||||||
message:
|
|
||||||
"Provided request body contains errors. Please check the data and retry the request",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("throws error when category ID is invalid", async () => {
|
|
||||||
const payload = { product_ids: [] }
|
|
||||||
|
|
||||||
const error = await api
|
|
||||||
.post(
|
|
||||||
`/admin/product-categories/invalid-category-id/products/batch`,
|
|
||||||
payload,
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
.catch((e) => e)
|
|
||||||
|
|
||||||
expect(error.response.status).toEqual(404)
|
|
||||||
expect(error.response.data).toEqual({
|
|
||||||
message: "ProductCategory with id: invalid-category-id was not found",
|
|
||||||
type: "not_found",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("throws error trying to expand not allowed relations", async () => {
|
|
||||||
const payload = { product_ids: [] }
|
|
||||||
|
|
||||||
const error = await api
|
|
||||||
.post(
|
|
||||||
`/admin/product-categories/invalid-category-id/products/batch?expand=products`,
|
|
||||||
payload,
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
.catch((e) => e)
|
|
||||||
|
|
||||||
expect(error.response.status).toEqual(400)
|
|
||||||
expect(error.response.data).toEqual({
|
|
||||||
message: "Requested fields [products] are not valid",
|
|
||||||
type: "invalid_data",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: Remove in v2, endpoint changed
|
|
||||||
describe("DELETE /admin/product-categories/:id/products/batch", () => {
|
|
||||||
let testProduct1, testProduct2
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
testProduct1 = await simpleProductFactory(dbConnection, {
|
|
||||||
id: "test-product-1",
|
|
||||||
title: "test product 1",
|
|
||||||
})
|
|
||||||
|
|
||||||
testProduct2 = await simpleProductFactory(dbConnection, {
|
|
||||||
id: "test-product-2",
|
|
||||||
title: "test product 2",
|
|
||||||
})
|
|
||||||
|
|
||||||
productCategory = await simpleProductCategoryFactory(dbConnection, {
|
|
||||||
id: "test-category",
|
|
||||||
name: "test category",
|
|
||||||
products: [testProduct1, testProduct2],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should remove products from a product category", async () => {
|
|
||||||
const payload = {
|
|
||||||
product_ids: [{ id: testProduct2.id }],
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await api.delete(
|
|
||||||
`/admin/product-categories/${productCategory.id}/products/batch`,
|
|
||||||
{
|
|
||||||
...adminHeaders,
|
|
||||||
data: payload,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
|
||||||
expect(response.data.product_category).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: productCategory.id,
|
|
||||||
created_at: expect.any(String),
|
|
||||||
updated_at: expect.any(String),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const products = await dbConnection.manager.find(Product, {
|
|
||||||
where: { id: In([testProduct1.id, testProduct2.id]) },
|
|
||||||
relations: ["categories"],
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(products[0].categories).toEqual([
|
|
||||||
expect.objectContaining({
|
|
||||||
id: productCategory.id,
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
|
|
||||||
expect(products[1].categories).toEqual([])
|
|
||||||
})
|
|
||||||
|
|
||||||
it("throws error when product ID is invalid", async () => {
|
|
||||||
const payload = {
|
|
||||||
product_ids: [{ id: "product-id-invalid" }],
|
|
||||||
}
|
|
||||||
|
|
||||||
const error = await api
|
|
||||||
.delete(
|
|
||||||
`/admin/product-categories/${productCategory.id}/products/batch`,
|
|
||||||
{
|
|
||||||
...adminHeaders,
|
|
||||||
data: payload,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch((e) => e)
|
|
||||||
|
|
||||||
expect(error.response.status).toEqual(400)
|
|
||||||
expect(error.response.data).toEqual({
|
|
||||||
errors: ["Products product-id-invalid do not exist"],
|
|
||||||
message:
|
|
||||||
"Provided request body contains errors. Please check the data and retry the request",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("throws error when category ID is invalid", async () => {
|
|
||||||
const payload = { product_ids: [] }
|
|
||||||
|
|
||||||
const error = await api
|
|
||||||
.delete(
|
|
||||||
`/admin/product-categories/invalid-category-id/products/batch`,
|
|
||||||
{
|
|
||||||
...adminHeaders,
|
|
||||||
data: payload,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch((e) => e)
|
|
||||||
|
|
||||||
expect(error.response.status).toEqual(404)
|
|
||||||
expect(error.response.data).toEqual({
|
|
||||||
message: "ProductCategory with id: invalid-category-id was not found",
|
|
||||||
type: "not_found",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("throws error trying to expand not allowed relations", async () => {
|
|
||||||
const payload = { product_ids: [] }
|
|
||||||
|
|
||||||
const error = await api
|
|
||||||
.delete(
|
|
||||||
`/admin/product-categories/invalid-category-id/products/batch?expand=products`,
|
|
||||||
{
|
|
||||||
...adminHeaders,
|
|
||||||
data: payload,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch((e) => e)
|
|
||||||
|
|
||||||
expect(error.response.status).toEqual(400)
|
|
||||||
expect(error.response.data).toEqual({
|
|
||||||
message: "Requested fields [products] are not valid",
|
|
||||||
type: "invalid_data",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Skipping because the test is for V2 only
|
|
||||||
describe.skip("POST /admin/product-categories/:id/products", () => {
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
productCategory = await productModuleService.createCategory({
|
productCategory = await productModuleService.createCategory({
|
||||||
name: "category parent",
|
name: "category parent",
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ export class RemoteJoiner {
|
|||||||
uniqueIds,
|
uniqueIds,
|
||||||
relationship
|
relationship
|
||||||
)
|
)
|
||||||
|
|
||||||
const isObj = isDefined(response.path)
|
const isObj = isDefined(response.path)
|
||||||
let resData = isObj ? response.data[response.path!] : response.data
|
let resData = isObj ? response.data[response.path!] : response.data
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "../../../../../types/routing"
|
} from "../../../../../types/routing"
|
||||||
import { refetchCategory } from "../../helpers"
|
import { refetchEntity } from "../../../../utils/refetch-entity"
|
||||||
|
|
||||||
export const POST = async (
|
export const POST = async (
|
||||||
req: AuthenticatedMedusaRequest<LinkMethodRequest>,
|
req: AuthenticatedMedusaRequest<LinkMethodRequest>,
|
||||||
@@ -24,11 +24,11 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
const category = await refetchCategory(
|
const category = await refetchEntity(
|
||||||
req.params.id,
|
"product_category",
|
||||||
|
id,
|
||||||
req.scope,
|
req.scope,
|
||||||
req.remoteQueryConfig.fields,
|
req.remoteQueryConfig.fields
|
||||||
req.filterableFields
|
|
||||||
)
|
)
|
||||||
|
|
||||||
res.status(200).json({ product_category: category })
|
res.status(200).json({ product_category: category })
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "../../../../types/routing"
|
} from "../../../../types/routing"
|
||||||
import { refetchCategory } from "../helpers"
|
import { refetchEntities } from "../../../utils/refetch-entity"
|
||||||
import {
|
import {
|
||||||
AdminProductCategoryParamsType,
|
AdminProductCategoryParamsType,
|
||||||
AdminUpdateProductCategoryType,
|
AdminUpdateProductCategoryType,
|
||||||
@@ -14,11 +14,11 @@ export const GET = async (
|
|||||||
req: AuthenticatedMedusaRequest<AdminProductCategoryParamsType>,
|
req: AuthenticatedMedusaRequest<AdminProductCategoryParamsType>,
|
||||||
res: MedusaResponse<AdminProductCategoryResponse>
|
res: MedusaResponse<AdminProductCategoryResponse>
|
||||||
) => {
|
) => {
|
||||||
const category = await refetchCategory(
|
const [category] = await refetchEntities(
|
||||||
req.params.id,
|
"product_category",
|
||||||
|
{ id: req.params.id, ...req.filterableFields },
|
||||||
req.scope,
|
req.scope,
|
||||||
req.remoteQueryConfig.fields,
|
req.remoteQueryConfig.fields
|
||||||
req.filterableFields
|
|
||||||
)
|
)
|
||||||
|
|
||||||
res.json({ product_category: category })
|
res.json({ product_category: category })
|
||||||
@@ -40,11 +40,11 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
const category = await refetchCategory(
|
const [category] = await refetchEntities(
|
||||||
req.params.id,
|
"product_category",
|
||||||
|
{ id, ...req.filterableFields },
|
||||||
req.scope,
|
req.scope,
|
||||||
req.remoteQueryConfig.fields,
|
req.remoteQueryConfig.fields
|
||||||
req.filterableFields
|
|
||||||
)
|
)
|
||||||
|
|
||||||
res.status(200).json({ product_category: category })
|
res.status(200).json({ product_category: category })
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import { MedusaContainer } from "@medusajs/types"
|
|
||||||
import {
|
|
||||||
ContainerRegistrationKeys,
|
|
||||||
remoteQueryObjectFromString,
|
|
||||||
} from "@medusajs/utils"
|
|
||||||
|
|
||||||
export const refetchCategory = async (
|
|
||||||
categoryId: string,
|
|
||||||
scope: MedusaContainer,
|
|
||||||
fields: string[],
|
|
||||||
filterableFields: Record<string, any> = {}
|
|
||||||
) => {
|
|
||||||
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
|
||||||
const queryObject = remoteQueryObjectFromString({
|
|
||||||
entryPoint: "product_category",
|
|
||||||
variables: {
|
|
||||||
filters: { ...filterableFields, id: categoryId },
|
|
||||||
},
|
|
||||||
fields: fields,
|
|
||||||
})
|
|
||||||
|
|
||||||
const categorys = await remoteQuery(queryObject)
|
|
||||||
return categorys[0]
|
|
||||||
}
|
|
||||||
@@ -10,17 +10,36 @@ export const defaults = [
|
|||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
"metadata",
|
"metadata",
|
||||||
"*parent_category",
|
"parent_category",
|
||||||
"*category_children",
|
"category_children",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const allowed = [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"description",
|
||||||
|
"handle",
|
||||||
|
"is_active",
|
||||||
|
"is_internal",
|
||||||
|
"rank",
|
||||||
|
"parent_category_id",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"metadata",
|
||||||
|
"category_children",
|
||||||
|
"parent_category",
|
||||||
|
"products",
|
||||||
]
|
]
|
||||||
|
|
||||||
export const retrieveProductCategoryConfig = {
|
export const retrieveProductCategoryConfig = {
|
||||||
defaults,
|
defaults,
|
||||||
|
allowed,
|
||||||
isList: false,
|
isList: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const listProductCategoryConfig = {
|
export const listProductCategoryConfig = {
|
||||||
defaults,
|
defaults,
|
||||||
|
allowed,
|
||||||
defaultLimit: 50,
|
defaultLimit: 50,
|
||||||
isList: true,
|
isList: true,
|
||||||
}
|
}
|
||||||
@@ -3,15 +3,11 @@ import {
|
|||||||
AdminProductCategoryListResponse,
|
AdminProductCategoryListResponse,
|
||||||
AdminProductCategoryResponse,
|
AdminProductCategoryResponse,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
|
||||||
ContainerRegistrationKeys,
|
|
||||||
remoteQueryObjectFromString,
|
|
||||||
} from "@medusajs/utils"
|
|
||||||
import {
|
import {
|
||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "../../../types/routing"
|
} from "../../../types/routing"
|
||||||
import { refetchCategory } from "./helpers"
|
import { refetchEntities } from "../../utils/refetch-entity"
|
||||||
import {
|
import {
|
||||||
AdminCreateProductCategoryType,
|
AdminCreateProductCategoryType,
|
||||||
AdminProductCategoriesParamsType,
|
AdminProductCategoriesParamsType,
|
||||||
@@ -21,18 +17,13 @@ export const GET = async (
|
|||||||
req: AuthenticatedMedusaRequest<AdminProductCategoriesParamsType>,
|
req: AuthenticatedMedusaRequest<AdminProductCategoriesParamsType>,
|
||||||
res: MedusaResponse<AdminProductCategoryListResponse>
|
res: MedusaResponse<AdminProductCategoryListResponse>
|
||||||
) => {
|
) => {
|
||||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
const { rows: product_categories, metadata } = await refetchEntities(
|
||||||
|
"product_category",
|
||||||
const queryObject = remoteQueryObjectFromString({
|
req.filterableFields,
|
||||||
entryPoint: "product_category",
|
req.scope,
|
||||||
variables: {
|
req.remoteQueryConfig.fields,
|
||||||
filters: req.filterableFields,
|
req.remoteQueryConfig.pagination
|
||||||
...req.remoteQueryConfig.pagination,
|
)
|
||||||
},
|
|
||||||
fields: req.remoteQueryConfig.fields,
|
|
||||||
})
|
|
||||||
|
|
||||||
const { rows: product_categories, metadata } = await remoteQuery(queryObject)
|
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
product_categories,
|
product_categories,
|
||||||
@@ -57,11 +48,11 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
const category = await refetchCategory(
|
const [category] = await refetchEntities(
|
||||||
result.id,
|
"product_category",
|
||||||
|
{ id: result.id, ...req.filterableFields },
|
||||||
req.scope,
|
req.scope,
|
||||||
req.remoteQueryConfig.fields,
|
req.remoteQueryConfig.fields
|
||||||
req.filterableFields
|
|
||||||
)
|
)
|
||||||
|
|
||||||
res.status(200).json({ product_category: category })
|
res.status(200).json({ product_category: category })
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
import { StoreProductCategoryResponse } from "@medusajs/types"
|
import { StoreProductCategoryResponse } from "@medusajs/types"
|
||||||
|
import { MedusaError } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
AuthenticatedMedusaRequest,
|
AuthenticatedMedusaRequest,
|
||||||
MedusaResponse,
|
MedusaResponse,
|
||||||
} from "../../../../types/routing"
|
} from "../../../../types/routing"
|
||||||
import { refetchCategory } from "../helpers"
|
import { refetchEntities } from "../../../utils/refetch-entity"
|
||||||
import { StoreProductCategoryParamsType } from "../validators"
|
import { StoreProductCategoryParamsType } from "../validators"
|
||||||
import { MedusaError } from "@medusajs/utils"
|
|
||||||
|
|
||||||
export const GET = async (
|
export const GET = async (
|
||||||
req: AuthenticatedMedusaRequest<StoreProductCategoryParamsType>,
|
req: AuthenticatedMedusaRequest<StoreProductCategoryParamsType>,
|
||||||
res: MedusaResponse<StoreProductCategoryResponse>
|
res: MedusaResponse<StoreProductCategoryResponse>
|
||||||
) => {
|
) => {
|
||||||
const category = await refetchCategory(
|
const category = await refetchEntities(
|
||||||
req.params.id,
|
"product_category",
|
||||||
|
{ id: req.params.id, ...req.filterableFields },
|
||||||
req.scope,
|
req.scope,
|
||||||
req.remoteQueryConfig.fields,
|
req.remoteQueryConfig.fields,
|
||||||
req.filterableFields
|
req.remoteQueryConfig.pagination
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!category) {
|
if (!category) {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export const refetchEntities = async (
|
|||||||
entryPoint: string,
|
entryPoint: string,
|
||||||
idOrFilter: string | object,
|
idOrFilter: string | object,
|
||||||
scope: MedusaContainer,
|
scope: MedusaContainer,
|
||||||
fields: string[]
|
fields: string[],
|
||||||
|
pagination: object = {}
|
||||||
) => {
|
) => {
|
||||||
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||||
const filters = isString(idOrFilter) ? { id: idOrFilter } : idOrFilter
|
const filters = isString(idOrFilter) ? { id: idOrFilter } : idOrFilter
|
||||||
@@ -23,7 +24,7 @@ export const refetchEntities = async (
|
|||||||
delete filters.context
|
delete filters.context
|
||||||
}
|
}
|
||||||
|
|
||||||
let variables = { filters, ...context }
|
let variables = { filters, ...context, ...pagination }
|
||||||
|
|
||||||
const queryObject = remoteQueryObjectFromString({
|
const queryObject = remoteQueryObjectFromString({
|
||||||
entryPoint,
|
entryPoint,
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import {
|
|||||||
stringToSelectRelationObject,
|
stringToSelectRelationObject,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { pick } from "lodash"
|
import { pick } from "lodash"
|
||||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
import { MedusaError, isDefined } from "medusa-core-utils"
|
||||||
import { BaseEntity } from "../interfaces"
|
import { BaseEntity } from "../interfaces"
|
||||||
import { FindConfig, QueryConfig, RequestQueryFields } from "../types/common"
|
|
||||||
import { featureFlagRouter } from "../loaders/feature-flags"
|
import { featureFlagRouter } from "../loaders/feature-flags"
|
||||||
import MedusaV2 from "../loaders/feature-flags/medusa-v2"
|
import MedusaV2 from "../loaders/feature-flags/medusa-v2"
|
||||||
|
import { FindConfig, QueryConfig, RequestQueryFields } from "../types/common"
|
||||||
|
|
||||||
export function pickByConfig<TModel extends BaseEntity>(
|
export function pickByConfig<TModel extends BaseEntity>(
|
||||||
obj: TModel | TModel[],
|
obj: TModel | TModel[],
|
||||||
|
|||||||
@@ -29,6 +29,51 @@ 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> {
|
||||||
|
buildFindOptions(
|
||||||
|
findOptions: DAL.FindOptions<ProductCategory> = { where: {} },
|
||||||
|
familyOptions: ProductCategoryTransformOptions = {}
|
||||||
|
) {
|
||||||
|
const findOptions_ = { ...findOptions }
|
||||||
|
findOptions_.options ??= {}
|
||||||
|
|
||||||
|
const fields = (findOptions_.options.fields ??= [])
|
||||||
|
const populate = (findOptions_.options.populate ??= [])
|
||||||
|
|
||||||
|
// Ref: Building descendants
|
||||||
|
// mpath and parent_category_id needs to be added to the query for the tree building to be done accurately
|
||||||
|
if (
|
||||||
|
familyOptions.includeDescendantsTree ||
|
||||||
|
familyOptions.includeAncestorsTree
|
||||||
|
) {
|
||||||
|
fields.indexOf("mpath") === -1 && fields.push("mpath")
|
||||||
|
fields.indexOf("parent_category_id") === -1 &&
|
||||||
|
fields.push("parent_category_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldExpandParent =
|
||||||
|
familyOptions.includeAncestorsTree || fields.includes("parent_category")
|
||||||
|
|
||||||
|
if (shouldExpandParent) {
|
||||||
|
populate.indexOf("parent_category") === -1 &&
|
||||||
|
populate.push("parent_category")
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldExpandChildren =
|
||||||
|
familyOptions.includeDescendantsTree ||
|
||||||
|
fields.includes("category_children")
|
||||||
|
|
||||||
|
if (shouldExpandChildren) {
|
||||||
|
populate.indexOf("category_children") === -1 &&
|
||||||
|
populate.push("category_children")
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(findOptions_.options, {
|
||||||
|
strategy: LoadStrategy.SELECT_IN,
|
||||||
|
})
|
||||||
|
|
||||||
|
return findOptions_
|
||||||
|
}
|
||||||
|
|
||||||
async find(
|
async find(
|
||||||
findOptions: DAL.FindOptions<ProductCategory> = { where: {} },
|
findOptions: DAL.FindOptions<ProductCategory> = { where: {} },
|
||||||
transformOptions: ProductCategoryTransformOptions = {},
|
transformOptions: ProductCategoryTransformOptions = {},
|
||||||
@@ -36,22 +81,7 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
|||||||
): Promise<ProductCategory[]> {
|
): Promise<ProductCategory[]> {
|
||||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
|
|
||||||
const findOptions_ = { ...findOptions }
|
const findOptions_ = this.buildFindOptions(findOptions, transformOptions)
|
||||||
const { includeDescendantsTree, includeAncestorsTree } = transformOptions
|
|
||||||
findOptions_.options ??= {}
|
|
||||||
const fields = (findOptions_.options.fields ??= [])
|
|
||||||
|
|
||||||
// Ref: Building descendants
|
|
||||||
// mpath and parent_category_id needs to be added to the query for the tree building to be done accurately
|
|
||||||
if (includeDescendantsTree || includeAncestorsTree) {
|
|
||||||
fields.indexOf("mpath") === -1 && fields.push("mpath")
|
|
||||||
fields.indexOf("parent_category_id") === -1 &&
|
|
||||||
fields.push("parent_category_id")
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(findOptions_.options, {
|
|
||||||
strategy: LoadStrategy.SELECT_IN,
|
|
||||||
})
|
|
||||||
|
|
||||||
const productCategories = await manager.find(
|
const productCategories = await manager.find(
|
||||||
ProductCategory,
|
ProductCategory,
|
||||||
@@ -59,14 +89,17 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
|||||||
findOptions_.options as MikroOptions<ProductCategory>
|
findOptions_.options as MikroOptions<ProductCategory>
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!includeDescendantsTree && !includeAncestorsTree) {
|
if (
|
||||||
|
!transformOptions.includeDescendantsTree &&
|
||||||
|
!transformOptions.includeAncestorsTree
|
||||||
|
) {
|
||||||
return productCategories
|
return productCategories
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.buildProductCategoriesWithTree(
|
return await this.buildProductCategoriesWithTree(
|
||||||
{
|
{
|
||||||
descendants: includeDescendantsTree,
|
descendants: transformOptions.includeDescendantsTree,
|
||||||
ancestors: includeAncestorsTree,
|
ancestors: transformOptions.includeAncestorsTree,
|
||||||
},
|
},
|
||||||
productCategories,
|
productCategories,
|
||||||
findOptions_
|
findOptions_
|
||||||
@@ -84,12 +117,6 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
|||||||
): Promise<ProductCategory[]> {
|
): Promise<ProductCategory[]> {
|
||||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
|
|
||||||
const hasPopulateParentCategory = (
|
|
||||||
findOptions.options?.populate ?? ([] as any)
|
|
||||||
).find((pop) => pop.field === "parent_category")
|
|
||||||
|
|
||||||
include.ancestors = include.ancestors || hasPopulateParentCategory
|
|
||||||
|
|
||||||
const mpaths: any[] = []
|
const mpaths: any[] = []
|
||||||
const parentMpaths = new Set()
|
const parentMpaths = new Set()
|
||||||
for (const cat of productCategories) {
|
for (const cat of productCategories) {
|
||||||
@@ -179,42 +206,27 @@ export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeReposito
|
|||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<[ProductCategory[], number]> {
|
): Promise<[ProductCategory[], number]> {
|
||||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
|
|
||||||
const findOptions_ = { ...findOptions }
|
const findOptions_ = this.buildFindOptions(findOptions, transformOptions)
|
||||||
const { includeDescendantsTree, includeAncestorsTree } = transformOptions
|
|
||||||
findOptions_.options ??= {}
|
|
||||||
const fields = (findOptions_.options.fields ??= [])
|
|
||||||
|
|
||||||
// Ref: Building descendants
|
|
||||||
// mpath and parent_category_id needs to be added to the query for the tree building to be done accurately
|
|
||||||
if (includeDescendantsTree) {
|
|
||||||
fields.indexOf("mpath") === -1 && fields.push("mpath")
|
|
||||||
fields.indexOf("parent_category_id") === -1 &&
|
|
||||||
fields.push("parent_category_id")
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(findOptions_.options, {
|
|
||||||
strategy: LoadStrategy.SELECT_IN,
|
|
||||||
})
|
|
||||||
|
|
||||||
const [productCategories, count] = await manager.findAndCount(
|
const [productCategories, count] = await manager.findAndCount(
|
||||||
ProductCategory,
|
ProductCategory,
|
||||||
findOptions_.where as MikroFilterQuery<ProductCategory>,
|
findOptions_.where as MikroFilterQuery<ProductCategory>,
|
||||||
findOptions_.options as MikroOptions<ProductCategory>
|
findOptions_.options as MikroOptions<ProductCategory>
|
||||||
)
|
)
|
||||||
if (!includeDescendantsTree) {
|
|
||||||
return [productCategories, count]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!includeDescendantsTree && !includeAncestorsTree) {
|
if (
|
||||||
|
!transformOptions.includeDescendantsTree &&
|
||||||
|
!transformOptions.includeAncestorsTree
|
||||||
|
) {
|
||||||
return [productCategories, count]
|
return [productCategories, count]
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
await this.buildProductCategoriesWithTree(
|
await this.buildProductCategoriesWithTree(
|
||||||
{
|
{
|
||||||
descendants: includeDescendantsTree,
|
descendants: transformOptions.includeDescendantsTree,
|
||||||
ancestors: includeAncestorsTree,
|
ancestors: transformOptions.includeAncestorsTree,
|
||||||
},
|
},
|
||||||
productCategories,
|
productCategories,
|
||||||
findOptions_
|
findOptions_
|
||||||
|
|||||||
Reference in new issue
Block a user