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
2024-05-01 09:54:51 +02:00
committed by GitHub
parent 2c807df99d
commit ec37576dd0
25 changed files with 845 additions and 503 deletions
@@ -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[]
}