diff --git a/packages/core/framework/src/http/__tests__/validate-body.spec.ts b/packages/core/framework/src/http/__tests__/validate-body.spec.ts index 417ee75945b4d..cb4418cfd6c44 100644 --- a/packages/core/framework/src/http/__tests__/validate-body.spec.ts +++ b/packages/core/framework/src/http/__tests__/validate-body.spec.ts @@ -18,28 +18,102 @@ describe("validateAndTransformBody", () => { it("should pass additionalDataValidator to validator factory", async () => { let mockRequest = { query: {}, - body: {}, + body: { + additional_data: {}, + }, } as MedusaRequest const mockResponse = {} as MedusaResponse const nextFunction = jest.fn() - mockRequest.additionalDataValidator = zod.object({ - brand_id: zod.number(), - }) + mockRequest.additionalDataValidator = zod + .object({ + brand_id: zod.number(), + }) + .nullish() - const validatorFactory = (schema?: Zod.ZodObject) => { - return schema ? createLinkBody().merge(schema) : createLinkBody() + const validatorFactory = ( + schema?: Zod.ZodOptional>> + ) => { + return schema + ? createLinkBody().extend({ + additional_data: schema, + }) + : createLinkBody() } let middleware = validateAndTransformBody(validatorFactory) await middleware(mockRequest, mockResponse, nextFunction) - expect(nextFunction).toHaveBeenCalledWith( + expect(nextFunction.mock.calls[0]).toEqual([ new MedusaError( "invalid_data", - `Invalid request: Field 'brand_id' is required` - ) - ) + `Invalid request: Field 'additional_data, brand_id' is required` + ), + ]) + }) + + it("should allow additional_data to be undefined", async () => { + let mockRequest = { + query: {}, + body: {}, + } as MedusaRequest + + const mockResponse = {} as MedusaResponse + const nextFunction = jest.fn() + + mockRequest.additionalDataValidator = zod + .object({ + brand_id: zod.number(), + }) + .nullish() + + const validatorFactory = ( + schema?: Zod.ZodOptional>> + ) => { + return schema + ? createLinkBody().extend({ + additional_data: schema, + }) + : createLinkBody() + } + + let middleware = validateAndTransformBody(validatorFactory) + + await middleware(mockRequest, mockResponse, nextFunction) + expect(nextFunction.mock.calls[0]).toEqual([]) + }) + + it("should allow additional_data nested properties to be undefined", async () => { + let mockRequest = { + query: {}, + body: { + additional_data: {}, + }, + } as MedusaRequest + + const mockResponse = {} as MedusaResponse + const nextFunction = jest.fn() + + mockRequest.additionalDataValidator = zod + .object({ + brand_id: zod.number().optional(), + }) + .nullish() + + const validatorFactory = ( + schema?: Zod.ZodOptional>> + ) => { + return schema + ? createLinkBody().extend({ + additional_data: schema, + }) + : createLinkBody() + } + + let middleware = validateAndTransformBody(validatorFactory) + + await middleware(mockRequest, mockResponse, nextFunction) + expect(nextFunction.mock.calls[0]).toEqual([]) }) }) diff --git a/packages/core/framework/src/http/types.ts b/packages/core/framework/src/http/types.ts index e1f19434c3f37..197f9a928c196 100644 --- a/packages/core/framework/src/http/types.ts +++ b/packages/core/framework/src/http/types.ts @@ -1,5 +1,5 @@ import type { NextFunction, Request, Response } from "express" -import { ZodObject } from "zod" +import { ZodNullable, ZodObject, ZodOptional } from "zod" import { FindConfig, @@ -148,7 +148,7 @@ export interface MedusaRequest * Custom validator to validate the `additional_data` property in * requests that allows for additional_data */ - additionalDataValidator?: ZodObject + additionalDataValidator?: ZodOptional>> } export interface AuthContext { diff --git a/packages/core/framework/src/http/utils/define-middlewares.ts b/packages/core/framework/src/http/utils/define-middlewares.ts index 5c080863b667b..eba76d36b82b3 100644 --- a/packages/core/framework/src/http/utils/define-middlewares.ts +++ b/packages/core/framework/src/http/utils/define-middlewares.ts @@ -47,7 +47,9 @@ export function defineMiddlewares< */ if (additionalDataValidator) { customMiddleware.push((req, _, next) => { - req.additionalDataValidator = zod.object(additionalDataValidator) + req.additionalDataValidator = zod + .object(additionalDataValidator) + .nullish() next() }) } diff --git a/packages/core/framework/src/http/utils/validate-body.ts b/packages/core/framework/src/http/utils/validate-body.ts index 31f64ed1a4e96..18096bf716469 100644 --- a/packages/core/framework/src/http/utils/validate-body.ts +++ b/packages/core/framework/src/http/utils/validate-body.ts @@ -7,7 +7,7 @@ export function validateAndTransformBody( zodSchema: | z.ZodObject | (( - customSchema?: z.ZodObject + customSchema?: z.ZodOptional>> ) => z.ZodObject | z.ZodEffects) ): ( req: MedusaRequest, diff --git a/packages/medusa/src/api/utils/validators.ts b/packages/medusa/src/api/utils/validators.ts index c3aff0abb00b3..fdccc57c3fb68 100644 --- a/packages/medusa/src/api/utils/validators.ts +++ b/packages/medusa/src/api/utils/validators.ts @@ -1,4 +1,4 @@ -import { z, ZodEffects, ZodObject } from "zod" +import { z, ZodEffects, ZodNullable, ZodObject, ZodOptional } from "zod" /** * Wraps the original schema to a function to accept and merge @@ -8,7 +8,9 @@ export const WithAdditionalData = >( originalSchema: T, modifyCallback?: (schema: T) => ZodObject | ZodEffects ) => { - return (additionalDataValidator?: ZodObject) => { + return ( + additionalDataValidator?: ZodOptional>> + ) => { let schema: ZodObject if (!additionalDataValidator) {