@trapi/swagger
Version:
Generate Swagger files from a decorator APIs.
254 lines • 10.7 kB
JavaScript
"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