openapi-tsk
Version:
openapi tool to use with NodeTskeleton template project
209 lines (208 loc) • 8.98 kB
JavaScript
;
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _ApiDocGenerator_apiRootPath;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApiDocGenerator = void 0;
const SecuritySchemesStore_1 = require("../resources/SecuritySchemesStore");
const httpStatusDescriber_1 = __importDefault(require("../resources/httpStatusDescriber"));
const types_1 = require("../resources/types");
const SchemasStore_1 = require("../resources/SchemasStore");
const StringUtil_1 = require("../resources/StringUtil");
const path_1 = require("path");
const fs_1 = require("fs");
class ApiDocGenerator {
constructor(env, info) {
this.env = env;
_ApiDocGenerator_apiRootPath.set(this, "/api");
this.apiDoc = {
openapi: "3.0.3",
info: {
title: StringUtil_1.StringUtil.EMPTY,
version: StringUtil_1.StringUtil.EMPTY,
description: StringUtil_1.StringUtil.EMPTY,
contact: {
name: StringUtil_1.StringUtil.EMPTY,
url: StringUtil_1.StringUtil.EMPTY,
email: StringUtil_1.StringUtil.EMPTY,
},
license: {
name: "BSD 3-Clause",
},
},
servers: [],
paths: {},
components: {
schemas: {},
},
};
this.apiDoc.info.title = info.title;
this.apiDoc.info.version = info.version;
this.apiDoc.info.description = info.description;
this.apiDoc.info.contact = info.contact;
this.apiDoc.info.license = info.license;
this.setSchemas(SchemasStore_1.SchemasStore.get());
this.setSchemasSecurity(SecuritySchemesStore_1.SecuritySchemesStore.get());
}
setSchemas(schemas) {
this.apiDoc.components.schemas = schemas;
}
setSchemasSecurity(securitySchemes) {
this.apiDoc.components.securitySchemes = securitySchemes;
}
buildParameters(path, parameters) {
if (!parameters.length)
return [];
const parameterNamesInPath = path.match(/(?<=\/:)\w+/g);
if (parameterNamesInPath === null || parameterNamesInPath === void 0 ? void 0 : parameterNamesInPath.length) {
const everyParameterInPathIsInParameters = parameterNamesInPath.every((parameterName) => parameters.find((parameter) => parameter.name === parameterName));
if (!everyParameterInPathIsInParameters) {
console.warn(`Path ${path} has parameters in path that are not defined in parameters array.`);
}
}
return parameters;
}
buildSchema(scheme) {
const schemaToSet = {
type: types_1.PropTypeEnum.OBJECT,
items: { type: types_1.PropTypeEnum.OBJECT, $ref: StringUtil_1.StringUtil.EMPTY },
$ref: StringUtil_1.StringUtil.EMPTY,
};
if (scheme.type === types_1.PropTypeEnum.ARRAY) {
schemaToSet.items = {
type: scheme.type,
$ref: `#/components/schemas/${scheme.schema.name}`,
};
delete schemaToSet.type;
delete schemaToSet.$ref;
}
else if (scheme.type === types_1.PropTypeEnum.OBJECT) {
schemaToSet.$ref = `#/components/schemas/${scheme.schema.name}`;
delete schemaToSet.type;
delete schemaToSet.items;
}
else {
schemaToSet.type = scheme.type;
delete schemaToSet.items;
delete schemaToSet.$ref;
}
return schemaToSet;
}
buildRequestBody(requestBody) {
return {
description: requestBody === null || requestBody === void 0 ? void 0 : requestBody.description,
required: requestBody === null || requestBody === void 0 ? void 0 : requestBody.required,
content: {
[requestBody === null || requestBody === void 0 ? void 0 : requestBody.contentType]: {
schema: { $ref: `#/components/schemas/${requestBody === null || requestBody === void 0 ? void 0 : requestBody.scheme.schema.name}` },
},
},
};
}
filterNonElegibleTags(tags) {
if (!tags.length)
return [];
const noElegibleTags = [
{ value: "api", regexType: false },
{ value: /v[0-9]+/, regexType: true },
];
return tags.filter((tag) => !noElegibleTags.some((noElegibleTag) => noElegibleTag.regexType
? noElegibleTag.value.test(tag)
: noElegibleTag.value === tag));
}
getTagFromPath(path) {
const tags = [];
const relativePath = path.replace(__classPrivateFieldGet(this, _ApiDocGenerator_apiRootPath, "f"), "");
const pathSegments = this.filterNonElegibleTags(relativePath.split("/").filter(Boolean));
if (pathSegments.length) {
tags.push(StringUtil_1.StringUtil.capitalize(pathSegments[0]));
}
else {
tags.push("Default");
}
return tags;
}
setApiRootPath(path) {
__classPrivateFieldSet(this, _ApiDocGenerator_apiRootPath, path, "f");
}
saveApiDoc(dirName, filePath) {
const wasDocGenerated = Object.keys(this.apiDoc.paths).length;
if (!wasDocGenerated)
return this;
filePath = (0, path_1.resolve)((0, path_1.join)(dirName, filePath));
(0, fs_1.writeFileSync)(filePath, JSON.stringify(this.apiDoc, null, 2), "utf8");
return this;
}
createRouteDoc(route) {
const { produces, method, description, apiDoc } = route;
if (!apiDoc)
return;
let path = route.path;
const { requestBody, parameters, securitySchemes } = apiDoc;
if (path.includes(":"))
path = path.replace(/:(\w+)/g, "{$1}");
if (!this.apiDoc.paths[path]) {
this.apiDoc.paths[path] = {};
}
if (!this.apiDoc.paths[path][method]) {
this.apiDoc.paths[path][method] = {
description: description,
tags: this.getTagFromPath(path),
};
this.apiDoc.paths[path][method].responses = {};
if (requestBody)
this.apiDoc.paths[path][method].requestBody = {};
if (parameters)
this.apiDoc.paths[path][method].parameters = [];
}
produces.forEach(({ httpStatus, model }) => {
if (!model)
return;
const { contentType, scheme } = model;
this.apiDoc.paths[path][method].responses[httpStatus.toString()] = {
description: httpStatusDescriber_1.default[httpStatus],
content: {
[contentType]: {
schema: this.buildSchema(scheme),
},
},
};
});
if (requestBody) {
this.apiDoc.paths[path][method].requestBody = this.buildRequestBody(requestBody);
}
if (parameters) {
this.apiDoc.paths[path][method].parameters = this.buildParameters(path, parameters);
}
if (securitySchemes) {
const securityKeys = Object.keys(securitySchemes);
this.apiDoc.paths[path][method].security = securityKeys.map((key) => ({
[key]: [],
}));
}
}
setServerUrl(url, description) {
this.apiDoc.servers.push({
url,
description,
});
}
finish() {
SchemasStore_1.SchemasStore.dispose();
SecuritySchemesStore_1.SecuritySchemesStore.dispose();
}
}
exports.ApiDocGenerator = ApiDocGenerator;
_ApiDocGenerator_apiRootPath = new WeakMap();