UNPKG

@trapi/swagger

Version:

Generate Swagger files from a decorator APIs.

254 lines 10.7 kB
"use strict"; /* * Copyright (c) 2021-2023. * Author Peter Placzek (tada5hi) * For the full copyright and license information, * view the LICENSE file that was distributed with this source code. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AbstractSpecGenerator = void 0; const metadata_1 = require("@trapi/metadata"); const node_path_1 = __importDefault(require("node:path")); const node_fs_1 = __importDefault(require("node:fs")); const smob_1 = require("smob"); const yamljs_1 = __importDefault(require("yamljs")); const config_1 = require("../config"); const schema_1 = require("../schema"); class AbstractSpecGenerator { constructor(metadata, config) { this.metadata = metadata; this.config = (0, config_1.buildOptions)(config); } async save() { if (!this.config.output) { return; } if (typeof this.spec === 'undefined') { throw new Error('The spec has not been build yet...'); } try { await node_fs_1.default.promises.access(this.config.outputDirectory, node_fs_1.default.constants.R_OK | node_fs_1.default.constants.O_DIRECTORY); } catch (e) { await node_fs_1.default.promises.mkdir(this.config.outputDirectory, { recursive: true }); } const data = [ { path: node_path_1.default.join(this.config.outputDirectory, `${this.config.outputFileName}.json`), name: `${this.config.outputFileName}.json`, content: JSON.stringify(this.spec, null, 4), }, ]; if (this.config.yaml) { data.push({ path: node_path_1.default.join(this.config.outputDirectory, `${this.config.outputFileName}.yaml`), name: `${this.config.outputFileName}.yaml`, content: yamljs_1.default.stringify(this.spec, 1000), }); } const promises = []; for (let i = 0; i < data.length; i++) { promises.push(node_fs_1.default.promises.writeFile(data[i].path, data[i].content, { encoding: 'utf-8' })); } await Promise.all(promises); } buildInfo() { const info = { title: this.config.name || 'Documentation', version: this.config.version || '1.0.0', }; if (this.config.description) { info.description = this.config.description; } if (this.config.license) { info.license = { name: this.config.license }; } return info; } getSchemaForType(type) { if ((0, metadata_1.isVoidType)(type) || (0, metadata_1.isUndefinedType)(type)) { return {}; } if ((0, metadata_1.isReferenceType)(type)) { return this.getSchemaForReferenceType(type); } if ((0, metadata_1.isPrimitiveType)(type)) { return this.getSchemaForPrimitiveType(type); } if ((0, metadata_1.isArrayType)(type)) { return this.getSchemaForArrayType(type); } if ((0, metadata_1.isEnumType)(type)) { return this.getSchemaForEnumType(type); } if ((0, metadata_1.isUnionType)(type)) { return this.getSchemaForUnionType(type); } if ((0, metadata_1.isIntersectionType)(type)) { return this.getSchemaForIntersectionType(type); } if ((0, metadata_1.isNestedObjectLiteralType)(type)) { return this.getSchemaForObjectLiteralType(type); } return {}; } getSchemaForPrimitiveType(type) { const PrimitiveSwaggerTypeMap = { [metadata_1.TypeName.ANY]: { additionalProperties: true, }, [metadata_1.TypeName.BINARY]: { type: schema_1.DataTypeName.STRING, format: schema_1.DataFormatName.BINARY }, [metadata_1.TypeName.BOOLEAN]: { type: schema_1.DataTypeName.BOOLEAN }, [metadata_1.TypeName.BUFFER]: { type: schema_1.DataTypeName.STRING, format: schema_1.DataFormatName.BYTE }, [metadata_1.TypeName.BYTE]: { type: schema_1.DataTypeName.STRING, format: schema_1.DataFormatName.BYTE }, [metadata_1.TypeName.DATE]: { type: schema_1.DataTypeName.STRING, format: schema_1.DataFormatName.DATE }, [metadata_1.TypeName.DATETIME]: { type: schema_1.DataTypeName.STRING, format: schema_1.DataFormatName.DATE_TIME }, [metadata_1.TypeName.DOUBLE]: { type: schema_1.DataTypeName.NUMBER, format: schema_1.DataFormatName.DOUBLE }, [metadata_1.TypeName.FILE]: { type: schema_1.DataTypeName.STRING, format: schema_1.DataFormatName.BINARY }, [metadata_1.TypeName.FLOAT]: { type: schema_1.DataTypeName.NUMBER, format: schema_1.DataFormatName.FLOAT }, [metadata_1.TypeName.BIGINT]: { type: schema_1.DataTypeName.INTEGER }, [metadata_1.TypeName.INTEGER]: { type: schema_1.DataTypeName.INTEGER, format: schema_1.DataFormatName.INT_32 }, [metadata_1.TypeName.LONG]: { type: schema_1.DataTypeName.INTEGER, format: schema_1.DataFormatName.INT_64 }, [metadata_1.TypeName.OBJECT]: { type: schema_1.DataTypeName.OBJECT, additionalProperties: true, }, [metadata_1.TypeName.STRING]: { type: schema_1.DataTypeName.STRING }, [metadata_1.TypeName.UNDEFINED]: {}, }; return PrimitiveSwaggerTypeMap[type.typeName] || { type: schema_1.DataTypeName.OBJECT }; } getSchemaForArrayType(arrayType) { return { type: schema_1.DataTypeName.ARRAY, items: this.getSchemaForType(arrayType.elementType), }; } getSchemaForObjectLiteralType(objectLiteral) { const properties = this.buildProperties(objectLiteral.properties); const additionalProperties = objectLiteral.additionalProperties && this.getSchemaForType(objectLiteral.additionalProperties); const required = objectLiteral.properties .filter((prop) => prop.required && !this.isUndefinedProperty(prop)) .map((prop) => prop.name); // An empty list required: [] is not valid. // If all properties are optional, do not specify the required keyword. return { properties, ...(additionalProperties && { additionalProperties }), ...(required && required.length && { required }), type: schema_1.DataTypeName.OBJECT, }; } buildSchemasForReferenceTypes(extendFn) { const output = {}; const keys = Object.keys(this.metadata.referenceTypes); for (let i = 0; i < keys.length; i++) { const referenceType = this.metadata.referenceTypes[keys[i]]; switch (referenceType.typeName) { case metadata_1.TypeName.REF_ALIAS: { output[referenceType.refName] = this.buildSchemaForRefAlias(referenceType); break; } case metadata_1.TypeName.REF_ENUM: { output[referenceType.refName] = this.buildSchemaForRefEnum(referenceType); break; } case metadata_1.TypeName.REF_OBJECT: { output[referenceType.refName] = this.buildSchemaForRefObject(referenceType); break; } } if (typeof extendFn === 'function') { extendFn(output[referenceType.refName], referenceType); } } return output; } // ---------------------------------------------------------------- isUndefinedProperty(input) { return (0, metadata_1.isUndefinedType)(input.type) || ((0, metadata_1.isUnionType)(input.type) && input.type.members.some((el) => (0, metadata_1.isUndefinedType)(el))); } determineTypesUsedInEnum(anEnum) { const set = new Set(); for (let i = 0; i < anEnum.length; i++) { if (anEnum[i] === null) { continue; } set.add(typeof anEnum[i]); } return Array.from(set); } decideEnumType(input) { const types = this.determineTypesUsedInEnum(input); if (types.length === 1) { const value = types[0]; if (value === 'string' || value === 'number' || value === 'boolean') { return value; } throw new Error(`Enums can only have string or number values, but type "${types[0] || 'unknown'}" given.`); } for (let i = 0; i < types.length; i++) { const type = types[i]; if (type !== 'string' && type !== 'number' && type !== 'boolean') { const values = types.join(','); throw new Error(`Enums can only have string or number values, but types ${values} given.`); } } return 'string'; } getOperationId(name) { return name.charAt(0).toUpperCase() + name.substring(1); } groupParameters(items) { const output = {}; for (let i = 0; i < items.length; i++) { if (typeof output[items[i].in] === 'undefined') { output[items[i].in] = []; } output[items[i].in].push(items[i]); } return output; } transformExtensions(input) { if (!input) { return {}; } const output = {}; for (let i = 0; i < input.length; i++) { const extension = input[i]; if (!extension.key.startsWith('x-')) { extension.key = `x-${extension.key}`; } output[extension.key] = extension.value; } return output; } transformValidators(input) { if (!(0, smob_1.isObject)(input)) { return {}; } const keys = Object.keys(input); const output = {}; for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (key.startsWith('is') || key === metadata_1.ValidatorName.MIN_DATE || key === metadata_1.ValidatorName.MAX_DATE) { continue; } output[key] = input[key].value; } return output; } } exports.AbstractSpecGenerator = AbstractSpecGenerator; //# sourceMappingURL=abstract.js.map