Commit b1e68a82 authored by Alex Quispe's avatar Alex Quispe
Browse files

Build optimizado y scaffolding adicionado.

parent c795d901
......@@ -184,7 +184,11 @@ function _header (route) {
}
function _headerSwagger (route) {
return {
let description = route.description
if (route.permissions) {
description = `<strong>PERMISOS:</strong> <code>${route.permissions}</code><br><br>${description}`
}
const swaggerDefinition = {
path: route.requestPathSwagger,
method:route.method,
content: {
......@@ -192,10 +196,13 @@ function _headerSwagger (route) {
"responses": { "200": { "description": route.description } },
"consumes": [ "application/json" ],
"produces": [ "application/json" ],
"description": route.description,
"name": route.name
"description": description
}
}
if (route.permissions) {
swaggerDefinition.content.security = [{}]
}
return swaggerDefinition
}
/**
......
const express = require('express')
const cors = require('cors')
const request = require('request')
const fs = require('fs')
const _ = require('lodash')
......@@ -14,9 +16,46 @@ class ApidocGenerator {
this.route = new Route(groupName)
}
static server () {
/**
* Servidor para mostrar la documentación
* @type {ExpressInstance}
*/
const app = express()
app.use(cors({
'origin' : '*',
'methods' : 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
'preflightContinue' : true,
'Access-Control-Allow-Headers' : '*'
}))
app.use(express.static(path.resolve(__dirname, `${ApidocGenerator.BUILD_PATH}/public`)))
return app.listen(ApidocGenerator.SERVER_PORT, () => {
console.log(`\n Servidor para la documentación activo: http://localhost:${ApidocGenerator.SERVER_PORT}\n`)
})
}
static scaffold (app) {
_crearCarpetaSiNoExiste(ApidocGenerator.SRC_PATH)
const ROUTES = _getRoutes(app)
const archivosVerificados = []
// console.log("ROUTES = ", ROUTES);
Object.keys(ROUTES).forEach(groupName => {
ROUTES[groupName].forEach(routeInfo => {
const { filePath } = _crearArchivoTemplateSiNoExiste(routeInfo.fileName)
if (!archivosVerificados.includes(filePath)) {
_verificarSiExisteLaRuta(filePath, ROUTES[groupName])
archivosVerificados.push(filePath)
}
_crearContenidoSiNoExiste(filePath, routeInfo)
})
})
console.log('\n Scaffold ok :) \n')
process.exit(0)
}
static async create (filesName) {
console.log(' Construyendo apidoc ...')
if (util.isDir(ApidocGenerator.BUILD_PATH)) util.rmdir(ApidocGenerator.BUILD_PATH)
if (!util.isDir(ApidocGenerator.BUILD_PATH)) util.mkdir(ApidocGenerator.BUILD_PATH)
const outputRoutesPath = path.resolve(ApidocGenerator.BUILD_PATH, 'routes')
if (util.isDir(outputRoutesPath)) { util.rmdir(outputRoutesPath) }
......@@ -26,7 +65,7 @@ class ApidocGenerator {
for (let i in filesName) {
const fileName = filesName[i]
const filePath = path.resolve(ApidocGenerator.SRC_PATH, fileName)
console.log(`\n Procesando archivo "src/${fileName}.js" ...\n`)
console.log(`\n Procesando archivo "${filePath.replace(process.cwd(), '')}" ...\n`)
const dirname = path.dirname(filePath)
const outputDirPath = path.resolve(ApidocGenerator.BUILD_PATH, `routes/${fileName}`)
if(util.isDir(outputDirPath)) util.rmdir(outputDirPath)
......@@ -35,13 +74,16 @@ class ApidocGenerator {
ApidocGenerator.OUTPUT_TEMP_PATH = outputDirPath
await require(filePath)()
}
if (ApidocGenerator.COMPILE) {
const outputApidocPath = path.resolve(ApidocGenerator.BUILD_PATH, 'public')
if (util.isDir(outputApidocPath)) { util.rmdir(outputApidocPath) }
util.mkdir(outputApidocPath)
await _build()
}
// Unión de ficheros y construcción del apidoc
const outputApidocPath = path.resolve(ApidocGenerator.BUILD_PATH, 'public')
if (!util.isDir(outputApidocPath)) { util.mkdir(outputApidocPath) }
await _build()
util.rmdir(outputRoutesPath)
util.removeFile(path.resolve(ApidocGenerator.BUILD_PATH, 'apidoc.js'))
util.removeFile(path.resolve(ApidocGenerator.BUILD_PATH, 'swagger.json'))
console.log('\n \x1b[32minfo:\x1b[0m Apidoc generado exitosamente :)\n')
}
......@@ -127,9 +169,9 @@ class ApidocGenerator {
// SWAGER
swaggerContent.paths[route.apidocSwagger.path] = swaggerContent.paths[route.apidocSwagger.path] || {}
if (swaggerContent.paths[route.apidocSwagger.path][route.apidocSwagger.method]) {
// console.log(` \x1b[33m${_.padEnd(`[${this.route.method}]`, 9)} ${this.route.path} - ${this.route.group} (${this.route.name}) \u2715 Ya ha sido documentado en SWAGGER\x1b[0m`)
return
}
if (!this.route.request) { route.apidocSwagger.content.summary = '<<< SIN DATOS >>>' }
swaggerContent.paths[route.apidocSwagger.path][route.apidocSwagger.method] = route.apidocSwagger.content
})
apiRouter[this.route.method](this.route.path, this.route)
......@@ -157,7 +199,7 @@ class ApidocGenerator {
content = {
"swagger": "2.0",
"info": {
"description": `ApidocJS: ${ApidocGenerator.DOC_URL}/apidoc`,
"description": ``,
"version": "1.0.0",
"title": "Apidoc",
},
......@@ -198,17 +240,50 @@ class ApidocGenerator {
}
}
ApidocGenerator.BUILD_PATH = path.resolve(process.cwd(), `build`)
ApidocGenerator.API_URL = 'http://localhost:4000'
ApidocGenerator.DOC_URL = 'http://localhost:5000'
ApidocGenerator.COMPILE = true
ApidocGenerator.SRC_PATH = path.resolve(process.cwd(), `src`)
ApidocGenerator.BUILD_PATH = path.resolve(process.cwd(), `build`)
ApidocGenerator.API_URL = 'http://localhost:4000'
function _toWords (text) {
return `${_words(_.upperFirst(_.camelCase(text)))}`
const split = text.split('/')
let result = ''
split.forEach(e => {
result += _.upperFirst(_.deburr(e)) + ' '
})
return result
}
function _words (text) {
return _.replace((_.words(text)).toString(), /,/g, ' ')
function _getRoutes (app) {
const defaultGroupName = '/default'
const routes = {}
routes[`${defaultGroupName}`] = []
for (let i in app._router.stack) {
const r = app._router.stack[i]
if (r.route && r.route.path) {
const routePath = r.route.path
const method = r.route.stack[0].method.toUpperCase()
const groupName = defaultGroupName
let fileName = _.trim(_.replace(groupName, /\//g, '-'), '-')
if (fileName === '') { fileName = 'root' }
const filePath = path.resolve(ApidocGenerator.SRC_PATH,`${fileName}.js`)
routes[groupName].push({ method: method.toLowerCase(), path: routePath, group: groupName, fileName: fileName, filePath: filePath })
} else {
const urlBase = r.regexp.toString().split('/^').pop().split('/?').shift().replace(/\\/g, '')
if (urlBase !== '') {
routes[urlBase] = []
for (let sr in r.handle.stack) {
const routePath = r.handle.stack[sr].route.path
const method = r.handle.stack[sr].route.stack[0].method.toUpperCase()
const groupName = urlBase
let fileName = _.trim(_.replace(groupName, /\//g, '-'), '-')
if (fileName === '') { fileName = 'root' }
const filePath = path.resolve(ApidocGenerator.SRC_PATH,`${fileName}.js`)
routes[groupName].push({ method: method.toLowerCase(), path: routePath, group: groupName, fileName: fileName, filePath: filePath })
}
}
}
}
return routes
}
function toFieldGroup (obj, fullPath = '') {
......@@ -276,7 +351,7 @@ function _request (method, uri, body, headers) {
}
async function _build () {
console.log('\n Uniendo ficheros (apidoc.js, swagger.json) ...\n')
console.log('\n Uniendo ficheros ...\n')
// APIDOC JS
const filePath1 = path.resolve(ApidocGenerator.BUILD_PATH, `apidoc.js`)
......@@ -287,12 +362,13 @@ async function _build () {
const newBlock = util.readFile(info.filePath)
const newRouteKey = newBlock.split('\n')[2]
if (content.includes(newRouteKey)) {
console.log(`\n \x1b[33mAdvertencia:\x1b[0m El servicio "${newRouteKey.substr(7)}" ya ha sido documentado. (ignorado)`)
console.log(` \x1b[33mAdvertencia:\x1b[0m El servicio "${newRouteKey.substr(7)}" ya ha sido documentado. (ignorado)`)
return
}
content += newBlock
})
util.writeFile(filePath1, content)
console.log('', filePath1.replace(process.cwd(), ''), '\u2713')
// SWAGGER
const filePath2 = path.resolve(ApidocGenerator.BUILD_PATH, `swagger.json`)
......@@ -320,11 +396,15 @@ async function _build () {
})
})
util.writeFile(filePath2, JSON.stringify(content2, null, 2))
console.log('', filePath2.replace(process.cwd(), ''), '\u2713\n')
await _buildApidocJS()
util.copyFile(path.resolve(ApidocGenerator.BUILD_PATH, `swagger.json`), path.resolve(ApidocGenerator.BUILD_PATH, `public/swagger.json`))
const SWAGGER_JSON_OUTPUT_PATH = path.resolve(ApidocGenerator.BUILD_PATH, `public/swagger.json`)
util.copyFile(path.resolve(ApidocGenerator.BUILD_PATH, `swagger.json`), SWAGGER_JSON_OUTPUT_PATH)
console.log('', SWAGGER_JSON_OUTPUT_PATH.replace(process.cwd(), ''), '\u2713')
}
async function _buildApidocJS () {
console.log(' Compilando ficheros ...\n');
const templatePath = path.resolve(__dirname, '../template/apidocjs')
const templatePathSwagger = path.resolve(__dirname, '../template/swagger')
const tmpPath = path.resolve(__dirname, '../.temp')
......@@ -346,7 +426,10 @@ async function _buildApidocJS () {
util.writeFile(path.resolve(tmpPath, 'apidoc.json'), JSON.stringify(apidocConfig, null, 2))
util.copyFile(path.resolve(ApidocGenerator.BUILD_PATH, `apidoc.js`), path.resolve(tmpPath, `apidoc.js`))
const output = path.resolve(ApidocGenerator.BUILD_PATH, 'public/apidoc')
await util.copyDir(templatePathSwagger, path.resolve(ApidocGenerator.BUILD_PATH, 'public/swagger-ui'))
console.log('', output.replace(process.cwd(), ''), '\u2713')
const SWAGGER_OUTPUT_PATH = path.resolve(ApidocGenerator.BUILD_PATH, 'public/swagger')
await util.copyDir(templatePathSwagger, SWAGGER_OUTPUT_PATH)
console.log('', SWAGGER_OUTPUT_PATH.replace(process.cwd(), ''), '\u2713')
try { util.rmdir(output) } catch (e) { }
try {
await _createApidoc('.', output, templatePath, tmpPath)
......@@ -381,4 +464,60 @@ async function _createApidoc (input, output, template, execPath) {
}
}
function _crearCarpetaSiNoExiste (dirPath) {
if (!util.isDir(dirPath)) {
util.mkdir(dirPath)
}
}
function _crearArchivoTemplateSiNoExiste (fileName) {
const content = util.readFile(path.resolve(__dirname, '../template/route.js'))
const filePath = path.resolve(ApidocGenerator.SRC_PATH,`${fileName}.js`)
if (!util.isFile(filePath)) {
util.writeFile(filePath, content)
console.log(` [archivo] ${fileName} \u2713`)
}
return { filePath, fileName }
}
function _crearContenidoSiNoExiste (filePath, routeInfo) {
let content = util.readFile(filePath)
const ROUTE_FLAG = '// <!-- [ROUTE DEFINITION] --!> //'
const ROUTE_PATH = `${routeInfo.group === '/default' ? '' : routeInfo.group}${routeInfo.path}`
if (!content.includes(`await ApiGen.${routeInfo.method}('${ROUTE_PATH}')`)) {
content = content.replace(ROUTE_FLAG, `await ApiGen.${routeInfo.method}('${ROUTE_PATH}').request(false).generate()\n\n ${ROUTE_FLAG}`)
console.log(` [ruta] ${routeInfo.fileName} - ${ROUTE_PATH} \u2713`)
}
util.removeFile(filePath)
util.writeFile(filePath, content)
}
function _verificarSiExisteLaRuta (filePath, groupInfo) {
const existInRoutes = (method, path) => {
for(let i in groupInfo) {
const routeInfo = groupInfo[i]
const ROUTE_PATH = `${routeInfo.group === '/default' ? '' : routeInfo.group}${routeInfo.path}`
if (routeInfo.method === method && ROUTE_PATH === path) {
return true
}
}
return false
}
const content = util.readFile(filePath).split('\n')
for (let i in content) {
const line = content[i]
if (line.includes('await ApiGen.')) {
const A = line.indexOf('await ApiGen.') + 13
const B = line.indexOf('(', A)
const routeMethod = line.substring(A, B)
const C = line.indexOf(')', B + 2)
const routePath =line.substring(B + 2, C - 1)
if (!existInRoutes(routeMethod, routePath)) {
const fileName = filePath.replace(process.cwd(), '')
console.log(` \x1b[33mAdvertencia:\x1b[0m "[${routeMethod}] ${routePath}" no se encuentra dentro de la aplicación. Archivo: "${fileName}:${parseInt(i) + 1}:${A - 13 + 1}"`);
}
}
}
}
module.exports = ApidocGenerator
const express = require('express')
const path = require('path')
const cors = require('cors')
const app = express()
const PORT = process.env.DOC_PORT || 5000
const APIDOC_PATH = path.resolve(__dirname, 'build/public')
// view engine setup
app.set('views', APIDOC_PATH)
app.set('view engine', 'ejs')
app.engine('html', require('ejs').renderFile)
app.use(cors({
'origin' : '*',
'methods' : 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
'preflightContinue' : true,
'Access-Control-Allow-Headers' : '*'
}))
// public
app.use(express.static(APIDOC_PATH))
// open index.html
app.get('/', function(req, res) {
res.redirect(`/swagger-ui/index.html?url=http://localhost:${PORT}/swagger.json`)
})
// run server
app.listen(PORT, function() {
console.log(`\n Servidor para la documentación activo: http://localhost:${PORT}\n`)
})
const ApiGen = require('../../')
const path = require('path')
ApiGen.API_URL = 'http://localhost:4000'
ApiGen.DOC_URL = 'http://localhost:5000'
ApiGen.API_URL = process.env.API_URL || 'http://localhost:4000'
ApiGen.SRC_PATH = path.resolve(__dirname, 'src')
ApiGen.BUILD_PATH = path.resolve(__dirname, 'build')
......
const ApiGen = require('../../')
const path = require('path')
ApiGen.SRC_PATH = path.resolve(__dirname, 'src')
const app = require('../')
ApiGen.scaffold(app).catch(e => { console.log(e) })
const path = require('path')
const ApiGen = require('../../')
ApiGen.SERVER_PORT = 5000
ApiGen.BUILD_PATH = path.resolve(__dirname, 'build')
ApiGen.server()
const ApiGen = require('../../../')
module.exports = async () => {
await ApiGen.get('/api/custom/other/route').execute()
await ApiGen.get('/api/custom/other/route').execute()
await ApiGen.get('/api/custom/other/route').execute()
await ApiGen.get('/api/custom/other/route').permissions(['user']).request(false).generate()
// <!-- [ROUTE DEFINITION] --!> //
}
const ApiGen = require('../../../')
module.exports = async () => {
await ApiGen.get('/api/v1/users/').permissions(['admin', 'user']).generate()
await ApiGen.get('/api/v1/users/:id').data({ params: { id: 1 } }).generate()
await ApiGen.post('/api/v1/users/').description('Crea un nuevo usuario').request(false).generate()
await ApiGen.post('/api/v1/users/bulk').request(false).generate()
// <!-- [ROUTE DEFINITION] --!> //
}
const ApiGen = require('../../../')
module.exports = async () => {
await ApiGen.get('/ruta/sin/grupo').request(false).generate()
// <!-- [ROUTE DEFINITION] --!> //
}
const ApiGen = require('../../../')
module.exports = async () => {
await ApiGen.post('/api/v1/users').data({body:{name:'John',user:'admin',pass:'123'}}).key('Admin').generate('GRUPO 2')
await ApiGen.post('/api/v1/users').data({body:{name:'Smith',user:'user',pass:'123'}}).key('User').generate('GRUPO 2')
await ApiGen.post('/api/v1/users/').data({body:{name:'John',user:'admin',pass:'123'}}).key('Admin').generate('GRUPO 2')
await ApiGen.post('/api/v1/users/').data({body:{name:'Smith',user:'user',pass:'123'}}).key('User').generate('GRUPO 2')
}
const ApiGen = require('../../../')
module.exports = async () => {
await ApiGen.get('/api/v1/users').generate()
await ApiGen.post('/api/v1/users').data({body:{name:'rosa',user:'user100',pass:'123'}}).generate()
await ApiGen.get('/api/v1/users/').generate()
await ApiGen.post('/api/v1/users/').data({body:{name:'rosa',user:'user100',pass:'123'}}).generate()
await ApiGen.get('/api/v1/users').name('Devuelve una lista de usuarios').generate('GRUPO 1')
await ApiGen.get('/api/v1/users').name('Listar usuarios').generate('GRUPO 1')
await ApiGen.get('/api/v1/users/').name('Devuelve una lista de usuarios').generate('GRUPO 1')
await ApiGen.get('/api/v1/users/').name('Listar usuarios').generate('GRUPO 1')
await ApiGen.get('/documentando/solo/la/ruta').request(false).generate('GRUPO 3')
......
const ApiGen = require('../../../')
module.exports = async () => {
await ApiGen.post('/api/v1/users').data({body:{name:'John',user:'admin',pass:'123'}}).key('Admin').generate('GRUPO 2')
await ApiGen.post('/api/v1/users').data({body:{name:'Smith',user:'user',pass:'123'}}).key('User').generate('GRUPO 2')
}
const ApiGen = require('../../../')
module.exports = async () => {
await ApiGen.get('/api/v1/users').generate()
await ApiGen.post('/api/v1/users').data({body:{name:'rosa',user:'user100',pass:'123'}}).generate()
await ApiGen.get('/api/v1/users').name('Devuelve una lista de usuarios').generate('GRUPO 1')
await ApiGen.get('/api/v1/users').name('Listar usuarios').generate('GRUPO 1')
await ApiGen.get('/documentando/solo/la/ruta').request(false).generate('GRUPO 3')
const DATA = [
{ id: 1, name: 'Anna', user: 'admin', pass: '123' },
{ id: 2, name: 'Juan', user: 'user', pass: '123' }
]
await ApiGen.post('/api/v1/users/bulk').data({ body: DATA }).generate('GROUP 4')
}
module.exports = require('./src/app')
\ No newline at end of file
{
"name": "example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index",
"doc:scaffold": "node documentation/scaffold",
"doc:generate": "node documentation/generate",
"doc:server": "node documentation/server"
},
"author": "",
"license": "ISC"
}
......@@ -14,7 +14,9 @@ app.use(cors({
app.use(express.static('public'))
app.get('/api/v1/users', (req, res, next) => {
const ROUTER1 = express.Router()
ROUTER1.get('/', (req, res, next) => {
const users = [
{ id: 1, name: 'John', user: 'admin', pass: '123' },
{ id: 2, name: 'Dany', user: 'user', pass: '123' }
......@@ -22,7 +24,12 @@ app.get('/api/v1/users', (req, res, next) => {
res.status(200).json(users)
})
app.post('/api/v1/users', (req, res, next) => {
ROUTER1.get('/:id', (req, res, next) => {
const user = { id: 1, name: 'John', user: 'admin', pass: '123' }
res.status(200).json(user)
})
ROUTER1.post('/', (req, res, next) => {
const result = {
finalizado: true,
datos: { id: 100, name: req.body.name, user: req.body.user, pass: req.body.pass }
......@@ -30,7 +37,7 @@ app.post('/api/v1/users', (req, res, next) => {
res.status(201).json(result)
})
app.post('/api/v1/users/bulk', (req, res, next) => {
ROUTER1.post('/bulk', (req, res, next) => {
const users = req.body
let cnt = 1
users.forEach(user => { user.id = cnt++ })
......@@ -41,7 +48,17 @@ app.post('/api/v1/users/bulk', (req, res, next) => {
res.status(201).json(result)
})
const ROUTER2 = express.Router()
ROUTER2.get('/other/route', (req, res, next) => { res.status(200).json({ msg: 'ok' }) })
app.get('/ruta/sin/grupo', (req, res, next) => { res.status(200).json({ msg: 'ok' }) })
app.use('/api/v1/users', ROUTER1)
app.use('/api/custom', ROUTER2)
const PORT = 4000
app.listen(PORT)
console.log(`App listening on port ${PORT}`);
console.log(`\n App listening on port ${PORT}\n`);
module.exports = app
const express = require('express')
const pathToSwaggerUi = require('swagger-ui-dist').absolutePath()
const app = express()
const PORT = process.env.SWAGGER_PORT || '5000'
app.use(express.static(pathToSwaggerUi))
app.listen(PORT)
console.log(`\n Swagger UI activo: http://localhost:${PORT}\n`)
......@@ -4,10 +4,7 @@
"description": "Genera la documentación de un servicio web RESTFull para ApidocJS y Swagger",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"app": "node example/service/app",
"example": "node example/documentation && rm -rf public && cp -rf example/documentation/build/public public",
"doc-server": "node example/documentation/doc-server.js"
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "AGETIC",
"license": "GPL v2",
......
......@@ -16,7 +16,7 @@
<script id="template-sidenav" type="text/x-handlebars-template">
<nav id="scrollingNav">
<div class="sidenav-search">
<div class="pagina-inicio"><a href="./../../">Swagger UI</a></div>
<div class="pagina-inicio"><a href="./../../">Página de inicio</a></div>
<input class="form-control search" type="text" placeholder="{{__ "Buscar ..."}}">
<span class="search-reset">x</span>
</div>
......
const ApiGen = require('apidoc-generator')
module.exports = async () => {
// <!-- [ROUTE DEFINITION] --!> //
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment