feature: introduce additional_data to the product endpoints (#8405)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { AdditionalData, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
@@ -9,8 +9,7 @@ import { createCollectionsStep } from "../steps"
|
||||
|
||||
type WorkflowInput = {
|
||||
collections: ProductTypes.CreateProductCollectionDTO[]
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
export const createCollectionsWorkflowId = "create-collections"
|
||||
export const createCollectionsWorkflow = createWorkflow(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { AdditionalData, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
@@ -9,8 +9,7 @@ import { createProductOptionsStep } from "../steps"
|
||||
|
||||
type WorkflowInput = {
|
||||
product_options: ProductTypes.CreateProductOptionDTO[]
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
export const createProductOptionsWorkflowId = "create-product-options"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { AdditionalData, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
@@ -9,8 +9,7 @@ import { createProductTagsStep } from "../steps"
|
||||
|
||||
type WorkflowInput = {
|
||||
product_tags: ProductTypes.CreateProductTagDTO[]
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
export const createProductTagsWorkflowId = "create-product-tags"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { AdditionalData, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
@@ -9,8 +9,7 @@ import { createProductTypesStep } from "../steps"
|
||||
|
||||
type WorkflowInput = {
|
||||
product_types: ProductTypes.CreateProductTypeDTO[]
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
export const createProductTypesWorkflowId = "create-product-types"
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { LinkDefinition } from "@medusajs/modules-sdk"
|
||||
import { InventoryTypes, PricingTypes, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
AdditionalData,
|
||||
InventoryTypes,
|
||||
PricingTypes,
|
||||
ProductTypes,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
@@ -25,8 +30,7 @@ type WorkflowInput = {
|
||||
required_quantity?: number
|
||||
}[]
|
||||
})[]
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
const buildLink = (
|
||||
variant_id: string,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
AdditionalData,
|
||||
CreateProductWorkflowInputDTO,
|
||||
PricingTypes,
|
||||
ProductTypes,
|
||||
@@ -17,8 +18,7 @@ import { createProductVariantsWorkflow } from "./create-product-variants"
|
||||
|
||||
type WorkflowInput = {
|
||||
products: CreateProductWorkflowInputDTO[]
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
export const createProductsWorkflowId = "create-products"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { AdditionalData, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
@@ -10,8 +10,7 @@ import { updateCollectionsStep } from "../steps"
|
||||
type UpdateCollectionsStepInput = {
|
||||
selector: ProductTypes.FilterableProductCollectionProps
|
||||
update: ProductTypes.UpdateProductCollectionDTO
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
type WorkflowInput = UpdateCollectionsStepInput
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { AdditionalData, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
@@ -10,8 +10,7 @@ import { updateProductOptionsStep } from "../steps"
|
||||
type UpdateProductOptionsStepInput = {
|
||||
selector: ProductTypes.FilterableProductOptionProps
|
||||
update: ProductTypes.UpdateProductOptionDTO
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
type WorkflowInput = UpdateProductOptionsStepInput
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { AdditionalData, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
@@ -10,8 +10,7 @@ import { updateProductTagsStep } from "../steps"
|
||||
type UpdateProductTagsStepInput = {
|
||||
selector: ProductTypes.FilterableProductTypeProps
|
||||
update: ProductTypes.UpdateProductTypeDTO
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
type WorkflowInput = UpdateProductTagsStepInput
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { AdditionalData, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
@@ -10,8 +10,7 @@ import { updateProductTypesStep } from "../steps"
|
||||
type UpdateProductTypesStepInput = {
|
||||
selector: ProductTypes.FilterableProductTypeProps
|
||||
update: ProductTypes.UpdateProductTypeDTO
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
type WorkflowInput = UpdateProductTypesStepInput
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PricingTypes, ProductTypes } from "@medusajs/types"
|
||||
import { AdditionalData, PricingTypes, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
@@ -23,9 +23,7 @@ type UpdateProductVariantsStepInput =
|
||||
})[]
|
||||
}
|
||||
|
||||
type WorkflowInput = UpdateProductVariantsStepInput & {
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
type WorkflowInput = UpdateProductVariantsStepInput & AdditionalData
|
||||
|
||||
export const updateProductVariantsWorkflowId = "update-product-variants"
|
||||
export const updateProductVariantsWorkflow = createWorkflow(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { updateProductsStep } from "../steps/update-products"
|
||||
|
||||
import {
|
||||
AdditionalData,
|
||||
CreateMoneyAmountDTO,
|
||||
ProductTypes,
|
||||
UpdateProductVariantWorkflowInputDTO,
|
||||
@@ -26,16 +27,14 @@ type UpdateProductsStepInputSelector = {
|
||||
sales_channels?: { id: string }[]
|
||||
variants?: UpdateProductVariantWorkflowInputDTO[]
|
||||
}
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
type UpdateProductsStepInputProducts = {
|
||||
products: (Omit<ProductTypes.UpsertProductDTO, "variants"> & {
|
||||
sales_channels?: { id: string }[]
|
||||
variants?: UpdateProductVariantWorkflowInputDTO[]
|
||||
})[]
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
} & AdditionalData
|
||||
|
||||
type UpdateProductsStepInput =
|
||||
| UpdateProductsStepInputSelector
|
||||
|
||||
7
packages/core/types/src/http/common/additional_data.ts
Normal file
7
packages/core/types/src/http/common/additional_data.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Represents the additional_data property accepted in HTTP
|
||||
* requests to allow arbitrary values
|
||||
*/
|
||||
export type AdditionalData = {
|
||||
additional_data?: Record<string, unknown>
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./request"
|
||||
export * from "./response"
|
||||
export * from "./additional_data"
|
||||
|
||||
@@ -147,14 +147,12 @@ export interface MedusaRequest<Body = unknown>
|
||||
* A generic context object that can be used across the request lifecycle
|
||||
*/
|
||||
context?: Record<string, any>
|
||||
|
||||
/**
|
||||
* Custom validators for the request body and query params that will be
|
||||
* merged with the original validator of the route.
|
||||
* Custom validator to validate the `additional_data` property in
|
||||
* requests that allows for additional_data
|
||||
*/
|
||||
extendedValidators?: {
|
||||
body?: ZodObject<any, any>
|
||||
queryParams?: ZodObject<any, any>
|
||||
}
|
||||
additionalDataValidator?: ZodObject<any, any>
|
||||
}
|
||||
|
||||
export interface AuthContext {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
MiddlewareVerb,
|
||||
ParserConfig,
|
||||
} from "../types"
|
||||
import { ZodObject } from "zod"
|
||||
import zod, { ZodRawShape } from "zod"
|
||||
|
||||
/**
|
||||
* A helper function to configure the routes by defining custom middleware,
|
||||
@@ -19,10 +19,7 @@ export function defineMiddlewares<
|
||||
method?: MiddlewareVerb | MiddlewareVerb[]
|
||||
matcher: string | RegExp
|
||||
bodyParser?: ParserConfig
|
||||
extendedValidators?: {
|
||||
body?: ZodObject<any, any>
|
||||
queryParams?: ZodObject<any, any>
|
||||
}
|
||||
additionalDataValidator?: ZodRawShape
|
||||
// eslint-disable-next-line space-before-function-paren
|
||||
middlewares?: (<Req extends MedusaRequest>(
|
||||
req: Req,
|
||||
@@ -41,17 +38,16 @@ export function defineMiddlewares<
|
||||
return {
|
||||
errorHandler,
|
||||
routes: routes.map((route) => {
|
||||
const { middlewares, extendedValidators, ...rest } = route
|
||||
const { middlewares, additionalDataValidator, ...rest } = route
|
||||
const customMiddleware: MedusaRequestHandler[] = []
|
||||
|
||||
/**
|
||||
* Define a custom validator when "extendedValidators.body" or
|
||||
* "extendedValidators.queryParams" validation schema is
|
||||
* provided.
|
||||
* Define a custom validator when a zod schema is provided via
|
||||
* "additionalDataValidator" property
|
||||
*/
|
||||
if (extendedValidators?.body || extendedValidators?.queryParams) {
|
||||
if (additionalDataValidator) {
|
||||
customMiddleware.push((req, _, next) => {
|
||||
req.extendedValidators = extendedValidators
|
||||
req.additionalDataValidator = zod.object(additionalDataValidator)
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "@medusajs/core-flows"
|
||||
|
||||
import { remapKeysForProduct, remapProductResponse } from "../../../helpers"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { AdditionalData, HttpTypes } from "@medusajs/types"
|
||||
import { refetchEntity } from "../../../../../utils/refetch-entity"
|
||||
|
||||
export const GET = async (
|
||||
@@ -28,16 +28,20 @@ export const GET = async (
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<HttpTypes.AdminUpdateProductOption>,
|
||||
req: AuthenticatedMedusaRequest<
|
||||
HttpTypes.AdminUpdateProductOption & AdditionalData
|
||||
>,
|
||||
res: MedusaResponse<HttpTypes.AdminProductResponse>
|
||||
) => {
|
||||
const productId = req.params.id
|
||||
const optionId = req.params.option_id
|
||||
const { additional_data, ...update } = req.validatedBody
|
||||
|
||||
await updateProductOptionsWorkflow(req.scope).run({
|
||||
input: {
|
||||
selector: { id: optionId, product_id: productId },
|
||||
update: req.validatedBody,
|
||||
update,
|
||||
additional_data,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
|
||||
import { createProductOptionsWorkflow } from "@medusajs/core-flows"
|
||||
import { remapKeysForProduct, remapProductResponse } from "../../helpers"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { AdditionalData, HttpTypes } from "@medusajs/types"
|
||||
import {
|
||||
refetchEntities,
|
||||
refetchEntity,
|
||||
@@ -33,18 +33,23 @@ export const GET = async (
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<HttpTypes.AdminCreateProductOption>,
|
||||
req: AuthenticatedMedusaRequest<
|
||||
HttpTypes.AdminCreateProductOption & AdditionalData
|
||||
>,
|
||||
res: MedusaResponse<HttpTypes.AdminProductResponse>
|
||||
) => {
|
||||
const productId = req.params.id
|
||||
const { additional_data, ...rest } = req.validatedBody
|
||||
|
||||
await createProductOptionsWorkflow(req.scope).run({
|
||||
input: {
|
||||
product_options: [
|
||||
{
|
||||
...req.validatedBody,
|
||||
...rest,
|
||||
product_id: productId,
|
||||
},
|
||||
],
|
||||
additional_data,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "../../../../types/routing"
|
||||
import { remapKeysForProduct, remapProductResponse } from "../helpers"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { AdditionalData, HttpTypes } from "@medusajs/types"
|
||||
import { refetchEntity } from "../../../utils/refetch-entity"
|
||||
|
||||
export const GET = async (
|
||||
@@ -31,13 +31,18 @@ export const GET = async (
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<HttpTypes.AdminUpdateProduct>,
|
||||
req: AuthenticatedMedusaRequest<
|
||||
HttpTypes.AdminUpdateProduct & AdditionalData
|
||||
>,
|
||||
res: MedusaResponse<HttpTypes.AdminProductResponse>
|
||||
) => {
|
||||
const { additional_data, ...update } = req.validatedBody
|
||||
|
||||
const { result } = await updateProductsWorkflow(req.scope).run({
|
||||
input: {
|
||||
selector: { id: req.params.id },
|
||||
update: req.validatedBody,
|
||||
update,
|
||||
additional_data,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
MedusaResponse,
|
||||
} from "../../../../../../types/routing"
|
||||
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { AdditionalData, HttpTypes } from "@medusajs/types"
|
||||
import { refetchEntity } from "../../../../../utils/refetch-entity"
|
||||
import {
|
||||
remapKeysForProduct,
|
||||
@@ -35,16 +35,20 @@ export const GET = async (
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<HttpTypes.AdminUpdateProductVariant>,
|
||||
req: AuthenticatedMedusaRequest<
|
||||
HttpTypes.AdminUpdateProductVariant & AdditionalData
|
||||
>,
|
||||
res: MedusaResponse<HttpTypes.AdminProductResponse>
|
||||
) => {
|
||||
const productId = req.params.id
|
||||
const variantId = req.params.variant_id
|
||||
const { additional_data, ...update } = req.validatedBody
|
||||
|
||||
await updateProductVariantsWorkflow(req.scope).run({
|
||||
input: {
|
||||
selector: { id: variantId, product_id: productId },
|
||||
update: req.validatedBody,
|
||||
update: update,
|
||||
additional_data,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createProductVariantsWorkflow } from "@medusajs/core-flows"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { AdditionalData, HttpTypes } from "@medusajs/types"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
@@ -53,19 +53,23 @@ export const GET = async (
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<HttpTypes.AdminCreateProductVariant>,
|
||||
req: AuthenticatedMedusaRequest<
|
||||
HttpTypes.AdminCreateProductVariant & AdditionalData
|
||||
>,
|
||||
res: MedusaResponse<HttpTypes.AdminProductResponse>
|
||||
) => {
|
||||
const productId = req.params.id
|
||||
const { additional_data, ...rest } = req.validatedBody
|
||||
|
||||
const input = [
|
||||
{
|
||||
...req.validatedBody,
|
||||
...rest,
|
||||
product_id: productId,
|
||||
},
|
||||
]
|
||||
|
||||
await createProductVariantsWorkflow(req.scope).run({
|
||||
input: { product_variants: input },
|
||||
input: { product_variants: input, additional_data },
|
||||
})
|
||||
|
||||
const product = await refetchEntity(
|
||||
|
||||
@@ -27,6 +27,8 @@ import {
|
||||
AdminUpdateProductOption,
|
||||
AdminUpdateProductVariant,
|
||||
AdminUpdateVariantInventoryItem,
|
||||
CreateProduct,
|
||||
CreateProductVariant,
|
||||
} from "./validators"
|
||||
import multer from "multer"
|
||||
|
||||
@@ -68,7 +70,7 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
matcher: "/admin/products/batch",
|
||||
middlewares: [
|
||||
validateAndTransformBody(
|
||||
createBatchBody(AdminCreateProduct, AdminBatchUpdateProduct)
|
||||
createBatchBody(CreateProduct, AdminBatchUpdateProduct)
|
||||
),
|
||||
validateAndTransformQuery(
|
||||
AdminGetProductParams,
|
||||
@@ -166,10 +168,7 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
matcher: "/admin/products/:id/variants/batch",
|
||||
middlewares: [
|
||||
validateAndTransformBody(
|
||||
createBatchBody(
|
||||
AdminCreateProductVariant,
|
||||
AdminBatchUpdateProductVariant
|
||||
)
|
||||
createBatchBody(CreateProductVariant, AdminBatchUpdateProductVariant)
|
||||
),
|
||||
validateAndTransformQuery(
|
||||
AdminGetProductVariantParams,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createProductsWorkflow } from "@medusajs/core-flows"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { AdditionalData, HttpTypes } from "@medusajs/types"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
@@ -30,11 +30,15 @@ export const GET = async (
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest<HttpTypes.AdminCreateProduct>,
|
||||
req: AuthenticatedMedusaRequest<
|
||||
HttpTypes.AdminCreateProduct & AdditionalData
|
||||
>,
|
||||
res: MedusaResponse<HttpTypes.AdminProductResponse>
|
||||
) => {
|
||||
const { additional_data, ...products } = req.validatedBody
|
||||
|
||||
const { result } = await createProductsWorkflow(req.scope).run({
|
||||
input: { products: [req.validatedBody] },
|
||||
input: { products: [products], additional_data },
|
||||
})
|
||||
|
||||
const product = await refetchEntity(
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { BatchMethodRequest } from "@medusajs/types"
|
||||
import { ProductStatus } from "@medusajs/utils"
|
||||
import { z } from "zod"
|
||||
import {
|
||||
GetProductsParams,
|
||||
transformProductParams,
|
||||
} from "../../utils/common-validators"
|
||||
import { z, ZodObject } from "zod"
|
||||
import {
|
||||
createFindParams,
|
||||
createOperatorMap,
|
||||
@@ -85,23 +85,46 @@ export const AdminUpdateProductTag = z.object({
|
||||
value: z.string().optional(),
|
||||
})
|
||||
|
||||
export type AdminCreateProductOptionType = z.infer<
|
||||
typeof AdminCreateProductOption
|
||||
>
|
||||
export const AdminCreateProductOption = z.object({
|
||||
export type AdminCreateProductOptionType = z.infer<typeof CreateProductOption>
|
||||
export const CreateProductOption = z.object({
|
||||
title: z.string(),
|
||||
values: z.array(z.string()),
|
||||
})
|
||||
export const AdminCreateProductOption = (
|
||||
additionalDataValidator?: ZodObject<any, any>
|
||||
) => {
|
||||
if (!additionalDataValidator) {
|
||||
return CreateProductOption.extend({
|
||||
additional_data: z.record(z.unknown()).nullish(),
|
||||
})
|
||||
}
|
||||
|
||||
export type AdminUpdateProductOptionType = z.infer<
|
||||
typeof AdminUpdateProductOption
|
||||
>
|
||||
export const AdminUpdateProductOption = z.object({
|
||||
return CreateProductOption.extend({
|
||||
additional_data: additionalDataValidator,
|
||||
})
|
||||
}
|
||||
|
||||
export type AdminUpdateProductOptionType = z.infer<typeof UpdateProductOption>
|
||||
export const UpdateProductOption = z.object({
|
||||
id: z.string().optional(),
|
||||
title: z.string().optional(),
|
||||
values: z.array(z.string()).optional(),
|
||||
})
|
||||
|
||||
export const AdminUpdateProductOption = (
|
||||
additionalDataValidator?: ZodObject<any, any>
|
||||
) => {
|
||||
if (!additionalDataValidator) {
|
||||
return UpdateProductOption.extend({
|
||||
additional_data: z.record(z.unknown()).nullish(),
|
||||
})
|
||||
}
|
||||
|
||||
return UpdateProductOption.extend({
|
||||
additional_data: additionalDataValidator,
|
||||
})
|
||||
}
|
||||
|
||||
export type AdminCreateVariantPriceType = z.infer<
|
||||
typeof AdminCreateVariantPrice
|
||||
>
|
||||
@@ -130,10 +153,8 @@ export const AdminCreateProductType = z.object({
|
||||
value: z.string(),
|
||||
})
|
||||
|
||||
export type AdminCreateProductVariantType = z.infer<
|
||||
typeof AdminCreateProductVariant
|
||||
>
|
||||
export const AdminCreateProductVariant = z
|
||||
export type AdminCreateProductVariantType = z.infer<typeof CreateProductVariant>
|
||||
export const CreateProductVariant = z
|
||||
.object({
|
||||
title: z.string(),
|
||||
sku: z.string().nullish(),
|
||||
@@ -164,11 +185,22 @@ export const AdminCreateProductVariant = z
|
||||
.optional(),
|
||||
})
|
||||
.strict()
|
||||
export const AdminCreateProductVariant = (
|
||||
additionalDataValidator?: ZodObject<any, any>
|
||||
) => {
|
||||
if (!additionalDataValidator) {
|
||||
return CreateProductVariant.extend({
|
||||
additional_data: z.record(z.string()).optional(),
|
||||
})
|
||||
}
|
||||
|
||||
export type AdminUpdateProductVariantType = z.infer<
|
||||
typeof AdminUpdateProductVariant
|
||||
>
|
||||
export const AdminUpdateProductVariant = z
|
||||
return CreateProductVariant.extend({
|
||||
additional_data: additionalDataValidator,
|
||||
})
|
||||
}
|
||||
|
||||
export type AdminUpdateProductVariantType = z.infer<typeof UpdateProductVariant>
|
||||
export const UpdateProductVariant = z
|
||||
.object({
|
||||
id: z.string().optional(),
|
||||
title: z.string().optional(),
|
||||
@@ -193,10 +225,24 @@ export const AdminUpdateProductVariant = z
|
||||
})
|
||||
.strict()
|
||||
|
||||
export const AdminUpdateProductVariant = (
|
||||
additionalDataValidator?: ZodObject<any, any>
|
||||
) => {
|
||||
if (!additionalDataValidator) {
|
||||
return UpdateProductVariant.extend({
|
||||
additional_data: z.record(z.string()).optional(),
|
||||
})
|
||||
}
|
||||
|
||||
return UpdateProductVariant.extend({
|
||||
additional_data: additionalDataValidator,
|
||||
})
|
||||
}
|
||||
|
||||
export type AdminBatchUpdateProductVariantType = z.infer<
|
||||
typeof AdminBatchUpdateProductVariant
|
||||
>
|
||||
export const AdminBatchUpdateProductVariant = AdminUpdateProductVariant.extend({
|
||||
export const AdminBatchUpdateProductVariant = UpdateProductVariant.extend({
|
||||
id: z.string(),
|
||||
})
|
||||
|
||||
@@ -204,8 +250,8 @@ export const IdAssociation = z.object({
|
||||
id: z.string(),
|
||||
})
|
||||
|
||||
export type AdminCreateProductType = z.infer<typeof AdminCreateProduct>
|
||||
export const AdminCreateProduct = z
|
||||
export type AdminCreateProductType = z.infer<typeof CreateProduct>
|
||||
export const CreateProduct = z
|
||||
.object({
|
||||
title: z.string(),
|
||||
subtitle: z.string().nullish(),
|
||||
@@ -220,8 +266,8 @@ export const AdminCreateProduct = z
|
||||
collection_id: z.string().nullish(),
|
||||
categories: z.array(IdAssociation).optional(),
|
||||
tags: z.array(IdAssociation).optional(),
|
||||
options: z.array(AdminCreateProductOption).optional(),
|
||||
variants: z.array(AdminCreateProductVariant).optional(),
|
||||
options: z.array(CreateProductOption).optional(),
|
||||
variants: z.array(CreateProductVariant).optional(),
|
||||
sales_channels: z.array(z.object({ id: z.string() })).optional(),
|
||||
weight: z.number().nullish(),
|
||||
length: z.number().nullish(),
|
||||
@@ -235,14 +281,28 @@ export const AdminCreateProduct = z
|
||||
})
|
||||
.strict()
|
||||
|
||||
export type AdminUpdateProductType = z.infer<typeof AdminUpdateProduct>
|
||||
export const AdminUpdateProduct = z
|
||||
export const AdminCreateProduct = (
|
||||
additionalDataValidator?: ZodObject<any, any>
|
||||
) => {
|
||||
if (!additionalDataValidator) {
|
||||
return CreateProduct.extend({
|
||||
additional_data: z.record(z.unknown()).nullish(),
|
||||
})
|
||||
}
|
||||
|
||||
return CreateProduct.extend({
|
||||
additional_data: additionalDataValidator,
|
||||
})
|
||||
}
|
||||
|
||||
export type AdminUpdateProductType = z.infer<typeof UpdateProduct>
|
||||
export const UpdateProduct = z
|
||||
.object({
|
||||
title: z.string().optional(),
|
||||
discountable: z.boolean().optional(),
|
||||
is_giftcard: z.boolean().optional(),
|
||||
options: z.array(AdminUpdateProductOption).optional(),
|
||||
variants: z.array(AdminUpdateProductVariant).optional(),
|
||||
options: z.array(UpdateProductOption).optional(),
|
||||
variants: z.array(UpdateProductVariant).optional(),
|
||||
status: statusEnum.optional(),
|
||||
subtitle: z.string().nullish(),
|
||||
description: z.string().nullish(),
|
||||
@@ -266,10 +326,24 @@ export const AdminUpdateProduct = z
|
||||
})
|
||||
.strict()
|
||||
|
||||
export const AdminUpdateProduct = (
|
||||
additionalDataValidator?: ZodObject<any, any>
|
||||
) => {
|
||||
if (!additionalDataValidator) {
|
||||
return UpdateProduct.extend({
|
||||
additional_data: z.record(z.unknown()).nullish(),
|
||||
})
|
||||
}
|
||||
|
||||
return UpdateProduct.extend({
|
||||
additional_data: additionalDataValidator,
|
||||
})
|
||||
}
|
||||
|
||||
export type AdminBatchUpdateProductType = z.infer<
|
||||
typeof AdminBatchUpdateProduct
|
||||
>
|
||||
export const AdminBatchUpdateProduct = AdminUpdateProduct.extend({
|
||||
export const AdminBatchUpdateProduct = UpdateProduct.extend({
|
||||
id: z.string(),
|
||||
})
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ describe("validateAndTransformBody", () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("should merge a custom validators schema", async () => {
|
||||
it("should pass additionalDataValidator to validator factory", async () => {
|
||||
let mockRequest = {
|
||||
query: {},
|
||||
body: {},
|
||||
@@ -18,42 +18,16 @@ describe("validateAndTransformBody", () => {
|
||||
const mockResponse = {} as MedusaResponse
|
||||
const nextFunction = jest.fn()
|
||||
|
||||
mockRequest.extendedValidators = {
|
||||
body: zod.object({
|
||||
brand_id: zod.number(),
|
||||
}),
|
||||
}
|
||||
|
||||
let middleware = validateAndTransformBody(createLinkBody())
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
expect(nextFunction).toHaveBeenCalledWith(
|
||||
new MedusaError(
|
||||
"invalid_data",
|
||||
`Invalid request: Field 'brand_id' is required`
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
it("should pass schema to merge to the original validator factory", async () => {
|
||||
let mockRequest = {
|
||||
query: {},
|
||||
body: {},
|
||||
} as MedusaRequest
|
||||
|
||||
const mockResponse = {} as MedusaResponse
|
||||
const nextFunction = jest.fn()
|
||||
|
||||
mockRequest.extendedValidators = {
|
||||
body: zod.object({
|
||||
brand_id: zod.number(),
|
||||
}),
|
||||
}
|
||||
mockRequest.additionalDataValidator = zod.object({
|
||||
brand_id: zod.number(),
|
||||
})
|
||||
|
||||
const validatorFactory = (schema?: Zod.ZodObject<any, any>) => {
|
||||
return schema ? createLinkBody().merge(schema) : createLinkBody()
|
||||
}
|
||||
|
||||
let middleware = validateAndTransformBody(validatorFactory)
|
||||
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
expect(nextFunction).toHaveBeenCalledWith(
|
||||
new MedusaError(
|
||||
|
||||
@@ -695,56 +695,4 @@ describe("validateAndTransformQuery", () => {
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
it("should merge a custom validators schema", async () => {
|
||||
let mockRequest = {
|
||||
query: {},
|
||||
} as MedusaRequest
|
||||
|
||||
const mockResponse = {} as MedusaResponse
|
||||
const nextFunction = jest.fn()
|
||||
|
||||
mockRequest.extendedValidators = {
|
||||
queryParams: zod.object({
|
||||
page: zod.number(),
|
||||
}),
|
||||
}
|
||||
|
||||
let middleware = validateAndTransformQuery(createFindParams(), {})
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
expect(nextFunction).toHaveBeenCalledWith(
|
||||
new MedusaError(
|
||||
"invalid_data",
|
||||
`Invalid request: Field 'page' is required`
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
it("should pass schema to merge to the original validator factory", async () => {
|
||||
let mockRequest = {
|
||||
query: {},
|
||||
} as MedusaRequest
|
||||
|
||||
const mockResponse = {} as MedusaResponse
|
||||
const nextFunction = jest.fn()
|
||||
|
||||
mockRequest.extendedValidators = {
|
||||
queryParams: zod.object({
|
||||
page: zod.number(),
|
||||
}),
|
||||
}
|
||||
|
||||
const validatorFactory = (schema?: Zod.ZodObject<any, any>) => {
|
||||
return schema ? createFindParams().merge(schema) : createFindParams()
|
||||
}
|
||||
|
||||
let middleware = validateAndTransformQuery(validatorFactory, {})
|
||||
await middleware(mockRequest, mockResponse, nextFunction)
|
||||
expect(nextFunction).toHaveBeenCalledWith(
|
||||
new MedusaError(
|
||||
"invalid_data",
|
||||
`Invalid request: Field 'page' is required`
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,12 +17,8 @@ export function validateAndTransformBody(
|
||||
return async (req: MedusaRequest, _: MedusaResponse, next: NextFunction) => {
|
||||
try {
|
||||
let schema: z.ZodObject<any, any> | z.ZodEffects<any, any>
|
||||
const { body: bodyValidatorToMerge } = req.extendedValidators ?? {}
|
||||
|
||||
if (typeof zodSchema === "function") {
|
||||
schema = zodSchema(bodyValidatorToMerge)
|
||||
} else if (bodyValidatorToMerge) {
|
||||
schema = zodSchema.merge(bodyValidatorToMerge)
|
||||
schema = zodSchema(req.additionalDataValidator)
|
||||
} else {
|
||||
schema = zodSchema
|
||||
}
|
||||
|
||||
@@ -58,11 +58,7 @@ const getFilterableFields = <T extends RequestQueryFields>(obj: T): T => {
|
||||
}
|
||||
|
||||
export function validateAndTransformQuery<TEntity extends BaseEntity>(
|
||||
zodSchema:
|
||||
| z.ZodObject<any, any>
|
||||
| ((
|
||||
customSchema?: z.ZodObject<any, any>
|
||||
) => z.ZodObject<any, any> | z.ZodEffects<any, any>),
|
||||
zodSchema: z.ZodObject<any, any> | z.ZodEffects<any, any>,
|
||||
queryConfig: QueryConfig<TEntity>
|
||||
): (
|
||||
req: MedusaRequest,
|
||||
@@ -75,18 +71,7 @@ export function validateAndTransformQuery<TEntity extends BaseEntity>(
|
||||
delete req.allowed
|
||||
const query = normalizeQuery(req)
|
||||
|
||||
let schema: z.ZodObject<any, any> | z.ZodEffects<any, any>
|
||||
const { queryParams: queryParamsToMerge } = req.extendedValidators ?? {}
|
||||
|
||||
if (typeof zodSchema === "function") {
|
||||
schema = zodSchema(queryParamsToMerge)
|
||||
} else if (queryParamsToMerge) {
|
||||
schema = zodSchema.merge(queryParamsToMerge)
|
||||
} else {
|
||||
schema = zodSchema
|
||||
}
|
||||
|
||||
const validated = await zodValidator(schema, query)
|
||||
const validated = await zodValidator(zodSchema, query)
|
||||
const cnf = queryConfig.isList
|
||||
? prepareListQuery(validated, { ...queryConfig, allowed })
|
||||
: prepareRetrieveQuery(validated, { ...queryConfig, allowed })
|
||||
|
||||
@@ -63,14 +63,12 @@ export interface MedusaRequest<Body = unknown>
|
||||
* A generic context object that can be used across the request lifecycle
|
||||
*/
|
||||
context?: Record<string, any>
|
||||
|
||||
/**
|
||||
* Custom validators for the request body and query params that will be
|
||||
* merged with the original validator of the route.
|
||||
* Custom validator to validate the `additional_data` property in
|
||||
* requests that allows for additional_data
|
||||
*/
|
||||
extendedValidators?: {
|
||||
body?: ZodObject<any, any>
|
||||
queryParams?: ZodObject<any, any>
|
||||
}
|
||||
additionalDataValidator?: ZodObject<any, any>
|
||||
}
|
||||
|
||||
export interface AuthContext {
|
||||
|
||||
@@ -21,22 +21,20 @@ describe("defineMiddlewares", function () {
|
||||
})
|
||||
})
|
||||
|
||||
test("should wrap body extendedValidator to middleware", () => {
|
||||
test("should wrap additionalDataValidator to middleware", () => {
|
||||
const req = {
|
||||
body: {},
|
||||
} as MedusaRequest
|
||||
const res = {} as MedusaResponse
|
||||
const nextFn = jest.fn()
|
||||
const schema = zod.object({
|
||||
const schema = {
|
||||
brand_id: zod.string(),
|
||||
})
|
||||
}
|
||||
|
||||
const config = defineMiddlewares([
|
||||
{
|
||||
matcher: "/admin/products",
|
||||
extendedValidators: {
|
||||
body: schema,
|
||||
},
|
||||
additionalDataValidator: schema,
|
||||
},
|
||||
])
|
||||
|
||||
@@ -50,42 +48,10 @@ describe("defineMiddlewares", function () {
|
||||
})
|
||||
|
||||
config.routes?.[0].middlewares?.[0](req, res, nextFn)
|
||||
expect(req.extendedValidators).toMatchObject({
|
||||
body: schema,
|
||||
})
|
||||
})
|
||||
|
||||
test("should wrap queryParams extendedValidator to middleware", () => {
|
||||
const req = {
|
||||
body: {},
|
||||
} as MedusaRequest
|
||||
const res = {} as MedusaResponse
|
||||
const nextFn = jest.fn()
|
||||
const schema = zod.object({
|
||||
brand_id: zod.string(),
|
||||
})
|
||||
|
||||
const config = defineMiddlewares([
|
||||
expect(req.additionalDataValidator!.parse({ brand_id: "1" })).toMatchObject(
|
||||
{
|
||||
matcher: "/admin/products",
|
||||
extendedValidators: {
|
||||
queryParams: schema,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
expect(config).toMatchObject({
|
||||
routes: [
|
||||
{
|
||||
matcher: "/admin/products",
|
||||
middlewares: [expect.any(Function)],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
config.routes?.[0].middlewares?.[0](req, res, nextFn)
|
||||
expect(req.extendedValidators).toMatchObject({
|
||||
queryParams: schema,
|
||||
})
|
||||
brand_id: "1",
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user