swagger-autogen-validator
Version:
This module performs the automatic construction of the Swagger documentation. The module can identify the endpoints and automatically capture methods such as to get, post, put, and so on. The module can also identify the paths, routes, middlewares, respon
359 lines (321 loc) • 15.7 kB
JavaScript
require('./src/prototype-functions');
const fs = require('fs');
const path = require('path');
const glob = require('glob-promise');
const swaggerTags = require('./src/swagger-tags');
const handleFiles = require('./src/handle-files');
const statics = require('./src/statics');
const utils = require('./src/utils');
const handleData = require('./src/handle-data');
const HashName = require('./src/hashName');
const validateRuleToDefinition = require('./src/validateRuleToDefinition');
const { platform } = process;
const symbols = platform === 'win32' ? { success: '', failed: '' } : { success: '✔', failed: '✖' };
function swaggerAutogen(args) {
let options = { language: null, disableLogs: false, disableWarnings: false, openapi: null, autoHeaders: true, autoQuery: true, autoBody: true, autoResponse: true };
let recLang = null;
if (args && typeof args === 'string') {
// will be deprecated in a future version
recLang = args;
} else if (args && typeof args === 'object') {
options = { ...options, ...args };
}
// REFACTOR:
options.language = recLang || options.language || 'en-US';
handleFiles.setOptions(options);
handleData.setOptions(options);
swaggerTags.setLanguage(recLang || options.language || 'en-US');
swaggerTags.setOpenAPI(options.openapi);
swaggerTags.setDisableLogs(options.disableLogs);
return async (outputFile, endpointsFiles, data) => {
try {
if (!outputFile) throw console.error("\nError: 'outputFile' was not specified.");
if (!endpointsFiles) throw console.error("\nError: 'endpointsFiles' was not specified.");
let allFiles = [];
// Checking if endpoint files exist
for (let idx = 0; idx < endpointsFiles.length; ++idx) {
let file = endpointsFiles[idx];
if (file.includes('*')) {
const patternPath = await utils.resolvePatternPath(file);
if (patternPath) {
for (let idxFile = 0; idxFile < patternPath.length; ++idxFile) {
let file = patternPath[idxFile];
let extension = await utils.getExtension(file);
if (!fs.existsSync(file + extension)) {
throw console.error("[swagger-autogen]: Error! File not found: '" + file + "'");
} else {
patternPath[idxFile] = file + extension;
}
}
allFiles = [...allFiles, ...patternPath];
}
} else {
let extension = await utils.getExtension(file);
allFiles = [...allFiles, file + extension];
if (!fs.existsSync(file + extension)) {
throw console.error("[swagger-autogen]: Error! File not found: '" + file + "'");
}
}
}
const objDoc = { ...statics.TEMPLATE, ...data, paths: {} };
if (options.openapi && utils.isNumeric(options.openapi.replaceAll('.', ''))) {
objDoc.openapi = options.openapi;
} else {
objDoc.swagger = '2.0';
}
// Removing all null attributes
for (let key in objDoc) {
if (objDoc[key] === null) {
delete objDoc[key];
}
}
if (!objDoc.info.version) {
objDoc.info.version = statics.TEMPLATE.info.version;
}
if (!objDoc.info.title) {
objDoc.info.title = statics.TEMPLATE.info.title;
}
if (!objDoc.info.description) {
objDoc.info.description = statics.TEMPLATE.info.description;
}
for (let file = 0; file < allFiles.length; file++) {
const filePath = allFiles[file];
const resp = fs.existsSync(filePath);
if (!resp) {
console.error('[swagger-autogen]: Error! Endpoint file not found => ' + "'" + filePath + "'");
if (!options.disableLogs) {
console.log('Swagger-autogen:', '\x1b[31m', 'Failed ' + symbols.failed, '\x1b[0m');
}
return false;
}
let relativePath = filePath.split('/');
if (relativePath.length > 1) {
relativePath.pop();
relativePath = relativePath.join('/');
} else {
relativePath = null;
}
let obj = await handleFiles.readEndpointFile(filePath, '', relativePath, []);
if (obj === false) {
if (!options.disableLogs) {
console.log('Swagger-autogen:', '\x1b[31m', 'Failed ' + symbols.failed, '\x1b[0m');
}
return false;
}
objDoc.paths = { ...objDoc.paths, ...obj };
}
let constainXML = false;
if (JSON.stringify(objDoc).includes('application/xml')) {
// REFACTOR: improve this
constainXML = true;
}
Object.keys(objDoc.definitions).forEach(definition => {
if (constainXML) {
objDoc.definitions[definition] = { ...swaggerTags.formatDefinitions(objDoc.definitions[definition], {}, constainXML), xml: { name: definition } };
} else {
objDoc.definitions[definition] = { ...swaggerTags.formatDefinitions(objDoc.definitions[definition], {}, constainXML) };
}
});
if (objDoc['@definitions']) {
if (!objDoc.definitions) {
objDoc.definitions = {};
}
objDoc.definitions = { ...objDoc.definitions, ...objDoc['@definitions'] };
delete objDoc['@definitions'];
}
/**
* Forcing convertion to OpenAPI 3.x
*/
if (objDoc.openapi) {
if (!objDoc.servers || objDoc.servers.length == 0) {
if (objDoc.host) {
if (objDoc.basePath) {
objDoc.host += objDoc.basePath;
}
if (objDoc.host.slice(0, 4).toLowerCase() != 'http') {
if (objDoc.schemes && objDoc.schemes.length > 0) {
objDoc.schemes.forEach(scheme => {
objDoc.servers.push({
url: scheme + '://' + objDoc.host
});
});
} else {
objDoc.host = 'http://' + objDoc.host;
objDoc.servers = [
{
url: objDoc.host
}
];
}
}
delete objDoc.host;
} else {
delete objDoc.servers;
}
} else {
delete objDoc.host;
}
if (objDoc.components && objDoc.components.schemas) {
Object.keys(objDoc.components.schemas).forEach(schema => {
if (constainXML) {
objDoc.components.schemas[schema] = { ...swaggerTags.formatDefinitions(objDoc.components.schemas[schema], {}, constainXML), xml: { name: schema } };
} else {
objDoc.components.schemas[schema] = { ...swaggerTags.formatDefinitions(objDoc.components.schemas[schema], {}, constainXML) };
}
});
}
if (objDoc.components && objDoc.components['@schemas']) {
if (!objDoc.components) {
objDoc.components = {};
}
if (!objDoc.components.schemas) {
objDoc.components.schemas = {};
}
objDoc.components.schemas = { ...objDoc.components.schemas, ...objDoc.components['@schemas'] };
delete objDoc.components['@schemas'];
}
if (objDoc.components && objDoc.components.examples) {
Object.keys(objDoc.components.examples).forEach(example => {
if (!objDoc.components.examples[example].value) {
let auxExample = { ...objDoc.components.examples[example] };
delete objDoc.components.examples[example];
objDoc.components.examples[example] = {
value: auxExample
};
}
});
}
if (objDoc.definitions && Object.keys(objDoc.definitions).length > 0) {
if (!objDoc.components) {
objDoc.components = {};
}
if (!objDoc.components.schemas) {
objDoc.components.schemas = {};
}
objDoc.components.schemas = {
...objDoc.components.schemas,
...objDoc.definitions
};
delete objDoc.definitions;
}
if (objDoc.securityDefinitions && Object.keys(objDoc.securityDefinitions).length > 0) {
if (!objDoc.components) {
objDoc.components = {};
}
if (!objDoc.components.securitySchemes) {
objDoc.components.securitySchemes = {};
}
objDoc.components.securitySchemes = {
...objDoc.components.securitySchemes,
...objDoc.securityDefinitions
};
delete objDoc.securityDefinitions;
}
if (objDoc.basePath) {
delete objDoc.basePath;
}
if (objDoc.schemes) {
delete objDoc.schemes;
}
if (objDoc.consumes) {
delete objDoc.consumes;
}
if (objDoc.produces) {
delete objDoc.produces;
}
if (objDoc.definitions) {
delete objDoc.definitions;
}
}
/**
* Removing unused parameters
*/
if (objDoc.components && Object.keys(objDoc.components).length == 0) {
delete objDoc.components;
}
if (objDoc.servers && Object.keys(objDoc.servers).length == 0) {
delete objDoc.servers;
}
if (objDoc.tags && objDoc.tags.length == 0) {
delete objDoc.tags;
}
if (objDoc.consumes && objDoc.consumes.length == 0) {
delete objDoc.consumes;
}
if (objDoc.produces && objDoc.produces.length == 0) {
delete objDoc.produces;
}
if (objDoc.definitions && Object.keys(objDoc.definitions).length == 0) {
delete objDoc.definitions;
}
// let dataJSON = JSON.stringify(objDoc, null, 2);
// fs.writeFileSync(outputFile, dataJSON);
if (!options.disableLogs) {
console.log('Swagger-autogen:', '\x1b[32m', 'Success ' + symbols.success, '\x1b[0m');
}
return { success: true, data: objDoc };
} catch (err) {
if (!options.disableLogs) {
console.log('Swagger-autogen:', '\x1b[31m', 'Failed ' + symbols.failed, '\x1b[0m');
}
return { success: false, data: null };
}
};
}
async function getAllDefinitions(definitionFilePattern, validateRulesFilePattern) {
if (!definitionFilePattern) definitionFilePattern = 'src/**/definitions.js';
if (!validateRulesFilePattern) validateRulesFilePattern = 'src/**/validateRules.js';
// all Definitions from all definitions.js
let allDefinitions = {};
let definitionFiles = await glob(definitionFilePattern);
definitionFiles.forEach(definitionFile => {
let definition = require(path.resolve(definitionFile));
allDefinitions = { ...allDefinitions, ...definition };
});
// Definitions 400 bad request
let validateRulesFiles = await glob(validateRulesFilePattern);
validateRulesFiles.forEach(validateRulesFile => {
let definitionValidateRules = require(path.resolve(validateRulesFile));
Object.keys(definitionValidateRules).forEach(nameRule => {
let name = HashName({ name: nameRule + '400', path: validateRulesFile });
allDefinitions = {
...allDefinitions,
[name]: validateRuleToDefinition(definitionValidateRules[nameRule])
};
});
});
return allDefinitions;
}
async function Swagger_Pattern_File_Autogen({ definitionFilePattern, validateRulesFilePattern, outputFile, endpointsFiles, doc }) {
let allDefinitions = await getAllDefinitions(definitionFilePattern, validateRulesFilePattern);
if (!doc.hasOwnProperty('definitions')) doc['definitions'] = {};
doc.definitions = { ...doc.definition, ...allDefinitions };
const { data: swaggerData } = await swaggerAutogen()(outputFile, endpointsFiles, doc);
// rewrite 400 bad request from validator to swagger
let paths = Object.keys(swaggerData.paths);
paths.forEach(path => {
let pathSwagger = swaggerData.paths[path];
Object.keys(pathSwagger).forEach(method => {
let methodSwagger = pathSwagger[method];
if (methodSwagger.responses && methodSwagger.responses['400']) {
let descriptionSplit = methodSwagger.responses[400].description.split('|');
if (descriptionSplit.length === 3) {
let [description, filePath, ruleName] = descriptionSplit;
let ruleFilePath = filePath.split('/');
ruleFilePath[ruleFilePath.length - 1] = 'validateRules.js';
filePath = ruleFilePath.join('/');
let name = HashName({ name: ruleName + '400', path: filePath });
swaggerData.paths[path][method]['responses']['400'] = {
schema: { $ref: `#/definitions/${name}` },
description
};
}
}
});
});
fs.writeFileSync(outputFile, JSON.stringify(swaggerData, null, 2), {
encoding: 'utf-8'
});
return swaggerData;
}
module.exports = () => Swagger_Pattern_File_Autogen;