diff --git a/src/framework/types.ts b/src/framework/types.ts index 2df2ea07..07666c94 100644 --- a/src/framework/types.ts +++ b/src/framework/types.ts @@ -77,9 +77,9 @@ export type ValidateSecurityOpts = { }; export type OperationHandlerOptions = { - basePath: string; + basePath: string | URL; resolver: ( - handlersPath: string, + handlersPath: string | URL, route: RouteMetadata, apiDoc: OpenAPIV3.Document, ) => unknown; @@ -169,7 +169,7 @@ export interface OpenApiValidatorOpts { $refParser?: { mode: 'bundle' | 'dereference'; }; - operationHandlers?: false | string | OperationHandlerOptions; + operationHandlers?: false | string | URL | OperationHandlerOptions; validateFormats?: boolean | 'fast' | 'full'; } diff --git a/src/middlewares/openapi.security.ts b/src/middlewares/openapi.security.ts index d9fc4f83..8663e6e8 100644 --- a/src/middlewares/openapi.security.ts +++ b/src/middlewares/openapi.security.ts @@ -232,6 +232,7 @@ class AuthValidator { const authHeader = req.headers['authorization'] && req.headers['authorization'].toLowerCase(); + // req.cookies will be `undefined` without `cookie-parser` middleware const authCookie = req.cookies?.[scheme.name] || req.signedCookies?.[scheme.name]; @@ -241,7 +242,7 @@ class AuthValidator { if (authHeader && !authHeader.includes('bearer')) { throw Error(`Authorization header with scheme 'Bearer' required`); } - + if (!authHeader && !authCookie) { if (scheme.in === 'cookie') { throw Error(`Cookie authentication required`); diff --git a/src/openapi.validator.ts b/src/openapi.validator.ts index 3265c187..3d130a35 100644 --- a/src/openapi.validator.ts +++ b/src/openapi.validator.ts @@ -56,7 +56,7 @@ export class OpenApiValidator { if (options.formats == null) options.formats = {}; if (options.useRequestUrl == null) options.useRequestUrl = false; - if (typeof options.operationHandlers === 'string') { + if (typeof options.operationHandlers === 'string' || options.operationHandlers instanceof URL) { /** * Internally, we want to convert this to a value typed OperationHandlerOptions. * In this way, we can treat the value as such when we go to install (rather than diff --git a/src/resolvers.ts b/src/resolvers.ts index 3092e3a3..405f1d6c 100644 --- a/src/resolvers.ts +++ b/src/resolvers.ts @@ -2,13 +2,17 @@ import * as path from 'path'; import { RequestHandler } from 'express'; import { RouteMetadata } from './framework/openapi.spec.loader'; import { OpenAPIV3 } from './framework/types'; +import { fileURLToPath, pathToFileURL } from 'url'; + +// Prevent TypeScript from replacing dynamic import with require() +const dynamicImport = new Function('specifier', 'return import(specifier)'); const cache = {}; -export function defaultResolver( - handlersPath: string, +export async function defaultResolver( + handlersPath: string | URL, route: RouteMetadata, apiDoc: OpenAPIV3.Document, -): RequestHandler { +): Promise { const tmpModules = {}; const { basePath, expressRoute, openApiRoute, method } = route; const pathKey = openApiRoute.substring(basePath.length); @@ -29,14 +33,17 @@ export function defaultResolver( `found x-eov-operation-handler for route [${method} - ${expressRoute}]. operationId or x-eov-operation-id required.`, ); } - if (oId && baseName && typeof handlersPath === 'string') { - const modulePath = path.join(handlersPath, baseName); - if (!tmpModules[modulePath]) { - tmpModules[modulePath] = require(modulePath); - } - - const handler = tmpModules[modulePath][oId] || tmpModules[modulePath].default[oId] || tmpModules[modulePath].default; + const hasHandlerPath = typeof handlersPath === 'string' || handlersPath instanceof URL; + if (oId && baseName && hasHandlerPath) { + const modulePath = typeof handlersPath === 'string' + ? path.join(handlersPath, baseName) + : path.join(fileURLToPath(handlersPath), baseName); + const importedModule = typeof handlersPath === 'string' + ? require(modulePath) + : await dynamicImport(pathToFileURL(modulePath).toString()); + const handler = importedModule[oId] || importedModule.default?.[oId] || importedModule.default; + if (!handler) { throw Error( `Could not find 'x-eov-operation-handler' with id ${oId} in module '${modulePath}'. Make sure operation '${oId}' defined in your API spec exists as a handler function (or module has a default export) in '${modulePath}'.`,