diff --git a/packages/medusa/src/loaders/helpers/routing/types.ts b/packages/medusa/src/loaders/helpers/routing/types.ts index 09871b788f..ff2e308d49 100644 --- a/packages/medusa/src/loaders/helpers/routing/types.ts +++ b/packages/medusa/src/loaders/helpers/routing/types.ts @@ -60,7 +60,7 @@ export type ParserConfigArgs = { preserveRawBody?: boolean } -type ParserConfig = false | ParserConfigArgs +export type ParserConfig = false | ParserConfigArgs export type MiddlewareRoute = { method?: MiddlewareVerb | MiddlewareVerb[] diff --git a/packages/medusa/src/utils/__tests__/define-routes-config.spec.ts b/packages/medusa/src/utils/__tests__/define-routes-config.spec.ts new file mode 100644 index 0000000000..66776609b5 --- /dev/null +++ b/packages/medusa/src/utils/__tests__/define-routes-config.spec.ts @@ -0,0 +1,91 @@ +import zod from "zod" +import { defineRoutesConfig } from "../define-routes-config" +import { MedusaRequest, MedusaResponse } from "../../types/routing" + +describe("defineRoutesConfig", function () { + test("define custom middleware for a route", () => { + const config = defineRoutesConfig([ + { + matcher: "/admin/products", + middlewares: [() => {}], + }, + ]) + + expect(config).toMatchObject({ + routes: [ + { + matcher: "/admin/products", + middlewares: [expect.any(Function)], + }, + ], + }) + }) + + test("should wrap body 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 = defineRoutesConfig([ + { + matcher: "/admin/products", + extendedValidators: { + body: schema, + }, + }, + ]) + + expect(config).toMatchObject({ + routes: [ + { + matcher: "/admin/products", + middlewares: [expect.any(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 = defineRoutesConfig([ + { + 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, + }) + }) +}) diff --git a/packages/medusa/src/utils/define-routes-config.ts b/packages/medusa/src/utils/define-routes-config.ts new file mode 100644 index 0000000000..835c82402c --- /dev/null +++ b/packages/medusa/src/utils/define-routes-config.ts @@ -0,0 +1,59 @@ +import { ZodObject } from "zod" +import { + MedusaRequest, + MedusaResponse, + MedusaNextFunction, + MedusaRequestHandler, +} from "../types/routing" +import { + ParserConfig, + MiddlewareVerb, + MiddlewaresConfig, +} from "../loaders/helpers/routing/types" + +/** + * A helper function to configure the routes by defining custom middleware, + * bodyparser config and validators to be merged with the pre-existing + * route validators. + */ +export function defineRoutesConfig< + Route extends { + method?: MiddlewareVerb | MiddlewareVerb[] + matcher: string | RegExp + bodyParser?: ParserConfig + extendedValidators?: { + body?: ZodObject + queryParams?: ZodObject + } + // eslint-disable-next-line space-before-function-paren + middlewares?: (( + req: Req, + res: MedusaResponse, + next: MedusaNextFunction + ) => any)[] + } +>(routes: Route[]): MiddlewaresConfig { + return { + routes: routes.map((route) => { + const { middlewares, extendedValidators, ...rest } = route + const customMiddleware: MedusaRequestHandler[] = [] + + /** + * Define a custom validator when "extendedValidators.body" or + * "extendedValidators.queryParams" validation schema is + * provided. + */ + if (extendedValidators?.body || extendedValidators?.queryParams) { + customMiddleware.push((req, _, next) => { + req.extendedValidators = extendedValidators + next() + }) + } + + return { + ...rest, + middlewares: customMiddleware.concat(middlewares || []), + } + }), + } +} diff --git a/packages/medusa/src/utils/index.ts b/packages/medusa/src/utils/index.ts index 13545c3094..271d85e5d3 100644 --- a/packages/medusa/src/utils/index.ts +++ b/packages/medusa/src/utils/index.ts @@ -2,4 +2,5 @@ export * from "./clean-response-data" export * from "./exception-formatter" export * from "./middlewares" export * from "./omit-deep" +export * from "./define-routes-config" export * from "./remove-undefined-properties"