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

Estructura actualizada

parent fe86a559
/node_modules
/src/template
module.exports = {
"env": {
"node": true,
"commonjs": true,
"es6": true
},
"extends": "standard",
"parserOptions": {
"ecmaVersion": 2017
},
"rules": {
"no-multi-spaces": "off",
"key-spacing": ["error", {
"align": "value",
"align": "colon",
"align": { "beforeColon": true, "afterColon": true, "on": "colon" }
}]
},
"globals": {
"describe": true,
"expect": true,
"it": true,
"before": true,
"after": true,
"beforeEach": true,
"afterEach": true
}
}
......@@ -2,3 +2,4 @@
/node_modules
/temp
/package-lock.json
/test/app
# Apidoc Generator
# Agetic BPM Doc Generator
Genera la documentación de un servicio web RESTFull con soporte para ApidocJS y Swagger
Generador de Apidoc y Test de Integración para servicios web creados con express.
La documentación se genera a partir de las respuestas que devuelve el servicio.
## Características
## Caracteristicas:
- El servicio debe estar creado con [express](https://expressjs.com/es/).
- Utiliza [ava](https://github.com/avajs/ava) para ejecutar los tests y [ApidocJS](http://apidocjs.com/) junto con [Swagger](https://swagger.io/) para construir el apidoc.
- Es posible documentar las rutas a partir de una instancia de `express`, sin necesidad de levantar el servicio.
- Si el servicio está activo en modo `development` o `test`, es posible ejecutar funciones adicionales realizando peticiones sobre el servicio, permitiendo de esa forma documentar las respuestas automaticamente.
- Es posible generar un escaffold para los tests.
## Interfaz de línea de comandos
### Archivo `src/auth.js`
Esta librería contiene una herramienta CLI, cuya función es generar y procesar todos los ficheros que se requieren para crear la documentación.
```js
const ApiGen = require('apidoc-generator')
### Instalación
module.exports = async () => {
await ApiGen.post('/api/auth/signin').inputData({ body: { user: 'demo1', password: 'Developer' } }).generate() // Sin verificar
await ApiGen.post('/api/auth/signin').inputData({ body: { user: 'demo1', password: 'Developer' } }).request(true).generate() // Verifica la ruta
}
```bash
npm install -g git+ssh://git@gitlab.geo.gob.bo:agetic/agetic-bpm-doc-generator.git
```
La propiedad `request(true)` indica si se va a ejecutar la petición, en tal caso se documenta el resultado de la petición automáticamente.
Para verificar la instalación, ejecutar el comando: `doc --version`
### Archivo `src/api.js`
Para ver todas las opciones disponibles: `doc --help` o `doc <comando> --help`
```js
const ApiGen = require('apidoc-generator')
## Configuración del proyecto
Al utilizar esta herramienta, se observan las siguientes modificaciones en el proyecto:
```txt
app
├─ doc.json
├─ index.js
└─ package.json
```
### Archivo de configuración `doc.json`
Este archivo, puede ser generado con el comando `doc init`
module.exports = async () => {
const login = await ApiGen.post('/api/auth/signin').inputData({ body: { user: 'demo1', password: 'Developer' } }).execute()
```json
{
"buildPath": "doc/build",
"testPath": "doc/test",
"templatePath": "doc/template",
"compilePath": "doc/public/bpm-doc",
"helpersPath": "doc/helpers",
"helpersType": "YAML",
"apiUrl": "http://localhost:4000",
"apiName": "Apidoc",
"apiVersion": "1.0.0",
"apiDescription": "<a href=\"../apidoc\" style=\"background-color: #0062cc;color:white;padding:10px;border-radius:20px;text-decoration:none;font-weight:bold\">APIDOC JS</a>",
"serverPort": 5000,
"serverPublic": "doc/public",
"serverRedirect": "./bpm-doc/swagger",
"appPath": "index.js"
}
```
### Archivo `package.json`
Estos datos se agregan automáticamente al ejecutar el comando `doc init`
```json
{
"devDependencies": {
"agetic-bpm-doc-generator": "git+ssh://git@gitlab.geo.gob.bo:agetic/agetic-bpm-doc-generator.git",
"ava": "^0.25.0"
}
}
```
const AUTH_HEADER = { Authorization: `Bearer ${login.token}` }
Esta configuración es opcional, en caso de que no se tenga instalado globalmente la libreria.
await ApiGen.get('/api/v1/instituciones').inputData({ headers: AUTH_HEADER }).request(true).generate('CUSTOM GROUP')
```json
{
"scripts": {
"doc:routes": "NODE_ENV=test ./node_modules/.bin/doc routes",
"doc:scaffold": "NODE_ENV=test ./node_modules/.bin/doc scaffold",
"doc:build": "rm -rf doc/build && NODE_ENV=test ./node_modules/.bin/ava doc/test/* --serial --verbose && npm run doc:compile",
"doc:compile": "./node_modules/.bin/doc compile",
"doc:server": "./node_modules/.bin/doc server",
"doc:start": "npm run doc:compile && npm run doc:server"
}
}
```
## Modo de uso
## Proceso de creación del apidoc
`node index.js`
```bash
# Crea el archivo de configuración
doc init
El resultado se encuentra en la carpeta `build`
# Crea los ficheros base
npm run doc:scaffold
- Apidoc compilado: `build/apidoc`
- Json Swagger: `build/apidoc/swagger.json`
# Ejecuta los tests y construye los ficheros que requiere ApidocJS y Swagger.
npm run doc:build
## Documentación
# Compila los ficheros
npm run doc:compile
# Levanta el servidor para visualizar el apidoc
npm run doc:server
# Compila los ficheros y Levanta el servidor
npm run doc:start
```
El siguiente comando, muestra todas las rutas de la aplicación
```bash
npm run doc:routes
```
### Métodos soportados:
## Métodos soportados:
```js
await ApiGen.get('api/v1/users').generate()
await ApiGen.post('api/v1/users').generate()
await ApiGen.put('api/v1/users/:id').generate()
await ApiGen.patch('api/v1/users/:id').generate()
await ApiGen.delete('api/v1/users/:id').generate()
await ApidocGenerator.get('api/v1/users').generate()
await ApidocGenerator.post('api/v1/users').generate()
await ApidocGenerator.put('api/v1/users/:id').generate()
await ApidocGenerator.patch('api/v1/users/:id').generate()
await ApidocGenerator.delete('api/v1/users/:id').generate()
```
### Funciones disponibles:
......@@ -69,22 +132,22 @@ await ApiGen.delete('api/v1/users/:id').generate()
| `permissions` | Lista de los roles. Ej.: `.permissions(['admin', 'user'])` | `null` |
| `request` | Indica si se va a ejecutar la petición para crear los datos de salida. | `false` |
| `key` | Palabra clave que se adiciona al final del nombre de la ruta. | `null` |
| `inputData` | Datos de entrada: `{ headers: {}, params: {}, query: {}, body: {} }` | `{}` |
| `outputData` | Datos de salida: `body` | `{}` |
| `inputData` | Datos de entrada: `{ headers: {}, params: {}, query: {}, body: {} }` | `null` |
| `outputData` | Datos de salida: `body` | `null` |
| `inputExamples` | Ejemplos de datos de entrada: `{ title: '', data: obj }` | `null` |
| `outputExamples` | Ejemplos de datos de salida: `{ title: '', data: obj }` | `null` |
**Ejemplo:**
```js
await ApiGen.get('/api/v1/users').generate()
await ApiGen.post('/api/v1/users').inputData({ body; { user: 'admin', pass: '123'} }).generate()
await ApiGen.post('/api/v1/users').inputData({ body; { user: 'admin', pass: '123'} }).name('Autenticar').generate()
await ApidocGenerator.get('/api/v1/users').generate()
await ApidocGenerator.post('/api/v1/users').inputData({ body; { user: 'admin', pass: '123'} }).generate()
await ApidocGenerator.post('/api/v1/users').inputData({ body; { user: 'admin', pass: '123'} }).name('Autenticar').generate()
await ApiGen.get('/api/v1/users').key('Admin').generate()
await ApiGen.get('/api/v1/users').key('User').generate()
await ApidocGenerator.get('/api/v1/users').key('Admin').generate()
await ApidocGenerator.get('/api/v1/users').key('User').generate()
await ApiGen.get('/ruta/verificada').request(true).generate()
await ApidocGenerator.get('/ruta/verificada').request(true).generate()
```
### Función `execute`
......@@ -92,175 +155,40 @@ await ApiGen.get('/ruta/verificada').request(true).generate()
Ejecuta la petición y devuelve el resultado del body.
```js
// Inicializa el objeto
ApidocRequest.init()
// Devuelve una lista de usuarios
const usuarios = await ApiGen.get('/api/v1/users').execute()
const usuarios = await ApidocRequest.get('/api/v1/users').execute()
```
### Función `generate`
Ejecuta la petición (solamente si la propiedad `request` es igual a `true`), genera el respectivo apidoc y devuelve el resultado del body.
Adicionalmente se puede pasar como parámetro el nombre del grupo al que pertenece la ruta. Por defecto es el nombre del fichero.
```js
// group = <fileName>
await ApiGen.get('/api/v1/users').generate()
// group = Auth
await ApiGen.get('/api/v1/users').generate('Auth')
// Devuelve el resultado de la petición (body)
const body = await ApiGen.get('/api/v1/users').generate()
```
## Configuración del proyecto para generar la documentación y los tests
### Estructura
```txt
app
├─ documentation
| ├─ build
| ├─ template
| │ ├─ test-block.js
| │ └─ test-body.js
| ├─ src
| │ └─ api-v1-users.js
| ├─ scaffold.js
| ├─ scaffold-test.js
| ├─ generate.js
| └─ server.js
├─ src
│ └─ app.js
├─ test
│ └─ api-v1-users.test.js
├─ index.js
└─ package.json
```
### Archivo `documentation/scaffold.js`
Este fichero se encarga de crear los ficheros necesarios para documentar las rutas:
- `documentation/src/api-v1-users.js`
- `documentation/src/api-v1-users.json`
- `documentation/src/api-v1-users.yml`
En este ejemplo, se dará prioridad al fichero `api-v1-users.js`, posteriormente al fichero `JSON` o `YAML` según se haya configurado en la variable `HELP_TYPE`.
```js
const ApiGen = require('apidoc-generator')
const path = require('path')
ApiGen.SRC_PATH = path.resolve(__dirname, 'src')
ApiGen.HELP_TYPE = 'YAML' // YAML o JSON
const app = require(process,cwd())
ApiGen.scaffold(app)
```
### Archivo `documentation/scaffold-test.js`
Este fichero se encarga de crear los ficheros necesarios para testear los servicios.
Se requieren los paquetes: `ava` y `supertest` para ejecutar los tests.
- `test/api-v1-users.test.js`
```js
const ApiGen = require('./../../')
const path = require('path')
ApiGen.TEST_PATH = path.resolve(__dirname, '../test')
ApiGen.TEMPLATE_PATH = path.resolve(__dirname, 'template')
const app = require(process.cwd())
ApiGen.scaffoldTest(app)
```
Ejecuta una consulta (solamente si la propiedad `request` es igual a `true`), genera el respectivo apidoc y devuelve el resultado de la consulta.
### Templates para generar los tests
Templates por defecto: Se configuran con la variable `ApidocGenerate.TEMPLATE_PATH`
#### Archivo `template/test-block.js`
```js
test.serial.cb('[GROUP] [METHOD] PATH', t => {
request(app).METHOD('PATH')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.end((err, res) => {
t.pass()
t.end()
})
})
// <!-- [TEST DEFINITION] --!> //
```
#### Archivo `template/test-body.js`
```js
const test = require('ava')
const request = require('supertest')
const app = require(process.cwd())
await ApidocGenerator.get('/api/v1/users').generate()
// <!-- [TEST DEFINITION] --!> //
// Devuelve el resultado de la petición
const response = await ApidocGenerator.get('/api/v1/users').generate()
const error = response.error
const body = response.body
```
### Archivo `documentation/generate.js`
Este fichero se encarga de generar la documentación con soporte para ApidocJS y Swagger.
### Flujo de ejecución
```js
const ApiGen = require('apidoc-generator')
const path = require('path')
ApiGen.API_URL = 'http://localhost:4000'
ApiGen.DOC_SERVER_PORT = 5000
ApiGen.BUILD_PATH = path.resolve(__dirname, 'build')
ApiGen.SRC_PATH = path.resolve(__dirname, 'src')
ApiGen.HELP_TYPE = 'YAML' // YAML o JSON
ApiGen.DESCRIPTION = 'Descripción general del Apidoc'
ApiGen.create().catch(e => console.log(e))
```
### Archivo `documentation/server.js`
Este fichero, se encarga de ejecutar un servidor para visualizar la documentación.
#### ApidocGenerator
```js
const ApiGen = require('apidoc-generator')
const path = require('path')
1. Definición de la ruta (method, path)
- Carga la configuración global a nivel de archivo [automático cuando se define la ruta]
- Carga los helpers (propiedades estáticas) [automático cuando se define la ruta]
- Asignación de datos
- generate: Completa los datos faltantes con sus valores por defecto
- generate: Si request === true, ejecuta la petición y actualiza la propiedad output.
ApiGen.DOC_SERVER_PORT = 5000
ApiGen.BUILD_PATH = path.resolve(__dirname, 'build')
#### ApidocRequest
const SWAGGER_JSON_URL = `http://localhost:${ApiGen.DOC_SERVER_PORT}/swagger.json`
ApiGen.REDIRECT_PATH = `/swagger?url=${SWAGGER_JSON_URL}`
ApiGen.server()
```
### Archivo `package.json`
```json
{
"name": "example",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index",
"test": "NODE_ENV=test ava test/* --serial --verbose",
"doc:scaffold": "node documentation/scaffold",
"doc:scaffold:test": "node documentation/scaffold-test",
"doc:generate": "node documentation/generate",
"doc:server": "node documentation/server",
"doc:start": "npm run doc:scaffold && npm run doc:generate && npm run doc:server"
},
"dependencies": {
"ava": "^0.25.0",
"supertest": "^3.3.0"
}
}
```
1. Definición de la ruta (method, path)
- Carga los helpers (propiedades estáticas) [manual llamando al método `.loadHelp(groupName)`]
- Asignación de datos
- execute: Ejecuta la petición con los datos actuales
This diff is collapsed.
const test = require('ava')
const request = require('supertest')
const app = require('../')
test.serial.cb('[Api V1 Users] [get] /api/v1/users/', t => {
request(app).get('/api/v1/users/')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.end((err, res) => {
t.pass()
t.end()
})
})
test.serial.cb('[Api V1 Users] [get] /api/v1/users/:id', t => {
request(app).get('/api/v1/users/:id')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.end((err, res) => {
t.pass()
t.end()
})
})
test.serial.cb('[Api V1 Users] [post] /api/v1/users/', t => {
request(app).post('/api/v1/users/')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.end((err, res) => {
t.pass()
t.end()
})
})
const ApiGen = require('./../../')
const path = require('path')
ApiGen.API_URL = process.env.API_URL || 'http://localhost:4000'
ApiGen.DOC_SERVER_PORT = process.env.DOC_SERVER_PORT || 5000
ApiGen.BUILD_PATH = path.resolve(__dirname, 'build')
ApiGen.SRC_PATH = path.resolve(__dirname, 'src')
ApiGen.HELP_TYPE = 'YAML' // YAML o JSON
const SWAGGER_JSON_URL = `http://localhost:${ApiGen.DOC_SERVER_PORT}/swagger.json`
let description = ''
description += `Swagger: http://localhost:${ApiGen.DOC_SERVER_PORT}/swagger?url=${SWAGGER_JSON_URL}\n`
description += `ApidocJS: http://localhost:${ApiGen.DOC_SERVER_PORT}/apidoc\n`
ApiGen.DESCRIPTION = description
ApiGen.create().catch(e => console.log(e))
const ApiGen = require('./../../')
const path = require('path')
ApiGen.TEST_PATH = path.resolve(__dirname, '../test')
ApiGen.TEMPLATE_PATH = path.resolve(__dirname, 'template')
const app = require(process.cwd())
ApiGen.scaffoldTest(app)
const ApiGen = require('./../../')
const path = require('path')
ApiGen.SRC_PATH = path.resolve(__dirname, 'src')
ApiGen.HELP_TYPE = 'YAML' // YAML o JSON
const app = require(process.cwd())
ApiGen.scaffold(app)
const ApiGen = require('./../../')
const path = require('path')
ApiGen.DOC_SERVER_PORT = process.env.DOC_SERVER_PORT || 5000
ApiGen.BUILD_PATH = path.resolve(__dirname, 'build')
const SWAGGER_JSON_URL = `http://localhost:${ApiGen.DOC_SERVER_PORT}/swagger.json`
ApiGen.REDIRECT_PATH = `/swagger?url=${SWAGGER_JSON_URL}`
ApiGen.server()
const ApiGen = require('../../../')
module.exports = async () => {
await ApiGen.get('/api/custom/other/route').generate()
// <!-- [ROUTE DEFINITION] --!> //
}
-
method: get
path: /api/custom/other/route
const ApiGen = require('../../../')
module.exports = async () => {
await ApiGen.get('/api/v1/users/').generate()
await ApiGen.get('/api/v1/users/:id').generate()
await ApiGen.post('/api/v1/users/').generate()
await ApiGen.post('/api/v1/users/bulk').generate()
// <!-- [ROUTE DEFINITION] --!> //
}
-
method: get
path: /api/v1/users/
-
method: get
path: '/api/v1/users/:id'
-
method: post
path: /api/v1/users/
-
method: post
path: /api/v1/users/bulk
const ApiGen = require('../../../')
module.exports = async () => {
await ApiGen.get('/ruta/sin/grupo').generate()
// <!-- [ROUTE DEFINITION] --!> //
}
-
method: get
path: /ruta/sin/grupo
test.serial.cb('[GROUP] [METHOD] PATH', t => {
t.pass()
t.end()
// request(app).METHOD('PATH')
// .set('Accept', 'application/json')
// .expect('Content-Type', /json/)
// .end((err, res) => {
// t.pass()
// t.end()
// })
})
// <!-- [TEST DEFINITION] --!> //
const test = require('ava')
const request = require('supertest')
const app = require(process.cwd())
// <!-- [TEST DEFINITION] --!> //
module.exports = require('./src/app')
\ No newline at end of file
Markdown is supported
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