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