fix: API validation management issues (#9693)
**What** Currently, the API validation layer is broken in both responsibilities and validation itself. This pr introduce the following fixes and patterns: - Always create a `*Fields` schema that only takes care of defining the schema validation without `effect` - Use the previous point into the API schema validator including `$and` and `$or` capabilities plus the recursive effects - remove `normalizeArray` which does not have to exists since array are already treated as they should - Add recursive transformation to take into account `$and` and `$or` as well or any other similar operators - New util `applyAndAndOrOperators` to wrap the management of those operators and to be merged to an existing schema Tasks - [x] store domain - [ ] admin domain
This commit is contained in:
committed by
GitHub
parent
7b147aa651
commit
6b989353ac
@@ -8,6 +8,7 @@ import {
|
|||||||
generateStoreHeaders,
|
generateStoreHeaders,
|
||||||
} from "../../../../helpers/create-admin-user"
|
} from "../../../../helpers/create-admin-user"
|
||||||
import { getProductFixture } from "../../../../helpers/fixtures"
|
import { getProductFixture } from "../../../../helpers/fixtures"
|
||||||
|
import qs from "qs"
|
||||||
|
|
||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
@@ -639,6 +640,35 @@ medusaIntegrationTestRunner({
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should list all products for a category using $and filters", async () => {
|
||||||
|
const category = await createCategory(
|
||||||
|
{ name: "test", is_internal: false, is_active: true },
|
||||||
|
[product.id]
|
||||||
|
)
|
||||||
|
|
||||||
|
const category2 = await createCategory(
|
||||||
|
{ name: "test2", is_internal: true, is_active: true },
|
||||||
|
[product4.id]
|
||||||
|
)
|
||||||
|
|
||||||
|
const searchParam = qs.stringify({
|
||||||
|
$and: [{ category_id: [category.id, category2.id] }],
|
||||||
|
})
|
||||||
|
|
||||||
|
const response = await api.get(
|
||||||
|
`/store/products?${searchParam}`,
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.count).toEqual(1)
|
||||||
|
expect(response.data.products).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: product.id,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
it("returns a list of ordered products by id ASC", async () => {
|
it("returns a list of ordered products by id ASC", async () => {
|
||||||
const response = await api.get("/store/products?order=id", storeHeaders)
|
const response = await api.get("/store/products?order=id", storeHeaders)
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
|
|||||||
@@ -4,9 +4,18 @@ import {
|
|||||||
createOperatorMap,
|
createOperatorMap,
|
||||||
createSelectParams,
|
createSelectParams,
|
||||||
} from "../../utils/validators"
|
} from "../../utils/validators"
|
||||||
|
import { applyAndAndOrOperators } from "../../utils/common-validators"
|
||||||
|
|
||||||
export const StoreGetCollectionParams = createSelectParams()
|
export const StoreGetCollectionParams = createSelectParams()
|
||||||
|
|
||||||
|
export const StoreGetCollectionsParamsFields = 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(),
|
||||||
|
})
|
||||||
|
|
||||||
export type StoreGetCollectionsParamsType = z.infer<
|
export type StoreGetCollectionsParamsType = z.infer<
|
||||||
typeof StoreGetCollectionsParams
|
typeof StoreGetCollectionsParams
|
||||||
>
|
>
|
||||||
@@ -14,14 +23,6 @@ export const StoreGetCollectionsParams = createFindParams({
|
|||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
order: "-created_at",
|
order: "-created_at",
|
||||||
}).merge(
|
})
|
||||||
z.object({
|
.merge(StoreGetCollectionsParamsFields)
|
||||||
q: z.string().optional(),
|
.merge(applyAndAndOrOperators(StoreGetCollectionsParamsFields))
|
||||||
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(),
|
|
||||||
$and: z.lazy(() => StoreGetCollectionsParams.array()).optional(),
|
|
||||||
$or: z.lazy(() => StoreGetCollectionsParams.array()).optional(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { createFindParams, createSelectParams } from "../../utils/validators"
|
import { createFindParams, createSelectParams } from "../../utils/validators"
|
||||||
|
import { applyAndAndOrOperators } from "../../utils/common-validators"
|
||||||
|
|
||||||
export const StoreGetCurrencyParams = createSelectParams()
|
export const StoreGetCurrencyParams = createSelectParams()
|
||||||
|
|
||||||
|
export const StoreGetCurrenciesParamsFields = z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
code: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
})
|
||||||
|
|
||||||
export type StoreGetCurrenciesParamsType = z.infer<
|
export type StoreGetCurrenciesParamsType = z.infer<
|
||||||
typeof StoreGetCurrenciesParams
|
typeof StoreGetCurrenciesParams
|
||||||
>
|
>
|
||||||
export const StoreGetCurrenciesParams = createFindParams({
|
export const StoreGetCurrenciesParams = createFindParams({
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
}).merge(
|
})
|
||||||
z.object({
|
.merge(StoreGetCurrenciesParamsFields)
|
||||||
q: z.string().optional(),
|
.merge(applyAndAndOrOperators(StoreGetCurrenciesParamsFields))
|
||||||
code: z.union([z.string(), z.array(z.string())]).optional(),
|
|
||||||
$and: z.lazy(() => StoreGetCurrenciesParams.array()).optional(),
|
|
||||||
$or: z.lazy(() => StoreGetCurrenciesParams.array()).optional(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { createFindParams, createSelectParams } from "../../utils/validators"
|
import { createFindParams, createSelectParams } from "../../utils/validators"
|
||||||
|
import { applyAndAndOrOperators } from "../../utils/common-validators"
|
||||||
|
|
||||||
export const StoreGetOrderParams = createSelectParams()
|
export const StoreGetOrderParams = createSelectParams()
|
||||||
export type StoreGetOrderParamsType = z.infer<typeof StoreGetOrderParams>
|
export type StoreGetOrderParamsType = z.infer<typeof StoreGetOrderParams>
|
||||||
|
|
||||||
|
export const StoreGetOrdersParamsFields = z.object({
|
||||||
|
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
status: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
})
|
||||||
|
|
||||||
export const StoreGetOrdersParams = createFindParams({
|
export const StoreGetOrdersParams = createFindParams({
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
}).merge(
|
})
|
||||||
z.object({
|
.merge(StoreGetOrdersParamsFields)
|
||||||
id: z.union([z.string(), z.array(z.string())]).optional(),
|
.merge(applyAndAndOrOperators(StoreGetOrdersParamsFields))
|
||||||
status: z.union([z.string(), z.array(z.string())]).optional(),
|
|
||||||
$and: z.lazy(() => StoreGetOrdersParams.array()).optional(),
|
|
||||||
$or: z.lazy(() => StoreGetOrdersParams.array()).optional(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
export type StoreGetOrdersParamsType = z.infer<typeof StoreGetOrdersParams>
|
export type StoreGetOrdersParamsType = z.infer<typeof StoreGetOrdersParams>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { booleanString } from "../../utils/common-validators"
|
import {
|
||||||
|
applyAndAndOrOperators,
|
||||||
|
booleanString,
|
||||||
|
} from "../../utils/common-validators"
|
||||||
import {
|
import {
|
||||||
createFindParams,
|
createFindParams,
|
||||||
createOperatorMap,
|
createOperatorMap,
|
||||||
@@ -16,26 +19,26 @@ export const StoreProductCategoryParams = createSelectParams().merge(
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const StoreProductCategoriesParamsFields = z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
name: 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: booleanString().optional(),
|
||||||
|
include_descendants_tree: booleanString().optional(),
|
||||||
|
created_at: createOperatorMap().optional(),
|
||||||
|
updated_at: createOperatorMap().optional(),
|
||||||
|
deleted_at: createOperatorMap().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
export type StoreProductCategoriesParamsType = z.infer<
|
export type StoreProductCategoriesParamsType = z.infer<
|
||||||
typeof StoreProductCategoriesParams
|
typeof StoreProductCategoriesParams
|
||||||
>
|
>
|
||||||
export const StoreProductCategoriesParams = createFindParams({
|
export const StoreProductCategoriesParams = createFindParams({
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
}).merge(
|
})
|
||||||
z.object({
|
.merge(StoreProductCategoriesParamsFields)
|
||||||
q: z.string().optional(),
|
.merge(applyAndAndOrOperators(StoreProductCategoriesParamsFields))
|
||||||
id: z.union([z.string(), z.array(z.string())]).optional(),
|
|
||||||
name: 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: booleanString().optional(),
|
|
||||||
include_descendants_tree: booleanString().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(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -16,10 +16,7 @@ import {
|
|||||||
import { validateAndTransformQuery } from "@medusajs/framework"
|
import { validateAndTransformQuery } from "@medusajs/framework"
|
||||||
import { maybeApplyStockLocationId } from "./helpers"
|
import { maybeApplyStockLocationId } from "./helpers"
|
||||||
import * as QueryConfig from "./query-config"
|
import * as QueryConfig from "./query-config"
|
||||||
import {
|
import { StoreGetProductsParams } from "./validators"
|
||||||
StoreGetProductsParams,
|
|
||||||
StoreGetProductsParamsType,
|
|
||||||
} from "./validators"
|
|
||||||
|
|
||||||
export const storeProductRoutesMiddlewares: MiddlewareRoute[] = [
|
export const storeProductRoutesMiddlewares: MiddlewareRoute[] = [
|
||||||
{
|
{
|
||||||
@@ -41,7 +38,8 @@ export const storeProductRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
}),
|
}),
|
||||||
applyDefaultFilters({
|
applyDefaultFilters({
|
||||||
status: ProductStatus.PUBLISHED,
|
status: ProductStatus.PUBLISHED,
|
||||||
categories: (filters: StoreGetProductsParamsType, fields: string[]) => {
|
// TODO: the type here seems off and the implementation does not take into account $and and $or possible filters. Might be worth re working (original type used here was StoreGetProductsParamsType)
|
||||||
|
categories: (filters: any, fields: string[]) => {
|
||||||
const categoryIds = filters.category_id
|
const categoryIds = filters.category_id
|
||||||
delete filters.category_id
|
delete filters.category_id
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import {
|
import {
|
||||||
|
applyAndAndOrOperators,
|
||||||
GetProductsParams,
|
GetProductsParams,
|
||||||
|
recursivelyNormalizeSchema,
|
||||||
|
StoreGetProductParamsDirectFields,
|
||||||
transformProductParams,
|
transformProductParams,
|
||||||
} from "../../utils/common-validators"
|
} from "../../utils/common-validators"
|
||||||
import {
|
import {
|
||||||
@@ -9,64 +12,68 @@ import {
|
|||||||
createSelectParams,
|
createSelectParams,
|
||||||
} from "../../utils/validators"
|
} from "../../utils/validators"
|
||||||
|
|
||||||
|
export const StoreGetProductParamsFields = z.object({
|
||||||
|
region_id: z.string().optional(),
|
||||||
|
country_code: z.string().optional(),
|
||||||
|
province: z.string().optional(),
|
||||||
|
cart_id: z.string().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
export type StoreGetProductParamsType = z.infer<typeof StoreGetProductParams>
|
export type StoreGetProductParamsType = z.infer<typeof StoreGetProductParams>
|
||||||
|
|
||||||
export const StoreGetProductParams = createSelectParams().merge(
|
export const StoreGetProductParams = createSelectParams().merge(
|
||||||
// These are used to populate the tax and pricing context
|
StoreGetProductParamsFields
|
||||||
z.object({
|
|
||||||
region_id: z.string().optional(),
|
|
||||||
country_code: z.string().optional(),
|
|
||||||
province: z.string().optional(),
|
|
||||||
cart_id: z.string().optional(),
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const StoreGetProductVariantsParamsFields = z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
options: z.object({ value: z.string(), option_id: z.string() }).optional(),
|
||||||
|
created_at: createOperatorMap().optional(),
|
||||||
|
updated_at: createOperatorMap().optional(),
|
||||||
|
deleted_at: createOperatorMap().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
export type StoreGetProductVariantsParamsType = z.infer<
|
export type StoreGetProductVariantsParamsType = z.infer<
|
||||||
typeof StoreGetProductVariantsParams
|
typeof StoreGetProductVariantsParams
|
||||||
>
|
>
|
||||||
export const StoreGetProductVariantsParams = createFindParams({
|
export const StoreGetProductVariantsParams = createFindParams({
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
}).merge(
|
})
|
||||||
z.object({
|
.merge(StoreGetProductVariantsParamsFields)
|
||||||
q: z.string().optional(),
|
.merge(applyAndAndOrOperators(StoreGetProductVariantsParamsFields))
|
||||||
id: z.union([z.string(), z.array(z.string())]).optional(),
|
|
||||||
options: z.object({ value: z.string(), option_id: z.string() }).optional(),
|
export const StoreGetProductsParamsFields = z
|
||||||
created_at: createOperatorMap().optional(),
|
.object({
|
||||||
updated_at: createOperatorMap().optional(),
|
region_id: z.string().optional(),
|
||||||
deleted_at: createOperatorMap().optional(),
|
country_code: z.string().optional(),
|
||||||
$and: z.lazy(() => StoreGetProductsParams.array()).optional(),
|
province: z.string().optional(),
|
||||||
$or: z.lazy(() => StoreGetProductsParams.array()).optional(),
|
cart_id: z.string().optional(),
|
||||||
})
|
})
|
||||||
)
|
.merge(GetProductsParams)
|
||||||
|
.strict()
|
||||||
|
|
||||||
export type StoreGetProductsParamsType = z.infer<typeof StoreGetProductsParams>
|
export type StoreGetProductsParamsType = z.infer<typeof StoreGetProductsParams>
|
||||||
export const StoreGetProductsParams = createFindParams({
|
export const StoreGetProductsParams = createFindParams({
|
||||||
offset: 0,
|
offset: 0,
|
||||||
limit: 50,
|
limit: 50,
|
||||||
})
|
})
|
||||||
|
.merge(StoreGetProductsParamsFields)
|
||||||
.merge(
|
.merge(
|
||||||
z
|
z
|
||||||
.object({
|
.object({
|
||||||
// These are used to populate the tax and pricing context
|
|
||||||
region_id: z.string().optional(),
|
|
||||||
country_code: z.string().optional(),
|
|
||||||
province: z.string().optional(),
|
|
||||||
cart_id: z.string().optional(),
|
|
||||||
|
|
||||||
variants: z
|
variants: z
|
||||||
|
|
||||||
.object({
|
.object({
|
||||||
options: z
|
options: z
|
||||||
.object({ value: z.string(), option_id: z.string() })
|
.object({ value: z.string(), option_id: z.string() })
|
||||||
.optional(),
|
.optional(),
|
||||||
$and: z.lazy(() => StoreGetProductsParams.array()).optional(),
|
|
||||||
$or: z.lazy(() => StoreGetProductsParams.array()).optional(),
|
|
||||||
})
|
})
|
||||||
|
.merge(applyAndAndOrOperators(StoreGetProductVariantsParamsFields))
|
||||||
.optional(),
|
.optional(),
|
||||||
$and: z.lazy(() => StoreGetProductsParams.array()).optional(),
|
|
||||||
$or: z.lazy(() => StoreGetProductsParams.array()).optional(),
|
|
||||||
})
|
})
|
||||||
.merge(GetProductsParams)
|
.merge(applyAndAndOrOperators(StoreGetProductParamsDirectFields))
|
||||||
.strict()
|
.strict()
|
||||||
)
|
)
|
||||||
.transform(transformProductParams)
|
.transform(recursivelyNormalizeSchema(transformProductParams))
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { createFindParams, createSelectParams } from "../../utils/validators"
|
import { createFindParams, createSelectParams } from "../../utils/validators"
|
||||||
|
import { applyAndAndOrOperators } from "../../utils/common-validators"
|
||||||
|
|
||||||
export type StoreGetRegionParamsType = z.infer<typeof StoreGetRegionParams>
|
export type StoreGetRegionParamsType = z.infer<typeof StoreGetRegionParams>
|
||||||
export const StoreGetRegionParams = createSelectParams()
|
export const StoreGetRegionParams = createSelectParams()
|
||||||
|
|
||||||
|
export const StoreGetRegionsParamsFields = z.object({
|
||||||
|
q: z.string().optional(),
|
||||||
|
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
currency_code: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
name: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
})
|
||||||
|
|
||||||
export type StoreGetRegionsParamsType = z.infer<typeof StoreGetRegionsParams>
|
export type StoreGetRegionsParamsType = z.infer<typeof StoreGetRegionsParams>
|
||||||
export const StoreGetRegionsParams = createFindParams({
|
export const StoreGetRegionsParams = createFindParams({
|
||||||
limit: 50,
|
limit: 50,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
}).merge(
|
})
|
||||||
z.object({
|
.merge(StoreGetRegionsParamsFields)
|
||||||
q: z.string().optional(),
|
.merge(applyAndAndOrOperators(StoreGetRegionsParamsFields))
|
||||||
id: z.union([z.string(), z.array(z.string())]).optional(),
|
|
||||||
currency_code: z.union([z.string(), z.array(z.string())]).optional(),
|
|
||||||
name: z.union([z.string(), z.array(z.string())]).optional(),
|
|
||||||
$and: z.lazy(() => StoreGetRegionsParams.array()).optional(),
|
|
||||||
$or: z.lazy(() => StoreGetRegionsParams.array()).optional(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { createFindParams, createSelectParams } from "../../utils/validators"
|
import { createFindParams, createSelectParams } from "../../utils/validators"
|
||||||
|
import { applyAndAndOrOperators } from "../../utils/common-validators"
|
||||||
|
|
||||||
export type ReturnParamsType = z.infer<typeof ReturnParams>
|
export type ReturnParamsType = z.infer<typeof ReturnParams>
|
||||||
export const ReturnParams = createSelectParams()
|
export const ReturnParams = createSelectParams()
|
||||||
|
|
||||||
|
export const ReturnsParamsFields = z.object({
|
||||||
|
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
order_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
})
|
||||||
|
|
||||||
export type ReturnsParamsType = z.infer<typeof ReturnsParams>
|
export type ReturnsParamsType = z.infer<typeof ReturnsParams>
|
||||||
export const ReturnsParams = createFindParams().merge(
|
export const ReturnsParams = createFindParams()
|
||||||
z.object({
|
.merge(ReturnsParamsFields)
|
||||||
id: z.union([z.string(), z.array(z.string())]).optional(),
|
.merge(applyAndAndOrOperators(ReturnsParamsFields))
|
||||||
order_id: z.union([z.string(), z.array(z.string())]).optional(),
|
|
||||||
$and: z.lazy(() => ReturnsParams.array()).optional(),
|
|
||||||
$or: z.lazy(() => ReturnsParams.array()).optional(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const ReturnShippingSchema = z.object({
|
const ReturnShippingSchema = z.object({
|
||||||
option_id: z.string(),
|
option_id: z.string(),
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { createFindParams } from "../../utils/validators"
|
import { createFindParams } from "../../utils/validators"
|
||||||
|
import { applyAndAndOrOperators } from "../../utils/common-validators"
|
||||||
|
|
||||||
|
export const StoreGetShippingOptionsFields = z.object({
|
||||||
|
cart_id: z.string(),
|
||||||
|
is_return: z.boolean().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
export type StoreGetShippingOptionsType = z.infer<
|
export type StoreGetShippingOptionsType = z.infer<
|
||||||
typeof StoreGetShippingOptions
|
typeof StoreGetShippingOptions
|
||||||
@@ -7,11 +13,6 @@ export type StoreGetShippingOptionsType = z.infer<
|
|||||||
export const StoreGetShippingOptions = createFindParams({
|
export const StoreGetShippingOptions = createFindParams({
|
||||||
limit: 20,
|
limit: 20,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
}).merge(
|
})
|
||||||
z.object({
|
.merge(StoreGetShippingOptionsFields)
|
||||||
cart_id: z.string(),
|
.merge(applyAndAndOrOperators(StoreGetShippingOptionsFields))
|
||||||
is_return: z.boolean().optional(),
|
|
||||||
$and: z.lazy(() => StoreGetShippingOptions.array()).optional(),
|
|
||||||
$or: z.lazy(() => StoreGetShippingOptions.array()).optional(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -25,6 +25,21 @@ export const BigNumberInput = z.union([
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a zod object to apply the $and and $or operators on a schema.
|
||||||
|
*
|
||||||
|
* @param {ZodObject<any>} schema
|
||||||
|
* @return {ZodObject<any>}
|
||||||
|
*/
|
||||||
|
export const applyAndAndOrOperators = (schema: z.ZodObject<any>) => {
|
||||||
|
return schema.merge(
|
||||||
|
z.object({
|
||||||
|
$and: z.lazy(() => schema.array()).optional(),
|
||||||
|
$or: z.lazy(() => schema.array()).optional(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates that a value is a boolean when it is passed as a string.
|
* Validates that a value is a boolean when it is passed as a string.
|
||||||
*/
|
*/
|
||||||
@@ -37,3 +52,26 @@ export const booleanString = () =>
|
|||||||
.transform((value) => {
|
.transform((value) => {
|
||||||
return value.toString().toLowerCase() === "true"
|
return value.toString().toLowerCase() === "true"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a transformer on a schema when the data are validated and recursively normalize the data $and and $or.
|
||||||
|
*
|
||||||
|
* @param {(data: Data) => NormalizedData} transform
|
||||||
|
* @return {(data: Data) => NormalizedData}
|
||||||
|
*/
|
||||||
|
export function recursivelyNormalizeSchema<
|
||||||
|
Data extends object,
|
||||||
|
NormalizedData extends object
|
||||||
|
>(transform: (data: Data) => NormalizedData): (data: Data) => NormalizedData {
|
||||||
|
return (data: any) => {
|
||||||
|
const normalizedData = transform(data)
|
||||||
|
|
||||||
|
Object.keys(normalizedData)
|
||||||
|
.filter((key) => ["$and", "$or"].includes(key))
|
||||||
|
.forEach((key) => {
|
||||||
|
normalizedData[key] = normalizedData[key].map(transform)
|
||||||
|
})
|
||||||
|
|
||||||
|
return normalizedData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ import { booleanString } from "../common"
|
|||||||
|
|
||||||
export const ProductStatusEnum = z.nativeEnum(ProductStatus)
|
export const ProductStatusEnum = z.nativeEnum(ProductStatus)
|
||||||
|
|
||||||
export const GetProductsParams = z.object({
|
export const StoreGetProductParamsDirectFields = z.object({
|
||||||
q: z.string().optional(),
|
q: z.string().optional(),
|
||||||
id: z.union([z.string(), z.array(z.string())]).optional(),
|
id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
handle: z.string().optional(),
|
handle: z.string().optional(),
|
||||||
is_giftcard: booleanString().optional(),
|
is_giftcard: booleanString().optional(),
|
||||||
category_id: z.union([z.string(), z.array(z.string())]).optional(),
|
category_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
sales_channel_id: z.union([z.string(), z.array(z.string())]).optional(),
|
|
||||||
collection_id: z.union([z.string(), z.array(z.string())]).optional(),
|
collection_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
tag_id: z.union([z.string(), z.array(z.string())]).optional(),
|
tag_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
type_id: z.union([z.string(), z.array(z.string())]).optional(),
|
type_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
@@ -22,6 +21,12 @@ export const GetProductsParams = z.object({
|
|||||||
deleted_at: createOperatorMap().optional(),
|
deleted_at: createOperatorMap().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const GetProductsParams = z
|
||||||
|
.object({
|
||||||
|
sales_channel_id: z.union([z.string(), z.array(z.string())]).optional(),
|
||||||
|
})
|
||||||
|
.merge(StoreGetProductParamsDirectFields)
|
||||||
|
|
||||||
type HttpProductFilters = FilterableProductProps & {
|
type HttpProductFilters = FilterableProductProps & {
|
||||||
tag_id?: string | string[]
|
tag_id?: string | string[]
|
||||||
category_id?: string | string[]
|
category_id?: string | string[]
|
||||||
@@ -32,8 +37,8 @@ export const transformProductParams = (
|
|||||||
): FilterableProductProps => {
|
): FilterableProductProps => {
|
||||||
const res = {
|
const res = {
|
||||||
...data,
|
...data,
|
||||||
tags: normalizeArray(data, "tag_id"),
|
tags: { id: data.tag_id },
|
||||||
categories: normalizeArray(data, "category_id"),
|
categories: { id: data.category_id },
|
||||||
}
|
}
|
||||||
|
|
||||||
delete res.tag_id
|
delete res.tag_id
|
||||||
@@ -41,19 +46,3 @@ export const transformProductParams = (
|
|||||||
|
|
||||||
return res as FilterableProductProps
|
return res as FilterableProductProps
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizeArray = (filters: HttpProductFilters, key: string) => {
|
|
||||||
if (filters[key]) {
|
|
||||||
if (Array.isArray(filters[key])) {
|
|
||||||
return {
|
|
||||||
id: { $in: filters[key] },
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
id: filters[key] as string,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user