@altenorjr/routing-controllers-openapi
Version:
Runtime OpenAPI v3 spec generation for routing-controllers
252 lines • 22 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.expressToOpenAPIPath = exports.getTags = exports.getSummary = exports.getSpec = exports.getResponses = exports.getStatusCode = exports.getContentType = exports.getRequestBody = exports.getQueryParams = exports.getPathParams = exports.getHeaderParams = exports.getPaths = exports.getOperationId = exports.getOperation = exports.getFullPath = exports.getFullExpressPath = void 0;
const tslib_1 = require("tslib");
const lodash_merge_1 = tslib_1.__importDefault(require("lodash.merge"));
const lodash_capitalize_1 = tslib_1.__importDefault(require("lodash.capitalize"));
const lodash_startcase_1 = tslib_1.__importDefault(require("lodash.startcase"));
const pathToRegexp = tslib_1.__importStar(require("path-to-regexp"));
require("reflect-metadata");
const decorators_1 = require("./decorators");
function getFullExpressPath(route) {
const { action, controller, options } = route;
return ((options.routePrefix || '') +
(controller.route || '') +
(action.route || ''));
}
exports.getFullExpressPath = getFullExpressPath;
function getFullPath(route) {
return expressToOpenAPIPath(getFullExpressPath(route));
}
exports.getFullPath = getFullPath;
function getOperation(route, schemas) {
const operation = {
operationId: getOperationId(route),
parameters: [
...getHeaderParams(route),
...getPathParams(route),
...getQueryParams(route, schemas),
],
requestBody: getRequestBody(route) || undefined,
responses: getResponses(route),
summary: getSummary(route),
tags: getTags(route),
};
const cleanedOperation = Object.entries(operation)
.filter(([_, value]) => value && (value.length || Object.keys(value).length))
.reduce((acc, [key, value]) => {
acc[key] = value;
return acc;
}, {});
return (0, decorators_1.applyOpenAPIDecorator)(cleanedOperation, route);
}
exports.getOperation = getOperation;
function getOperationId(route) {
return `${route.action.target.name}.${route.action.method}`;
}
exports.getOperationId = getOperationId;
function getPaths(routes, schemas) {
const routePaths = routes
.filter(route => !!route.controller)
.map((route) => ({
[getFullPath(route)]: {
[route.action.type]: getOperation(route, schemas),
},
}));
return (0, lodash_merge_1.default)(...routePaths);
}
exports.getPaths = getPaths;
function getHeaderParams(route) {
const headers = route.params
.filter((p) => p.type === 'header')
.map((headerMeta) => {
const schema = getParamSchema(headerMeta);
return {
in: 'header',
name: headerMeta.name || '',
required: isRequired(headerMeta, route),
schema,
};
});
const headersMeta = route.params.find((p) => p.type === 'headers');
if (headersMeta) {
const schema = getParamSchema(headersMeta);
headers.push({
in: 'header',
name: schema.$ref.split('/').pop() || '',
required: isRequired(headersMeta, route),
schema,
});
}
return headers;
}
exports.getHeaderParams = getHeaderParams;
function getPathParams(route) {
const path = getFullExpressPath(route);
const tokens = pathToRegexp.parse(path);
return tokens
.filter((token) => token && typeof token === 'object')
.map((token) => {
const name = token.name + '';
const param = {
in: 'path',
name,
required: !token.optional,
schema: { type: 'string' },
};
if (token.pattern && token.pattern !== '[^\\/]+?') {
param.schema = { pattern: token.pattern, type: 'string' };
}
const meta = route.params.find((p) => p.name === name && p.type === 'param');
if (meta) {
const metaSchema = getParamSchema(meta);
param.schema =
'type' in metaSchema ? Object.assign(Object.assign({}, param.schema), metaSchema) : metaSchema;
}
return param;
});
}
exports.getPathParams = getPathParams;
function getQueryParams(route, schemas) {
var _a;
const queries = route.params
.filter((p) => p.type === 'query')
.map((queryMeta) => {
const schema = getParamSchema(queryMeta);
return {
in: 'query',
name: queryMeta.name || '',
required: isRequired(queryMeta, route),
schema,
};
});
const queriesMeta = route.params.find((p) => p.type === 'queries');
if (queriesMeta) {
const { $ref: paramSchemaRef = '' } = getParamSchema(queriesMeta);
const paramSchemaName = paramSchemaRef.split('/').pop() || '';
const currentSchema = schemas[paramSchemaName];
for (const [name, schema] of Object.entries((currentSchema === null || currentSchema === void 0 ? void 0 : currentSchema.properties) || {})) {
queries.push({
in: 'query',
name,
required: (_a = currentSchema.required) === null || _a === void 0 ? void 0 : _a.includes(name),
schema,
});
}
}
return queries;
}
exports.getQueryParams = getQueryParams;
function getRequestBody(route) {
const bodyParamMetas = route.params.filter((d) => d.type === 'body-param');
const bodyParamsSchema = bodyParamMetas.length > 0
? bodyParamMetas.reduce((acc, d) => (Object.assign(Object.assign({}, acc), { properties: Object.assign(Object.assign({}, acc.properties), { [d.name]: getParamSchema(d) }), required: isRequired(d, route)
? [...(acc.required || []), d.name]
: acc.required })), { properties: {}, required: [], type: 'object' })
: null;
const bodyMeta = route.params.find((d) => d.type === 'body');
if (bodyMeta) {
const bodySchema = getParamSchema(bodyMeta);
const { $ref } = 'items' in bodySchema && bodySchema.items ? bodySchema.items : bodySchema;
return {
content: {
'application/json': {
schema: bodyParamsSchema
? { allOf: [bodySchema, bodyParamsSchema] }
: bodySchema,
},
},
description: ($ref || '').split('/').pop(),
required: isRequired(bodyMeta, route),
};
}
else if (bodyParamsSchema) {
return {
content: { 'application/json': { schema: bodyParamsSchema } },
};
}
}
exports.getRequestBody = getRequestBody;
function getContentType(route) {
const defaultContentType = route.controller.type === 'json'
? 'application/json'
: 'text/html; charset=utf-8';
const contentMeta = route.responseHandlers.find((h) => h.type === 'content-type');
return contentMeta ? contentMeta.value : defaultContentType;
}
exports.getContentType = getContentType;
function getStatusCode(route) {
const successMeta = route.responseHandlers.find((h) => h.type === 'success-code');
return successMeta ? successMeta.value + '' : '200';
}
exports.getStatusCode = getStatusCode;
function getResponses(route) {
const contentType = getContentType(route);
const successStatus = getStatusCode(route);
return {
[successStatus]: {
content: { [contentType]: {} },
description: 'Successful response',
},
};
}
exports.getResponses = getResponses;
function getSpec(routes, schemas) {
return {
components: { schemas: {} },
info: { title: '', version: '1.0.0' },
openapi: '3.0.0',
paths: getPaths(routes, schemas),
};
}
exports.getSpec = getSpec;
function getSummary(route) {
return (0, lodash_capitalize_1.default)((0, lodash_startcase_1.default)(route.action.method));
}
exports.getSummary = getSummary;
function getTags(route) {
return [(0, lodash_startcase_1.default)(route.controller.target.name.replace(/Controller$/, ''))];
}
exports.getTags = getTags;
function expressToOpenAPIPath(expressPath) {
const tokens = pathToRegexp.parse(expressPath);
return tokens
.map((d) => (typeof d === 'string' ? d : `${d.prefix}{${d.name}}`))
.join('');
}
exports.expressToOpenAPIPath = expressToOpenAPIPath;
function isRequired(meta, route) {
var _a, _b, _c;
const globalRequired = (_c = (_b = (_a = route.options) === null || _a === void 0 ? void 0 : _a.defaults) === null || _b === void 0 ? void 0 : _b.paramOptions) === null || _c === void 0 ? void 0 : _c.required;
return globalRequired ? meta.required !== false : !!meta.required;
}
function getParamSchema(param) {
const { explicitType, index, object, method } = param;
const type = Reflect.getMetadata('design:paramtypes', object, method)[index];
if (typeof type === 'function' && type.name === 'Array') {
const items = explicitType
? { $ref: '#/components/schemas/' + explicitType.name }
: { type: 'object' };
return { items, type: 'array' };
}
if (explicitType) {
return { $ref: '#/components/schemas/' + explicitType.name };
}
if (typeof type === 'function') {
if (type.prototype === String.prototype ||
type.prototype === Symbol.prototype) {
return { type: 'string' };
}
else if (type.prototype === Number.prototype) {
return { type: 'number' };
}
else if (type.prototype === Boolean.prototype) {
return { type: 'boolean' };
}
else if (type.name !== 'Object') {
return { $ref: '#/components/schemas/' + type.name };
}
}
return {};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VuZXJhdGVTcGVjLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2dlbmVyYXRlU3BlYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQ0Esd0VBQWlDO0FBQ2pDLGtGQUEyQztBQUMzQyxnRkFBeUM7QUFFekMscUVBQThDO0FBQzlDLDRCQUF5QjtBQUd6Qiw2Q0FBb0Q7QUFJcEQsU0FBZ0Isa0JBQWtCLENBQUMsS0FBYTtJQUM5QyxNQUFNLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxPQUFPLEVBQUUsR0FBRyxLQUFLLENBQUE7SUFDN0MsT0FBTyxDQUNMLENBQUMsT0FBTyxDQUFDLFdBQVcsSUFBSSxFQUFFLENBQUM7UUFDM0IsQ0FBQyxVQUFVLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUN4QixDQUFDLE1BQU0sQ0FBQyxLQUFLLElBQUksRUFBRSxDQUFDLENBQ3JCLENBQUE7QUFDSCxDQUFDO0FBUEQsZ0RBT0M7QUFLRCxTQUFnQixXQUFXLENBQUMsS0FBYTtJQUN2QyxPQUFPLG9CQUFvQixDQUFDLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUE7QUFDeEQsQ0FBQztBQUZELGtDQUVDO0FBS0QsU0FBZ0IsWUFBWSxDQUMxQixLQUFhLEVBQ2IsT0FBeUM7SUFFekMsTUFBTSxTQUFTLEdBQXVCO1FBQ3BDLFdBQVcsRUFBRSxjQUFjLENBQUMsS0FBSyxDQUFDO1FBQ2xDLFVBQVUsRUFBRTtZQUNWLEdBQUcsZUFBZSxDQUFDLEtBQUssQ0FBQztZQUN6QixHQUFHLGFBQWEsQ0FBQyxLQUFLLENBQUM7WUFDdkIsR0FBRyxjQUFjLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQztTQUNsQztRQUNELFdBQVcsRUFBRSxjQUFjLENBQUMsS0FBSyxDQUFDLElBQUksU0FBUztRQUMvQyxTQUFTLEVBQUUsWUFBWSxDQUFDLEtBQUssQ0FBQztRQUM5QixPQUFPLEVBQUUsVUFBVSxDQUFDLEtBQUssQ0FBQztRQUMxQixJQUFJ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
;