-
-
Notifications
You must be signed in to change notification settings - Fork 208
/
openapi.metadata.ts
120 lines (114 loc) · 3.82 KB
/
openapi.metadata.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { zipObject } from './util';
import { pathToRegexp } from 'path-to-regexp';
import { Response, NextFunction } from 'express';
import { OpenApiContext } from '../framework/openapi.context';
import {
BadRequest,
MethodNotAllowed,
NotFound,
OpenApiRequest,
OpenApiRequestHandler,
OpenApiRequestMetadata,
OpenAPIV3,
} from '../framework/types';
import { httpMethods } from './parsers/schema.preprocessor';
export function applyOpenApiMetadata(
openApiContext: OpenApiContext,
responseApiDoc: OpenAPIV3.Document,
): OpenApiRequestHandler {
return (req: OpenApiRequest, res: Response, next: NextFunction): void => {
// note base path is empty when path is fully qualified i.e. req.path.startsWith('')
const path = req.path.startsWith(req.baseUrl)
? req.path
: `${req.baseUrl}/${req.path}`;
if (openApiContext.shouldIgnoreRoute(path)) {
return next();
}
const matched = lookupRoute(req, openApiContext.useRequestUrl);
if (matched) {
const { expressRoute, openApiRoute, pathParams, schema } = matched;
if (!schema) {
// Prevents validation for routes which match on path but mismatch on method
if (openApiContext.ignoreUndocumented) {
return next();
}
throw new MethodNotAllowed({
path: req.path,
message: `${req.method} method not allowed`,
headers: {
Allow: Object.keys(openApiContext.openApiRouteMap[openApiRoute])
.filter((key) => httpMethods.has(key.toLowerCase()))
.join(', '),
},
});
}
req.openapi = {
expressRoute: expressRoute,
openApiRoute: openApiRoute,
pathParams: pathParams,
schema: schema,
serial: openApiContext.serial,
};
req.params = pathParams;
if (responseApiDoc) {
// add the response schema if validating responses
(<any>req.openapi)._responseSchema = (<any>matched)._responseSchema;
}
} else if (
openApiContext.isManagedRoute(path) &&
!openApiContext.ignoreUndocumented
) {
throw new NotFound({
path: req.path,
message: 'not found',
});
}
next();
};
function lookupRoute(
req: OpenApiRequest,
useRequestUrl: boolean,
): OpenApiRequestMetadata {
const path = useRequestUrl ? req.url.split('?')[0] : req.originalUrl.split('?')[0];
const method = req.method;
const routeEntries = Object.entries(openApiContext.expressRouteMap);
for (const [expressRoute, methods] of routeEntries) {
const routePair = openApiContext.routePair(expressRoute);
const openApiRoute = routePair.openApiRoute;
const pathKey = openApiRoute.substring((<any>methods).basePath.length);
const schema = openApiContext.apiDoc.paths[pathKey][method.toLowerCase()];
const _schema = responseApiDoc?.paths[pathKey][method.toLowerCase()];
const keys = [];
const strict = !!req.app.enabled('strict routing');
const sensitive = !!req.app.enabled('case sensitive routing');
const pathOpts = {
sensitive,
strict,
};
const regexp = pathToRegexp(expressRoute, keys, pathOpts);
const matchedRoute = regexp.exec(path);
if (matchedRoute) {
const paramKeys = keys.map((k) => k.name);
try {
const paramsVals = matchedRoute.slice(1).map(decodeURIComponent);
const pathParams = zipObject(paramKeys, paramsVals);
const r = {
schema,
expressRoute,
openApiRoute,
pathParams,
serial: -1,
};
(<any>r)._responseSchema = _schema;
return r;
} catch (error) {
throw new BadRequest({
path: req.path,
message: `malformed uri'`,
});
}
}
}
return null;
}
}