UNPKG

openapi-tsk

Version:
209 lines (208 loc) 8.98 kB
"use strict"; 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();