express-openapi-validator
express-openapi-validator copied to clipboard
Error in Custom Operation Resolver
Describe the bug In the example given for custom operation resolver, we can read:
new OpenApiValidator({
apiSpec,
operationHandlers: {
basePath: path.join(__dirname, 'routes'),
resolver: (basePath, route) => {
// Pluck controller and function names from operationId
const [controllerName, functionName] = route.schema['operationId'].split('.')
// Get path to module and attempt to require it
const modulePath = path.join(basePath, controllerName);
const handler = require(modulePath)
// Simplistic error checking to make sure the function actually exists
// on the handler module
if (handler[functionName] === undefined) {
throw new Error(
`Could not find a [${functionName}] function in ${modulePath} when trying to route [${route.method} ${route.expressRoute}].`
)
}
// Finally return our function
return handler[functionName]
}
});
So route is supposed to have a schema
property.
We can only access:
{
basePath: "..."
expressRoute: "..."
method: "GET"
openApiRoute: "..."
pathParams: []
}
Instead resolver has 3 arguments : basePath
(which is the baseDirectory, not route.basePath), route
AND apiDoc
!
The example should mention apiDoc
instead of route.schema
.
Yeah, I came across this issue as well and it should be noted that apiDoc
is the full schema, not just the schema for this route. It wasn't exactly trivial to get the schema for the current route but I was able to get it. Below is the resolver I put together for async
ESM modules. Hopefully this can help someone encountering the same issue!
import path from "path"
const esmresolver = basePath => {
return {
basePath,
resolver: (basePath, route, apiDoc) => {
const pathKey = route.openApiRoute.substring(route.basePath.length)
const schema = apiDoc.paths[pathKey][route.method.toLowerCase()]
// x-eov-operation-id takes priority over operationId
const fn = schema["x-eov-operation-id"] || schema["operationId"]
// x-eov-operation-handler with fallback to routes.js
const handler = schema["x-eov-operation-handler"] || "routes"
const handlerFile = `${handler}.js`
const modP = import(path.join(basePath, handlerFile))
return async (req, res, next) => {
try {
const mod = await modP
mod[fn](req, res)
} catch (err) {
console.error(err)
next(new Error(`Routing error ${handlerFile}:${fn}`))
}
}
}
}
}
export default esmresolver
This can be used like this:
app.use(
OpenApiValidator.middleware({
apiSpec,
validateResponses: true,
operationHandlers: esmresolver(modulePath)
})
)