feat: Add collection and category endpoints to store (#7155)

* feat: Add collection endpoints to store

* feat: Add category store endpoints
This commit is contained in:
Stevche Radevski authored and GitHub committed 2024-05-01 09:54:51 +02:00
1 parent 2c807df99d
commit ec37576dd0
25 files changed
+621 -279

No files matched your search

@@ -1,52 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`/store/collections /store/collections lists collections 1`] = `
Object {
"collections": Array [
Object {
"created_at": Any<String>,
"deleted_at": null,
"handle": "test-collection2",
"id": "test-collection2",
"metadata": null,
"title": "Test collection 2",
"updated_at": Any<String>,
},
Object {
"created_at": Any<String>,
"deleted_at": null,
"handle": "test-collection1",
"id": "test-collection1",
"metadata": null,
"title": "Test collection 1",
"updated_at": Any<String>,
},
Object {
"created_at": Any<String>,
"deleted_at": null,
"handle": "test-collection",
"id": "test-collection",
"metadata": null,
"title": "Test collection",
"updated_at": Any<String>,
},
],
"count": 3,
"limit": 10,
"offset": 0,
}
`;
exports[`/store/collections /store/collections/:id gets collection 1`] = `
Object {
"collection": Object {
"created_at": Any<String>,
"deleted_at": null,
"handle": "test-collection",
"id": "test-collection",
"metadata": null,
"title": "Test collection",
"updated_at": Any<String>,
},
}
`;
@@ -1,90 +0,0 @@
const { ProductCollection } = require("@medusajs/medusa")
const path = require("path")
const setupServer = require("../../../environment-helpers/setup-server")
const { useApi } = require("../../../environment-helpers/use-api")
const { initDb, useDb } = require("../../../environment-helpers/use-db")
const productSeeder = require("../../../helpers/product-seeder")
jest.setTimeout(30000)
describe("/store/collections", () => {
let medusaProcess
let dbConnection
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
dbConnection = await initDb({ cwd })
medusaProcess = await setupServer({ cwd })
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
describe("/store/collections/:id", () => {
beforeEach(async () => {
await productSeeder(dbConnection)
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("gets collection", async () => {
const api = useApi()
const response = await api.get("/store/collections/test-collection")
expect(response.data).toMatchSnapshot({
collection: {
id: "test-collection",
created_at: expect.any(String),
updated_at: expect.any(String),
},
})
})
})
describe("/store/collections", () => {
beforeEach(async () => {
await productSeeder(dbConnection)
})
afterEach(async () => {
const db = useDb()
await db.teardown()
})
it("lists collections", async () => {
const api = useApi()
const response = await api.get("/store/collections")
expect(response.data).toMatchSnapshot({
collections: [
{
id: "test-collection2",
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: "test-collection1",
created_at: expect.any(String),
updated_at: expect.any(String),
},
{
id: "test-collection",
created_at: expect.any(String),
updated_at: expect.any(String),
},
],
count: 3,
limit: 10,
offset: 0,
})
})
})
})
@@ -0,0 +1,92 @@
import { medusaIntegrationTestRunner } from "medusa-test-utils"
import {
createAdminUser,
adminHeaders,
} from "../../../helpers/create-admin-user"
jest.setTimeout(30000)
medusaIntegrationTestRunner({
env: { MEDUSA_FF_PRODUCT_CATEGORIES: true },
testSuite: ({ dbConnection, getContainer, api }) => {
let baseCollection
let baseCollection1
let baseCollection2
beforeEach(async () => {
const container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container)
baseCollection = (
await api.post(
"/admin/collections",
{ title: "test-collection" },
adminHeaders
)
).data.collection
baseCollection1 = (
await api.post(
"/admin/collections",
{ title: "test-collection1" },
adminHeaders
)
).data.collection
baseCollection2 = (
await api.post(
"/admin/collections",
{ title: "test-collection2" },
adminHeaders
)
).data.collection
})
describe("/store/collections", () => {
describe("/store/collections/:id", () => {
it("gets collection", async () => {
const response = await api.get(
`/store/collections/${baseCollection.id}`
)
expect(response.data.collection).toEqual(
expect.objectContaining({
id: baseCollection.id,
created_at: expect.any(String),
updated_at: expect.any(String),
})
)
})
})
describe("/store/collections", () => {
it("lists collections", async () => {
const response = await api.get("/store/collections")
expect(response.data).toEqual({
collections: [
expect.objectContaining({
id: baseCollection2.id,
created_at: expect.any(String),
updated_at: expect.any(String),
}),
expect.objectContaining({
id: baseCollection1.id,
created_at: expect.any(String),
updated_at: expect.any(String),
}),
expect.objectContaining({
id: baseCollection.id,
created_at: expect.any(String),
updated_at: expect.any(String),
}),
],
count: 3,
limit: 10,
offset: 0,
})
})
})
})
},
})
@@ -1,102 +1,113 @@
import {ProductCategory} from "@medusajs/medusa"
import path from "path"
import startServerWithEnvironment
from "../../../environment-helpers/start-server-with-environment"
import {useApi} from "../../../environment-helpers/use-api"
import {useDb} from "../../../environment-helpers/use-db"
import {simpleProductCategoryFactory} from "../../../factories"
import { medusaIntegrationTestRunner } from "medusa-test-utils"
import {
adminHeaders,
createAdminUser,
} from "../../../helpers/create-admin-user"
import { breaking } from "../../../helpers/breaking"
jest.setTimeout(30000)
describe("/store/product-categories", () => {
let medusaProcess
let dbConnection
let productCategory!: ProductCategory
let productCategory2!: ProductCategory
let productCategoryChild!: ProductCategory
let productCategoryParent!: ProductCategory
let productCategoryChild2!: ProductCategory
let productCategoryChild3!: ProductCategory
let productCategoryChild4!: ProductCategory
beforeAll(async () => {
const cwd = path.resolve(path.join(__dirname, "..", ".."))
const [process, connection] = await startServerWithEnvironment({
cwd,
medusaIntegrationTestRunner({
env: { MEDUSA_FF_PRODUCT_CATEGORIES: true },
})
dbConnection = connection
medusaProcess = process
})
afterAll(async () => {
const db = useDb()
await db.shutdown()
medusaProcess.kill()
})
testSuite: ({ dbConnection, getContainer, api }) => {
let productCategoryParent
let productCategory
let productCategoryChild
let productCategoryChild2
let productCategoryChild3
let productCategoryChild4
beforeEach(async () => {
productCategoryParent = await simpleProductCategoryFactory(dbConnection, {
const container = getContainer()
await createAdminUser(dbConnection, adminHeaders, container)
productCategoryParent = (
await api.post(
"/admin/product-categories",
{
name: "category parent",
description: "test description",
is_active: true,
is_internal: false,
rank: 0,
})
},
adminHeaders
)
).data.product_category
productCategory = await simpleProductCategoryFactory(dbConnection, {
productCategory = (
await api.post(
"/admin/product-categories",
{
name: "category",
parent_category: productCategoryParent,
parent_category_id: productCategoryParent.id,
is_active: true,
rank: 0,
})
},
adminHeaders
)
).data.product_category
productCategoryChild = await simpleProductCategoryFactory(dbConnection, {
name: "category child",
parent_category: productCategory,
is_active: true,
is_internal: false,
rank: 3,
})
productCategoryChild2 = await simpleProductCategoryFactory(dbConnection, {
// The order in which the children are created is intentional as in v1 there was no way to explicitly set the rank.
productCategoryChild2 = (
await api.post(
"/admin/product-categories",
{
name: "category child 2",
parent_category: productCategory,
parent_category_id: productCategory.id,
is_internal: true,
is_active: true,
rank: 0,
})
},
adminHeaders
)
).data.product_category
productCategoryChild3 = await simpleProductCategoryFactory(dbConnection, {
productCategoryChild3 = (
await api.post(
"/admin/product-categories",
{
name: "category child 3",
parent_category: productCategory,
is_active: false,
parent_category_id: productCategory.id,
is_internal: false,
rank: 1,
})
is_active: false,
},
adminHeaders
)
).data.product_category
productCategoryChild4 = await simpleProductCategoryFactory(dbConnection, {
productCategoryChild4 = (
await api.post(
"/admin/product-categories",
{
name: "category child 4",
parent_category: productCategory,
parent_category_id: productCategory.id,
is_internal: false,
is_active: true,
},
adminHeaders
)
).data.product_category
productCategoryChild = (
await api.post(
"/admin/product-categories",
{
name: "category child",
parent_category_id: productCategory.id,
is_active: true,
is_internal: false,
rank: 2,
})
},
adminHeaders
)
).data.product_category
})
describe("/store/product-categories", () => {
describe("GET /store/product-categories/:id", () => {
afterEach(async () => {
const db = useDb()
return await db.teardown()
})
it("gets product category with children tree and parent", async () => {
const api = useApi()
const response = await api.get(
`/store/product-categories/${productCategory.id}?fields=handle,name,description`
`/store/product-categories/${productCategory.id}?${breaking(
() => "fields=handle,name,description",
() => "include_ancestors_tree=true&include_descendants_tree=true"
)}`
)
expect(response.data.product_category).toEqual(
@@ -111,7 +122,7 @@ describe("/store/product-categories", () => {
name: productCategoryParent.name,
description: "test description",
}),
category_children: [
category_children: expect.arrayContaining([
expect.objectContaining({
id: productCategoryChild4.id,
handle: productCategoryChild4.handle,
@@ -122,16 +133,15 @@ describe("/store/product-categories", () => {
handle: productCategoryChild.handle,
name: productCategoryChild.name,
}),
],
]),
})
)
expect(response.status).toEqual(200)
})
// TODO: This one is failing since we don't validate allowed fields currently. We should add that as part of our validators
it("throws error on querying not allowed fields", async () => {
const api = useApi()
const error = await api
.get(`/store/product-categories/${productCategory.id}?fields=mpath`)
.catch((e) => e)
@@ -144,8 +154,6 @@ describe("/store/product-categories", () => {
})
it("throws error on querying for internal product category", async () => {
const api = useApi()
const error = await api
.get(`/store/product-categories/${productCategoryChild2.id}`)
.catch((e) => e)
@@ -153,13 +161,16 @@ describe("/store/product-categories", () => {
expect(error.response.status).toEqual(404)
expect(error.response.data.type).toEqual("not_found")
expect(error.response.data.message).toEqual(
`ProductCategory with id: ${productCategoryChild2.id}, is_internal: false, is_active: true was not found`
breaking(
() =>
`ProductCategory with id: ${productCategoryChild2.id}, is_internal: false, is_active: true was not found`,
() =>
`Product category with id: ${productCategoryChild2.id} was not found`
)
)
})
it("throws error on querying for inactive product category", async () => {
const api = useApi()
const error = await api
.get(`/store/product-categories/${productCategoryChild3.id}`)
.catch((e) => e)
@@ -167,21 +178,25 @@ describe("/store/product-categories", () => {
expect(error.response.status).toEqual(404)
expect(error.response.data.type).toEqual("not_found")
expect(error.response.data.message).toEqual(
`ProductCategory with id: ${productCategoryChild3.id}, is_internal: false, is_active: true was not found`
breaking(
() =>
`ProductCategory with id: ${productCategoryChild3.id}, is_internal: false, is_active: true was not found`,
() =>
`Product category with id: ${productCategoryChild3.id} was not found`
)
)
})
})
describe("GET /store/product-categories", () => {
afterEach(async () => {
const db = useDb()
return await db.teardown()
})
//TODO: The listing results in V2 are unexpected and differ from v1, we need to investigate where the issue is
it("gets list of product category with immediate children and parents", async () => {
const api = useApi()
const response = await api.get(`/store/product-categories?limit=10`)
const response = await api.get(
`/store/product-categories?limit=10${breaking(
() => "",
() => "&include_ancestors_tree=true&include_descendants_tree=true"
)}`
)
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(4)
@@ -195,7 +210,7 @@ describe("/store/product-categories", () => {
parent_category: expect.objectContaining({
id: productCategoryParent.id,
}),
category_children: [
category_children: expect.arrayContaining([
expect.objectContaining({
id: productCategoryChild4.id,
rank: 2,
@@ -204,7 +219,7 @@ describe("/store/product-categories", () => {
id: productCategoryChild.id,
rank: 3,
}),
],
]),
}),
expect.objectContaining({
id: productCategoryParent.id,
@@ -235,9 +250,8 @@ describe("/store/product-categories", () => {
])
})
// TODO: It seems filtering using null doesn't work.
it("gets list of product category with all childrens when include_descendants_tree=true", async () => {
const api = useApi()
const response = await api.get(
`/store/product-categories?parent_category_id=null&include_descendants_tree=true&limit=10`
)
@@ -277,8 +291,6 @@ describe("/store/product-categories", () => {
})
it("throws error when querying not allowed fields", async () => {
const api = useApi()
const error = await api
.get(`/store/product-categories?is_internal=true&limit=10`)
.catch((e) => e)
@@ -291,8 +303,6 @@ describe("/store/product-categories", () => {
})
it("filters based on free text on name and handle columns", async () => {
const api = useApi()
const response = await api.get(
`/store/product-categories?q=category-parent&limit=10`
)
@@ -305,20 +315,18 @@ describe("/store/product-categories", () => {
})
it("filters based on handle attribute of the data model", async () => {
const api = useApi()
const response = await api.get(
`/store/product-categories?handle=${productCategory.handle}&limit=10`
)
expect(response.status).toEqual(200)
expect(response.data.count).toEqual(1)
expect(response.data.product_categories[0].id).toEqual(productCategory.id)
expect(response.data.product_categories[0].id).toEqual(
productCategory.id
)
})
it("filters based on parent category", async () => {
const api = useApi()
const response = await api.get(
`/store/product-categories?parent_category_id=${productCategory.id}&limit=10`
)
@@ -356,3 +364,5 @@ describe("/store/product-categories", () => {
})
})
})
},
})
@@ -10,7 +10,7 @@ export const defaults = [
"created_at",
"updated_at",
"metadata",
"*parent_category",
"*category_children",
]
@@ -34,9 +34,11 @@ import { adminWorkflowsExecutionsMiddlewares } from "./admin/workflows-execution
import { authRoutesMiddlewares } from "./auth/middlewares"
import { hooksRoutesMiddlewares } from "./hooks/middlewares"
import { storeCartRoutesMiddlewares } from "./store/carts/middlewares"
import { storeCollectionRoutesMiddlewares } from "./store/collections/middlewares"
import { storeCurrencyRoutesMiddlewares } from "./store/currencies/middlewares"
import { storeCustomerRoutesMiddlewares } from "./store/customers/middlewares"
import { storeProductRoutesMiddlewares } from "./store/products/middlewares"
import { storeProductCategoryRoutesMiddlewares } from "./store/product-categories/middlewares"
import { storeRegionRoutesMiddlewares } from "./store/regions/middlewares"
export const config: MiddlewaresConfig = {
@@ -48,6 +50,8 @@ export const config: MiddlewaresConfig = {
...storeCartRoutesMiddlewares,
...storeCustomerRoutesMiddlewares,
...storeCartRoutesMiddlewares,
...storeCollectionRoutesMiddlewares,
...storeProductCategoryRoutesMiddlewares,
...authRoutesMiddlewares,
...adminWorkflowsExecutionsMiddlewares,
...storeRegionRoutesMiddlewares,
@@ -0,0 +1,18 @@
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { refetchCollection } from "../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const collection = await refetchCollection(
req.params.id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ collection })
}
@@ -0,0 +1,23 @@
import { MedusaContainer } from "@medusajs/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
export const refetchCollection = async (
collectionId: string,
scope: MedusaContainer,
fields: string[]
) => {
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "product_collection",
variables: {
filters: { id: collectionId },
},
fields: fields,
})
const collections = await remoteQuery(queryObject)
return collections[0]
}
@@ -0,0 +1,30 @@
import * as QueryConfig from "./query-config"
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { validateAndTransformQuery } from "../../utils/validate-query"
import {
StoreGetCollectionParams,
StoreGetCollectionsParams,
} from "./validators"
export const storeCollectionRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["GET"],
matcher: "/store/collections",
middlewares: [
validateAndTransformQuery(
StoreGetCollectionsParams,
QueryConfig.listTransformQueryConfig
),
],
},
{
method: ["GET"],
matcher: "/store/collections/:id",
middlewares: [
validateAndTransformQuery(
StoreGetCollectionParams,
QueryConfig.retrieveTransformQueryConfig
),
],
},
]
@@ -0,0 +1,18 @@
export const defaultStoreCollectionFields = [
"id",
"title",
"handle",
"created_at",
"updated_at",
]
export const retrieveTransformQueryConfig = {
defaults: defaultStoreCollectionFields,
isList: false,
}
export const listTransformQueryConfig = {
...retrieveTransformQueryConfig,
defaultLimit: 10,
isList: true,
}
@@ -0,0 +1,34 @@
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../types/routing"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const query = remoteQueryObjectFromString({
entryPoint: "product_collection",
variables: {
filters: req.filterableFields,
...req.remoteQueryConfig.pagination,
},
fields: req.remoteQueryConfig.fields,
})
const { rows: collections, metadata } = await remoteQuery(query)
res.json({
collections,
count: metadata.count,
offset: metadata.skip,
limit: metadata.take,
})
}
@@ -0,0 +1,28 @@
import {
createFindParams,
createOperatorMap,
createSelectParams,
} from "../../utils/validators"
import { z } from "zod"
export const StoreGetCollectionParams = createSelectParams()
export type StoreGetCollectionsParamsType = z.infer<
typeof StoreGetCollectionsParams
>
export const StoreGetCollectionsParams = createFindParams({
offset: 0,
limit: 10,
order: "-created_at",
}).merge(
z.object({
q: z.string().optional(),
title: z.union([z.string(), z.array(z.string())]).optional(),
handle: z.union([z.string(), z.array(z.string())]).optional(),
created_at: createOperatorMap().optional(),
updated_at: createOperatorMap().optional(),
deleted_at: createOperatorMap().optional(),
$and: z.lazy(() => StoreGetCollectionsParams.array()).optional(),
$or: z.lazy(() => StoreGetCollectionsParams.array()).optional(),
})
)
@@ -0,0 +1,28 @@
import { StoreProductCategoryResponse } from "@medusajs/types"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../types/routing"
import { refetchCategory } from "../helpers"
import { StoreProductCategoryParamsType } from "../validators"
import { MedusaError } from "@medusajs/utils"
export const GET = async (
req: AuthenticatedMedusaRequest<StoreProductCategoryParamsType>,
res: MedusaResponse<StoreProductCategoryResponse>
) => {
const category = await refetchCategory(
req.params.id,
req.scope,
req.remoteQueryConfig.fields,
req.filterableFields
)
if (!category) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Product category with id: ${req.params.id} was not found`
)
}
res.json({ product_category: category })
}
@@ -0,0 +1,38 @@
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 categories = await remoteQuery(queryObject)
return categories[0]
}
export const applyCategoryFilters = (req, res, next) => {
if (!req.filterableFields) {
req.filterableFields = {}
}
req.filterableFields = {
...req.filterableFields,
is_active: true,
is_internal: false,
}
next()
}
@@ -0,0 +1,33 @@
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
import { validateAndTransformQuery } from "../../utils/validate-query"
import { applyCategoryFilters } from "./helpers"
import * as QueryConfig from "./query-config"
import {
StoreProductCategoriesParams,
StoreProductCategoryParams,
} from "./validators"
export const storeProductCategoryRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["GET"],
matcher: "/store/product-categories",
middlewares: [
validateAndTransformQuery(
StoreProductCategoriesParams,
QueryConfig.listProductCategoryConfig
),
applyCategoryFilters,
],
},
{
method: ["GET"],
matcher: "/store/product-categories/:id",
middlewares: [
validateAndTransformQuery(
StoreProductCategoryParams,
QueryConfig.retrieveProductCategoryConfig
),
applyCategoryFilters,
],
},
]
@@ -0,0 +1,24 @@
export const defaults = [
"id",
"name",
"description",
"handle",
"rank",
"parent_category_id",
"created_at",
"updated_at",
"metadata",
"*parent_category",
"*category_children",
]
export const retrieveProductCategoryConfig = {
defaults,
isList: false,
}
export const listProductCategoryConfig = {
defaults,
defaultLimit: 50,
isList: true,
}
@@ -0,0 +1,35 @@
import { StoreProductCategoryListResponse } from "@medusajs/types"
import {
ContainerRegistrationKeys,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../types/routing"
import { StoreProductCategoriesParamsType } from "./validators"
export const GET = async (
req: AuthenticatedMedusaRequest<StoreProductCategoriesParamsType>,
res: MedusaResponse<StoreProductCategoryListResponse>
) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "product_category",
variables: {
filters: req.filterableFields,
...req.remoteQueryConfig.pagination,
},
fields: req.remoteQueryConfig.fields,
})
const { rows: product_categories, metadata } = await remoteQuery(queryObject)
res.json({
product_categories,
count: metadata.count,
offset: metadata.skip,
limit: metadata.take,
})
}
@@ -0,0 +1,52 @@
import { z } from "zod"
import { optionalBooleanMapper } from "../../../utils/validators/is-boolean"
import {
createFindParams,
createOperatorMap,
createSelectParams,
} from "../../utils/validators"
export type StoreProductCategoryParamsType = z.infer<
typeof StoreProductCategoryParams
>
export const StoreProductCategoryParams = createSelectParams().merge(
z.object({
include_ancestors_tree: z.preprocess(
(val: any) => optionalBooleanMapper.get(val?.toLowerCase()),
z.boolean().optional()
),
include_descendants_tree: z.preprocess(
(val: any) => optionalBooleanMapper.get(val?.toLowerCase()),
z.boolean().optional()
),
})
)
export type StoreProductCategoriesParamsType = z.infer<
typeof StoreProductCategoriesParams
>
export const StoreProductCategoriesParams = createFindParams({
offset: 0,
limit: 50,
}).merge(
z.object({
q: z.string().optional(),
id: z.union([z.string(), z.array(z.string())]).optional(),
description: z.union([z.string(), z.array(z.string())]).optional(),
handle: z.union([z.string(), z.array(z.string())]).optional(),
parent_category_id: z.union([z.string(), z.array(z.string())]).optional(),
include_ancestors_tree: z.preprocess(
(val: any) => optionalBooleanMapper.get(val?.toLowerCase()),
z.boolean().optional()
),
include_descendants_tree: z.preprocess(
(val: any) => optionalBooleanMapper.get(val?.toLowerCase()),
z.boolean().optional()
),
created_at: createOperatorMap().optional(),
updated_at: createOperatorMap().optional(),
deleted_at: createOperatorMap().optional(),
$and: z.lazy(() => StoreProductCategoriesParams.array()).optional(),
$or: z.lazy(() => StoreProductCategoriesParams.array()).optional(),
})
)
@@ -55,6 +55,7 @@ class ProductCategory {
@Property({ columnType: "text", default: "", nullable: false })
description?: string
@Searchable()
@Property({ columnType: "text", nullable: false })
handle?: string
@@ -0,0 +1,16 @@
import { PaginatedResponse } from "../../common"
import { ProductCategoryResponse } from "./common"
/**
* @experimental
*/
export interface AdminProductCategoryResponse {
product_category: ProductCategoryResponse
}
/**
* @experimental
*/
export interface AdminProductCategoryListResponse extends PaginatedResponse {
product_categories: ProductCategoryResponse[]
}
@@ -1 +0,0 @@
export * from "./product-category"
@@ -1,34 +0,0 @@
import { PaginatedResponse } from "../../../common"
/**
* @experimental
*/
interface ProductCategoryResponse {
id: string
name: string
description: string | null
handle: string | null
is_active: boolean
is_internal: boolean
rank: number | null
parent_category_id: string | null
created_at: string | Date
updated_at: string | Date
parent_category: ProductCategoryResponse
category_children: ProductCategoryResponse[]
}
/**
* @experimental
*/
export interface AdminProductCategoryResponse {
product_category: ProductCategoryResponse
}
/**
* @experimental
*/
export interface AdminProductCategoryListResponse extends PaginatedResponse {
product_categories: ProductCategoryResponse[]
}
@@ -0,0 +1,18 @@
/**
* @experimental
*/
export interface ProductCategoryResponse {
id: string
name: string
description: string | null
handle: string | null
is_active: boolean
is_internal: boolean
rank: number | null
parent_category_id: string | null
created_at: string | Date
updated_at: string | Date
parent_category: ProductCategoryResponse
category_children: ProductCategoryResponse[]
}
@@ -1 +1,2 @@
export * from "./admin"
export * from "./store"
@@ -0,0 +1,16 @@
import { PaginatedResponse } from "../../common"
import { ProductCategoryResponse } from "./common"
/**
* @experimental
*/
export interface StoreProductCategoryResponse {
product_category: ProductCategoryResponse
}
/**
* @experimental
*/
export interface StoreProductCategoryListResponse extends PaginatedResponse {
product_categories: ProductCategoryResponse[]
}