From 2c94312244b8e185c0d6c08dd9915ad0c1e5e200 Mon Sep 17 00:00:00 2001 From: Krzysztof Rudowski Date: Sun, 17 Jul 2022 00:39:16 +0200 Subject: [PATCH] fix: fix linting --- .eslintrc | 18 +- example/.eslintrc.js | 18 -- example/index.js | 13 +- example/routes.js | 265 +++++++++++---------- package-lock.json | 544 ++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + src/api.js | 228 +++++++++--------- src/generator.js | 514 ++++++++++++++++++++-------------------- src/index.js | 2 - test/test-api.js | 359 ++++++++++++++-------------- 10 files changed, 1237 insertions(+), 725 deletions(-) delete mode 100644 example/.eslintrc.js diff --git a/.eslintrc b/.eslintrc index 8fb1cba..9b73e4e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,6 @@ { "env": { - "node":true, + "node": true, "es6": true }, "extends": [ @@ -19,16 +19,10 @@ "error", "all" ], - "func-names": "error", - "no-underscore-dangle": [ - 2, - { - "allow": [ - "__" - ] - } - ], + "func-names": "warn", + "no-underscore-dangle": "warn", "global-require": "off", + "guard-for-in": "warn", "quotes": [ "warn", "single", @@ -70,6 +64,10 @@ "skipTemplates": false } ], + "no-multi-assign": "warn", + "no-param-reassign": "warn", + "no-restricted-syntax": "warn", + "no-shadow": "warn", "no-plusplus": [ "error", { diff --git a/example/.eslintrc.js b/example/.eslintrc.js deleted file mode 100644 index bcd6888..0000000 --- a/example/.eslintrc.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - env: { - node: true, - es6: true, - }, - globals: { - describe: true, - it: true - }, - extends: 'eslint:recommended', - rules: { - 'no-console': 0, - indent: ['error', 2], - 'linebreak-style': ['error', 'unix'], - quotes: ['error', 'single'], - semi: 0, - }, -}; diff --git a/example/index.js b/example/index.js index 90d2c5d..9b24248 100644 --- a/example/index.js +++ b/example/index.js @@ -1,6 +1,9 @@ -const koa = require('koa') -const app = new koa() -app.use(require('./routes').middleware()) +const Koa = require('koa'); + +const app = new Koa(); +app.use(require('./routes').middleware()); + app.listen(5566, () => { - console.log('API docs url: http://localhost:5566/apiDocs') -}) + // eslint-disable-next-line no-console + console.log('API docs url: http://localhost:5566/apiDocs'); +}); diff --git a/example/routes.js b/example/routes.js index 2201bfb..dd27218 100644 --- a/example/routes.js +++ b/example/routes.js @@ -1,158 +1,165 @@ -const { SwaggerAPI } = require('../') -const Router = require('koa-joi-router') -const Joi = Router.Joi +const Router = require('@koa-better-modules/joi-router'); +const { SwaggerAPI } = require('../src'); + +const Joi = Router.Joi; /** * Define routes */ -const router = Router() +const router = Router(); -// Get /user/:_id -router.get('/user/:_id', { - meta: { - swagger: { - summary: 'Get User Info', - description: `Note: \nSensitive data can only be viewed by the \`corresponding user\` or \`Admin\`.`, - tags: ['users'] - } - }, - validate: { - params: { - _id: Joi.string().alphanum().max(24).example('abcdefg').description('User id').required() +// Get /user/:id +router.get('/user/:id', { + meta: { + swagger: { + summary: 'Get User Info', + description: 'Note: \nSensitive data can only be viewed by the `corresponding user` or `Admin`.', + tags: ['users'], + }, }, - output: { - '200-299': { - body: Joi.object({ - userId: Joi.string().description('User id') - }).options({ - allowUnknown: true - }).description('User object') - }, - 500: { - body: Joi.object({ - message: Joi.string().description('error message') - }).description('error body') - } - } - }, - handler: async ctx => { - console.log('getUser...') - ctx.body = { - userId: ctx.params._id - } - } -}) + validate: { + params: { + id: Joi.string().alphanum().max(24).example('abcdefg').description('User id').required(), + }, + output: { + '200-299': { + body: Joi.object({ + userId: Joi.string().description('User id'), + }) + .options({ + allowUnknown: true, + }) + .description('User object'), + }, + 500: { + body: Joi.object({ + message: Joi.string().description('error message'), + }).description('error body'), + }, + }, + }, + handler: async (ctx) => { + ctx.body = { + userId: ctx.params.id, + }; + }, +}); // POST /signup router.post('/signup', { - meta: { - swagger: { - summary: 'User Signup', - description: 'Signup with username and password.', - tags: ['users'] - } - }, - validate: { - type: 'json', - body: Joi.object({ - username: Joi.string().alphanum().min(3).max(30).required(), - password: Joi.string().alphanum().min(6).max(30).required() - }).example({username: 'abcdefg', password: '123123'}), - output: { - 200: { - body: { - userId: Joi.string().description('Newly created user id') - } - }, - 500: { - body: { - code: Joi.number().description('error code'), - } - } - } - }, - handler: async ctx => { - ctx.body = { - userId: ctx.body.username - } - } -}) + meta: { + swagger: { + summary: 'User Signup', + description: 'Signup with username and password.', + tags: ['users'], + }, + }, + validate: { + type: 'json', + body: Joi.object({ + username: Joi.string().alphanum().min(3).max(30).required(), + password: Joi.string().alphanum().min(6).max(30).required(), + }).example({ username: 'abcdefg', password: '123123' }), + output: { + 200: { + body: { + userId: Joi.string().description('Newly created user id'), + }, + }, + 500: { + body: { + code: Joi.number().description('error code'), + }, + }, + }, + }, + handler: async (ctx) => { + ctx.body = { + userId: ctx.body.username, + }; + }, +}); const ProfileJoi = Joi.object({ - profileName: Joi.string(), -}) + profileName: Joi.string(), +}); // POST /profile using schema references `ref` router.post('/profile', { - meta: { - swagger: { - summary: 'Profile', - description: 'Signup with username and password.', - tags: ['users'] - } - }, - validate: { - type: 'json', - body: ProfileJoi, - ref: "#/definitions/Profile", - output: { - 200: { + meta: { + swagger: { + summary: 'Profile', + description: 'Signup with username and password.', + tags: ['users'], + }, + }, + validate: { + type: 'json', body: ProfileJoi, - ref: "#/definitions/Profile", - } - } - }, - handler: async ctx => { - ctx.body = { - profileName: 'this is a profile name' - } - } -}) + ref: '#/definitions/Profile', + output: { + 200: { + body: ProfileJoi, + ref: '#/definitions/Profile', + }, + }, + }, + handler: async (ctx) => { + ctx.body = { + profileName: 'this is a profile name', + }; + }, +}); /** * Generate Swagger json from the router object */ -const generator = new SwaggerAPI() -generator.addJoiRouter(router) +const generator = new SwaggerAPI(); +generator.addJoiRouter(router); -const spec = generator.generateSpec({ - info: { - title: 'Example API', - description: 'API for creating and editing examples.', - version: '1.1' - }, - basePath: '/', - tags: [{ - name: 'users', - description: `A User represents a person who can login - and take actions subject to their granted permissions.` - }], - // pass `definitions` if you need schema references - definitions: { - Profile: ProfileJoi - }, -}, { - defaultResponses: { - 200: { - description: 'OK' +const spec = generator.generateSpec( + { + info: { + title: 'Example API', + description: 'API for creating and editing examples.', + version: '1.1', + }, + basePath: '/', + tags: [ + { + name: 'users', + description: `A User represents a person who can login + and take actions subject to their granted permissions.`, + }, + ], + // pass `definitions` if you need schema references + definitions: { + Profile: ProfileJoi, + }, }, - 500: { - description: 'ERROR' + { + defaultResponses: { + 200: { + description: 'OK', + }, + 500: { + description: 'ERROR', + }, + }, // Custom default responses if you don't like default 200 } - } // Custom default responses if you don't like default 200 -}) +); /** * Swagger JSON API */ -router.get('/_api.json', async ctx => { - ctx.body = JSON.stringify(spec, null, ' ') -}) +router.get('/_api.json', async (ctx) => { + ctx.body = JSON.stringify(spec, null, ' '); +}); /** * API documentation */ -router.get('/apiDocs', async ctx => { - ctx.body = ` +router.get('/apiDocs', async (ctx) => { + ctx.body = ` @@ -166,7 +173,7 @@ router.get('/apiDocs', async ctx => { - ` -}) + `; +}); -module.exports = router +module.exports = router; diff --git a/package-lock.json b/package-lock.json index aa29346..8dbed90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "eslint-plugin-prettier": "^4.2.1", "husky": "^8.0.1", "intelli-espower-loader": "^1.1.0", + "koa": "^2.13.4", "lint-staged": "^13.0.3", "mocha": "^10.0.0", "power-assert": "^1.6.1", @@ -147,12 +148,6 @@ "type-is": "^1.6.16" } }, - "node_modules/@koa-better-modules/joi-router/node_modules/koa-compose": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", - "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", - "dev": true - }, "node_modules/@sideway/address": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", @@ -184,6 +179,19 @@ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", @@ -437,6 +445,19 @@ "node": ">= 0.8" } }, + "node_modules/cache-content-type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", + "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", + "dev": true, + "dependencies": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -654,6 +675,16 @@ "node": ">=0.8" } }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -699,6 +730,47 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -708,6 +780,28 @@ "safe-buffer": "~5.1.1" } }, + "node_modules/cookies": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", + "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", + "dev": true, + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cookies/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/core-js": { "version": "2.6.11", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", @@ -796,6 +890,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -805,6 +905,16 @@ "node": ">= 0.6" } }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -838,6 +948,12 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -873,6 +989,15 @@ "core-js": "^2.0.0" } }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/es-abstract": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", @@ -1050,6 +1175,12 @@ "node": ">=0.10.0" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "node_modules/escodegen": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", @@ -1819,6 +1950,15 @@ "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", "dev": true }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2045,6 +2185,25 @@ "he": "bin/he" } }, + "node_modules/http-assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", + "dev": true, + "dependencies": { + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-assert/node_modules/deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", + "dev": true + }, "node_modules/http-errors": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", @@ -2520,6 +2679,80 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "dev": true, + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.13.4.tgz", + "integrity": "sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g==", + "dev": true, + "dependencies": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.8.0", + "debug": "^4.3.2", + "delegates": "^1.0.0", + "depd": "^2.0.0", + "destroy": "^1.0.4", + "encodeurl": "^1.0.2", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^2.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "engines": { + "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" + } + }, + "node_modules/koa-compose": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", + "dev": true + }, + "node_modules/koa-convert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", + "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", + "dev": true, + "dependencies": { + "co": "^4.6.0", + "koa-compose": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/koa/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -3236,6 +3469,15 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -3358,6 +3600,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3382,6 +3636,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/only": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", + "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==", + "dev": true + }, "node_modules/openapi3-ts": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-2.0.2.tgz", @@ -3471,6 +3731,15 @@ "node": ">=6" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -4275,6 +4544,15 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "devOptional": true }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "dev": true, + "engines": { + "node": ">=0.6.x" + } + }, "node_modules/type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -4383,6 +4661,15 @@ "node": ">= 0.10" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4657,6 +4944,15 @@ "node": ">=8" } }, + "node_modules/ylru": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.3.2.tgz", + "integrity": "sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -4767,12 +5063,6 @@ "raw-body": "^2.3.3", "type-is": "^1.6.16" } - }, - "koa-compose": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", - "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", - "dev": true } } }, @@ -4807,6 +5097,16 @@ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, "acorn": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", @@ -4993,6 +5293,16 @@ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true }, + "cache-content-type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", + "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", + "dev": true, + "requires": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + } + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -5160,6 +5470,12 @@ "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", "dev": true }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -5199,6 +5515,29 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -5208,6 +5547,24 @@ "safe-buffer": "~5.1.1" } }, + "cookies": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz", + "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==", + "dev": true, + "requires": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + } + } + }, "core-js": { "version": "2.6.11", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", @@ -5274,12 +5631,24 @@ "object-keys": "^1.1.1" } }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -5307,6 +5676,12 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -5342,6 +5717,12 @@ "core-js": "^2.0.0" } }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, "es-abstract": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz", @@ -5501,6 +5882,12 @@ } } }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "escodegen": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", @@ -6106,6 +6493,12 @@ "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", "dev": true }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6259,6 +6652,24 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "http-assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", + "dev": true, + "requires": { + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" + }, + "dependencies": { + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", + "dev": true + } + } + }, "http-errors": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", @@ -6587,6 +6998,70 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "dev": true, + "requires": { + "tsscmp": "1.0.6" + } + }, + "koa": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.13.4.tgz", + "integrity": "sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g==", + "dev": true, + "requires": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.8.0", + "debug": "^4.3.2", + "delegates": "^1.0.0", + "depd": "^2.0.0", + "destroy": "^1.0.4", + "encodeurl": "^1.0.2", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^2.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + } + } + }, + "koa-compose": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", + "dev": true + }, + "koa-convert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", + "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", + "dev": true, + "requires": { + "co": "^4.6.0", + "koa-compose": "^4.1.0" + } + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -7107,6 +7582,12 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -7189,6 +7670,15 @@ "es-abstract": "^1.19.1" } }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -7207,6 +7697,12 @@ "mimic-fn": "^4.0.0" } }, + "only": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", + "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==", + "dev": true + }, "openapi3-ts": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-2.0.2.tgz", @@ -7275,6 +7771,12 @@ "callsites": "^3.0.0" } }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -7905,6 +8407,12 @@ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "devOptional": true }, + "tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "dev": true + }, "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -7992,6 +8500,12 @@ "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", "optional": true }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8191,6 +8705,12 @@ } } }, + "ylru": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.3.2.tgz", + "integrity": "sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA==", + "dev": true + }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 1b3baaf..43b3d19 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "eslint-plugin-prettier": "^4.2.1", "husky": "^8.0.1", "intelli-espower-loader": "^1.1.0", + "koa": "^2.13.4", "lint-staged": "^13.0.3", "mocha": "^10.0.0", "power-assert": "^1.6.1", diff --git a/src/api.js b/src/api.js index 6a12733..d21ff9f 100644 --- a/src/api.js +++ b/src/api.js @@ -1,141 +1,137 @@ -'use strict'; - const assert = require('assert'); const cloneDeep = require('lodash/cloneDeep'); const get = require('lodash/get'); +const each = require('lodash/each'); const j2s = require('joi-to-swagger'); const generator = require('./generator'); class SwaggerAPI { - constructor() { - this.apiRoutes = []; - } - - /** - * Add a `koa-joi-router` router to the API. - * @param {Router} router - koa-joi-router instance - * @param {object} options - * @param {string} [options.prefix] - */ - addJoiRouter(router, options) { - options = options || {}; - - if (typeof options === 'string') { - options = {prefix: options}; - } - - if (!Array.isArray(router.routes)) { - throw new TypeError('router does not have exposed .routes array' + - ' (not a joi-router instance)'); + constructor() { + this.apiRoutes = []; } - const prefix = get(router, 'router.opts.prefix'); - router.routes.forEach(function(route) { - this.apiRoutes.push({ - route: route, - prefix: options.prefix || prefix - }); - }, this); - } - - /** - * Generate a Swagger 2.0 specification as an object for this API. - * - * @param {object} baseSpec - base document - * @param {object} baseSpec.info - * @param {string} baseSpec.info.title - * @param {string} baseSpec.info.version - * @param {object} baseSpec.tags - * @param {string} baseSpec.tags.name - * @param {string} baseSpec.tags.description - * @param {object} [options] - * @param {object} [options.warnFunc] - * @param {object} [options.defaultResponses] - * @param {object} [options.definitions] - * @returns {object} swagger 2.0 specification - */ - generateSpec(baseSpec, options) { - options = Object.assign({ - warnFunc: console.warn, - defaultResponses: { - 200: { - description: 'Success' - }, - 400: { - description: 'Bad Request' - }, - 500: { - description: 'Failure' + /** + * Add a `koa-joi-router` router to the API. + * @param {Router} router - koa-joi-router instance + * @param {object} options + * @param {string} [options.prefix] + */ + addJoiRouter(router, options) { + options = options || {}; + + if (typeof options === 'string') { + options = { prefix: options }; } - }, - }, options); - assert(baseSpec.info, 'baseSpec.info parameter missing'); - assert(baseSpec.info.title, 'baseSpec.info.title parameter missing'); - assert(baseSpec.info.version, 'baseSpec.info.version parameter missing'); + if (!Array.isArray(router.routes)) { + throw new TypeError('router does not have exposed .routes array (not a joi-router instance)'); + } - if (!options.defaultResponses) { - options.defaultResponses = {}; + const prefix = get(router, 'router.opts.prefix'); + router.routes.forEach(function addRoute(route) { + this.apiRoutes.push({ + route, + prefix: options.prefix || prefix, + }); + }, this); } - Object.freeze(options.defaultResponses); - - if (baseSpec.definitions) { - for (let key in baseSpec.definitions) { - const val = baseSpec.definitions[key]; - // if it's not a swagger object, convert it - if (val && val.type !== 'object' && !val.properties) { - baseSpec.definitions[key] = j2s(val).swagger; + /** + * Generate a Swagger 2.0 specification as an object for this API. + * + * @param {object} baseSpec - base document + * @param {object} baseSpec.info + * @param {string} baseSpec.info.title + * @param {string} baseSpec.info.version + * @param {object} baseSpec.tags + * @param {string} baseSpec.tags.name + * @param {string} baseSpec.tags.description + * @param {object} [options] + * @param {object} [options.warnFunc] + * @param {object} [options.defaultResponses] + * @param {object} [options.definitions] + * @returns {object} swagger 2.0 specification + */ + generateSpec(baseSpec, options) { + options = { + warnFunc: console.warn, + defaultResponses: { + 200: { + description: 'Success', + }, + 400: { + description: 'Bad Request', + }, + 500: { + description: 'Failure', + }, + }, + ...options, + }; + + assert(baseSpec.info, 'baseSpec.info parameter missing'); + assert(baseSpec.info.title, 'baseSpec.info.title parameter missing'); + assert(baseSpec.info.version, 'baseSpec.info.version parameter missing'); + + if (!options.defaultResponses) { + options.defaultResponses = {}; } - } - } - const doc = cloneDeep(baseSpec); - doc.swagger = '2.0'; - doc.paths = doc.paths || {}; - doc.tags = doc.tags || []; - - this.apiRoutes.forEach(function(apiRoute) { - const routeOptions = Object.assign({}, options, { - prefix: apiRoute.prefix - }); - - /** - * Caching All `class-validator` Schemas - */ - try { - require('reflect-metadata'); - const { getFromContainer, MetadataStorage } = require('class-validator'); - const { validationMetadatasToSchemas } = require('class-validator-jsonschema'); - const metadatas = (getFromContainer(MetadataStorage)).validationMetadatas; - const schemas = validationMetadatasToSchemas(metadatas); - if (schemas) { - for (let key in schemas) { - const schema = schemas[key]; - if (schema.required) { - each(schema.required, fieldKey => { - if (schema.properties && schema.properties[fieldKey]) { - schema.properties[fieldKey].required = true; + Object.freeze(options.defaultResponses); + + if (baseSpec.definitions) { + for (const key in baseSpec.definitions) { + const val = baseSpec.definitions[key]; + // if it's not a swagger object, convert it + if (val && val.type !== 'object' && !val.properties) { + baseSpec.definitions[key] = j2s(val).swagger; } - }); } - } - routeOptions.schemas = schemas; } - } catch (err) { - if (!/Cannot find module 'class-validator'/.test(err.message)) { - throw err; - } - } - const routePaths = generator.routeToSwaggerPaths(apiRoute.route, - routeOptions); + const doc = cloneDeep(baseSpec); + doc.swagger = '2.0'; + doc.paths = doc.paths || {}; + doc.tags = doc.tags || []; + + this.apiRoutes.forEach(function (apiRoute) { + const routeOptions = { ...options, prefix: apiRoute.prefix }; + + /** + * Caching All `class-validator` Schemas + */ + try { + require('reflect-metadata'); + const { getFromContainer, MetadataStorage } = require('class-validator'); + const { validationMetadatasToSchemas } = require('class-validator-jsonschema'); + const metadatas = getFromContainer(MetadataStorage).validationMetadatas; + const schemas = validationMetadatasToSchemas(metadatas); + if (schemas) { + for (const key in schemas) { + const schema = schemas[key]; + if (schema.required) { + each(schema.required, (fieldKey) => { + if (schema.properties && schema.properties[fieldKey]) { + schema.properties[fieldKey].required = true; + } + }); + } + } + routeOptions.schemas = schemas; + } + } catch (err) { + if (!/Cannot find module 'class-validator'/.test(err.message)) { + throw err; + } + } + + const routePaths = generator.routeToSwaggerPaths(apiRoute.route, routeOptions); - generator.mergeSwaggerPaths(doc.paths, routePaths, options); - }, this); + generator.mergeSwaggerPaths(doc.paths, routePaths, options); + }, this); - return doc; - } + return doc; + } } module.exports = SwaggerAPI; diff --git a/src/generator.js b/src/generator.js index e1f361b..9391d2d 100644 --- a/src/generator.js +++ b/src/generator.js @@ -1,6 +1,4 @@ -'use strict'; const pathToRegex = require('path-to-regexp'); - const j2s = require('joi-to-swagger'); const each = require('lodash/each'); const pick = require('lodash/pick'); @@ -11,52 +9,56 @@ const mapValues = require('lodash/mapValues'); const captureRegex = /(:\w+)/g; const JSON_SCHEMA_FIELDS = [ - 'type', 'required', - - 'maximum', 'exclusiveMaximum', 'minimum', 'exclusiveMinimum', - 'maxLength', 'minLength', /*'pattern',*/ 'maxItems', 'minItems', - 'uniqueItems', 'enum', 'multipleOf' -]; - -const JSON_SCHEMA_NESTED_FIELDS = [ - 'items', 'allOf', 'properties', 'additionalProperties' + 'type', + 'required', + + 'maximum', + 'exclusiveMaximum', + 'minimum', + 'exclusiveMinimum', + 'maxLength', + 'minLength', + /* 'pattern', */ 'maxItems', + 'minItems', + 'uniqueItems', + 'enum', + 'multipleOf', ]; -function isRouter(router) { - return router.routes && router.use; -} - exports.mergeSwaggerPaths = function mergeSwaggerPaths(paths, newPaths, options) { - const warn = options && options.warnFunc; + const warn = options && options.warnFunc; - each(newPaths, function(newPathItemObj, path) { - let pathItemObj = paths[path] = paths[path] || {}; + each(newPaths, function (newPathItemObj, path) { + const pathItemObj = (paths[path] = paths[path] || {}); - // Merge operations into path - each(newPathItemObj, function(operationObj, method) { - if (pathItemObj[method]) { - // already exists! - if(warn) warn(`${path}[${method}] exists in multiple routes`); - return; - } + // Merge operations into path + each(newPathItemObj, function (operationObj, method) { + if (pathItemObj[method]) { + // already exists! + if (warn) { + warn(`${path}[${method}] exists in multiple routes`); + } + + return; + } - pathItemObj[method] = operationObj; + pathItemObj[method] = operationObj; + }); }); - }); - return paths; + return paths; }; exports.routesToSwaggerPaths = function routesToSwaggerPaths(routes, options) { - let paths = {}; + const paths = {}; - routes.forEach(function(route) { - let routePaths = exports.routeToSwaggerPaths(route, options); + routes.forEach(function (route) { + const routePaths = exports.routeToSwaggerPaths(route, options); - exports.mergeSwaggerPaths(paths, routePaths, options); - }); + exports.mergeSwaggerPaths(paths, routePaths, options); + }); - return paths; + return paths; }; /** @@ -65,196 +67,198 @@ exports.routesToSwaggerPaths = function routesToSwaggerPaths(routes, options) { * @returns {object} paths */ exports.routeToSwaggerPaths = function routeToSwaggerPaths(route, options) { - options = options || {}; - - let paths = {}; - let routeDesc = { - responses: {} - }; - - if (!route.validate && route.handler && options.schemas) { - classValidatorToSwagger(route, options.schemas); - } - - if (route.validate) { - let type = route.validate.type; - - if (!type) { - // do nothing - } else if (type === 'json') { - routeDesc.consumes = ['application/json']; - } else if (type === 'form') { - routeDesc.consumes = ['application/x-www-form-urlencoded']; - } else if (type === 'multipart') { - routeDesc.consumes = ['multipart/form-data']; + options = options || {}; + + const paths = {}; + const routeDesc = { + responses: {}, + }; + + if (!route.validate && route.handler && options.schemas) { + classValidatorToSwagger(route, options.schemas); } - routeDesc.parameters = exports.validateToSwaggerParameters(route.validate); - - // add responses from output schema - let output = route.output || route.validate.output; - if (output) { - routeDesc.responses = Object.assign({}, options.defaultResponses); - Object.keys(output).forEach(x => { - x = x.toString(); - // 200,206,300 - let respCodes = x.split(','); - respCodes.forEach(code => { - // 200-299 - if (code.includes('-')) { - code = code.split('-')[0]; - } - code = code.trim(); - routeDesc.responses[code] = Object.assign({ - description: 'Success', - }, options.defaultResponses[code], outputToSwagger(output[x])); - }); - }); + if (route.validate) { + const type = route.validate.type; + + if (!type) { + // do nothing + } else if (type === 'json') { + routeDesc.consumes = ['application/json']; + } else if (type === 'form') { + routeDesc.consumes = ['application/x-www-form-urlencoded']; + } else if (type === 'multipart') { + routeDesc.consumes = ['multipart/form-data']; + } + + routeDesc.parameters = exports.validateToSwaggerParameters(route.validate); + + // add responses from output schema + const output = route.output || route.validate.output; + if (output) { + routeDesc.responses = { ...options.defaultResponses }; + Object.keys(output).forEach((x) => { + x = x.toString(); + // 200,206,300 + const respCodes = x.split(','); + respCodes.forEach((code) => { + // 200-299 + if (code.includes('-')) { + code = code.split('-')[0]; + } + code = code.trim(); + routeDesc.responses[code] = { + description: 'Success', + ...options.defaultResponses[code], + ...outputToSwagger(output[x]), + }; + }); + }); + } } - } - - if (route.meta && route.meta.swagger) { - Object.assign(routeDesc, route.meta.swagger); - } - - // This sets default 'path' parameters so swagger-ui doesn't complain. - let noPathParamsExist = !some(routeDesc.parameters, ['in', 'path']); - let noPathValidatorExists = get(route, 'validate.path') === undefined; - let noParamsValidatorExists = get(route, 'validate.params') === undefined; - if(noPathParamsExist && noPathValidatorExists && noParamsValidatorExists){ - routeDesc.parameters = routeDesc.parameters || []; - let pathCaptures = route.path.match(captureRegex); - if (pathCaptures) { - each(pathCaptures, function(pathParameter){ - routeDesc.parameters.push({ - name: pathParameter.replace(':',''), - in: 'path', - type: 'string', - required: true - }); - }); + + if (route.meta && route.meta.swagger) { + Object.assign(routeDesc, route.meta.swagger); + } + + // This sets default 'path' parameters so swagger-ui doesn't complain. + const noPathParamsExist = !some(routeDesc.parameters, ['in', 'path']); + const noPathValidatorExists = get(route, 'validate.path') === undefined; + const noParamsValidatorExists = get(route, 'validate.params') === undefined; + if (noPathParamsExist && noPathValidatorExists && noParamsValidatorExists) { + routeDesc.parameters = routeDesc.parameters || []; + const pathCaptures = route.path.match(captureRegex); + if (pathCaptures) { + each(pathCaptures, function (pathParameter) { + routeDesc.parameters.push({ + name: pathParameter.replace(':', ''), + in: 'path', + type: 'string', + required: true, + }); + }); + } } - } - let path = exports.swaggerizePath(route.path); + let path = exports.swaggerizePath(route.path); - if (options.prefix) { - if (options.prefix.endsWith('/') || path.startsWith('/')) { - path = `${options.prefix}${path}`; - } else { - path = `${options.prefix}/${path}`; + if (options.prefix) { + if (options.prefix.endsWith('/') || path.startsWith('/')) { + path = `${options.prefix}${path}`; + } else { + path = `${options.prefix}/${path}`; + } } - } - let pathItemObj = paths[path] = {}; + const pathItemObj = (paths[path] = {}); - let methods = Array.isArray(route.method) ? route.method : [route.method]; + const methods = Array.isArray(route.method) ? route.method : [route.method]; - methods.forEach(function (method) { - let operationObj = routeDesc; - pathItemObj[method.toLowerCase()] = operationObj; - }); + methods.forEach(function (method) { + const operationObj = routeDesc; + pathItemObj[method.toLowerCase()] = operationObj; + }); - return paths; + return paths; }; function addSchemaParameters(parameters, location, schema) { - let swaggerObject = schema.swagger || j2s(schema).swagger; - if (swaggerObject.type === 'object' && swaggerObject.properties) { - each(swaggerObject.properties, function(value, name) { - if (value.type === 'string' && value.format === 'binary') { - value.type = 'file'; - delete value.format; - } - let parameter = value; - parameter.name = name; - parameter.in = location; - parameters.push(parameter); - }); - } + const swaggerObject = schema.swagger || j2s(schema).swagger; + if (swaggerObject.type === 'object' && swaggerObject.properties) { + each(swaggerObject.properties, function (value, name) { + if (value.type === 'string' && value.format === 'binary') { + value.type = 'file'; + delete value.format; + } + const parameter = value; + parameter.name = name; + parameter.in = location; + parameters.push(parameter); + }); + } } -function outputToSwagger (respJson) { - let obj = {}; - if (respJson && respJson.body) { - const respBody = respJson.body; - obj.schema = typeof respJson.ref === 'string' - ? {'$ref': respJson.ref} - : respBody.swagger || j2s(respBody).swagger; - if (respJson.schema || obj.schema.description) { - obj.description = respJson.schema || obj.schema.description; +function outputToSwagger(respJson) { + const obj = {}; + if (respJson && respJson.body) { + const respBody = respJson.body; + obj.schema = + typeof respJson.ref === 'string' ? { $ref: respJson.ref } : respBody.swagger || j2s(respBody).swagger; + if (respJson.schema || obj.schema.description) { + obj.description = respJson.schema || obj.schema.description; + } } - } - return obj; + + return obj; } /** * Convert a JSON schema object to the subset used by Swagger */ -exports.jsonSchemaToSwagger = function(jsonSchema) { - if (Array.isArray(jsonSchema)) { - return jsonSchema.map(exports.jsonSchemaToSwagger); - } +exports.jsonSchemaToSwagger = function jsonSchemaToSwagger(jsonSchema) { + if (Array.isArray(jsonSchema)) { + return jsonSchema.map(exports.jsonSchemaToSwagger); + } - let schema = pick(jsonSchema, JSON_SCHEMA_FIELDS); + const schema = pick(jsonSchema, JSON_SCHEMA_FIELDS); - if (jsonSchema.items) { - // FIXME HACK - if (Array.isArray(jsonSchema.items)) { - jsonSchema.items = jsonSchema.items[0]; - } + if (jsonSchema.items) { + // FIXME HACK + if (Array.isArray(jsonSchema.items)) { + jsonSchema.items = jsonSchema.items[0]; + } - schema.items = exports.jsonSchemaToSwagger(jsonSchema.items); - } + schema.items = exports.jsonSchemaToSwagger(jsonSchema.items); + } - if (jsonSchema.properties) { - schema.properties = mapValues(jsonSchema.properties, function(value) { - return exports.jsonSchemaToSwagger(value); - }); - } + if (jsonSchema.properties) { + schema.properties = mapValues(jsonSchema.properties, function (value) { + return exports.jsonSchemaToSwagger(value); + }); + } - return schema; + return schema; }; /** * Convert joi-router validate object to swagger */ -exports.validateToSwaggerParameters = function(validate) { - let parameters = []; - - if (validate.header) { - addSchemaParameters(parameters, 'header', validate.header); - } - - if (validate.query) { - addSchemaParameters(parameters, 'query', validate.query); - } - - if (validate.path) { - // Still there for anyone who uses old syntax - addSchemaParameters(parameters, 'path', validate.path); - } - - if (validate.params) { - addSchemaParameters(parameters, 'path', validate.params); - } - - if (validate.formData) { - addSchemaParameters(parameters, 'formData', validate.formData); - } - - if (validate.body) { - const schema = validate.body; - let swaggerSchema = typeof validate.ref === 'string' - ? {'$ref': validate.ref} - : schema.swagger || j2s(schema).swagger; - parameters.push({ - name: 'body', - in: 'body', - schema: swaggerSchema - }); - } - return parameters; +exports.validateToSwaggerParameters = function validateToSwaggerParameters(validate) { + const parameters = []; + + if (validate.header) { + addSchemaParameters(parameters, 'header', validate.header); + } + + if (validate.query) { + addSchemaParameters(parameters, 'query', validate.query); + } + + if (validate.path) { + // Still there for anyone who uses old syntax + addSchemaParameters(parameters, 'path', validate.path); + } + + if (validate.params) { + addSchemaParameters(parameters, 'path', validate.params); + } + + if (validate.formData) { + addSchemaParameters(parameters, 'formData', validate.formData); + } + + if (validate.body) { + const schema = validate.body; + const swaggerSchema = + typeof validate.ref === 'string' ? { $ref: validate.ref } : schema.swagger || j2s(schema).swagger; + parameters.push({ + name: 'body', + in: 'body', + schema: swaggerSchema, + }); + } + + return parameters; }; /* Convert a joi-router path into a Swagger parameterized path, @@ -263,74 +267,82 @@ exports.validateToSwaggerParameters = function(validate) { * FIXME: incomplete handling, escaping, etc * FIXME: throw error if a complex regex is used */ -exports.swaggerizePath = function swaggerizePath(path, options) { - let pathTokens = pathToRegex.parse(path); +exports.swaggerizePath = function swaggerizePath(path) { + const pathTokens = pathToRegex.parse(path); - let segments = pathTokens.map(function (token) { - let segment = token; + const segments = pathTokens.map(function (token) { + let segment = token; - if (token.name) { - segment = `{${token.name}}`; //this means this is a complex regex group. for more info, read up on path-to-regexp, koa-joi-router uses it, it's great. - } else { - segment = token.replace('/',''); //remove leading slash, to handle things like complex routes: /users/:userId/friends/:friendId - } + if (token.name) { + segment = `{${token.name}}`; // this means this is a complex regex group. for more info, read up on path-to-regexp, koa-joi-router uses it, it's great. + } else { + segment = token.replace('/', ''); // remove leading slash, to handle things like complex routes: /users/:userId/friends/:friendId + } - return segment; - }); + return segment; + }); - return '/'+segments.join('/'); //path is normalized, just add that leading slash back to handle prefixes properly again. + return `/${segments.join('/')}`; // path is normalized, just add that leading slash back to handle prefixes properly again. }; -function classValidatorToSwagger (route, schemas) { - const handler = get(route, 'handler.0'); - if (!handler) return; - if (!schemas || schemas.length) return; - try { - // route --> Controller & method - const clazz = handler.__class__; - const method = handler.__method__; - if (!clazz || !method) return; - - // get configs from @decorators - const docs = Reflect.getMetadata('Docs', clazz, method); - const input = Reflect.getMetadata('ValidateInput', clazz, method); - const output = Reflect.getMetadata('ValidateOutput', clazz, method); - - route.validate = {}; - if (docs) { - route.meta = {swagger: docs}; - } else { - route.meta = {swagger: { summary: route.path }}; +function classValidatorToSwagger(route, schemas) { + const handler = get(route, 'handler.0'); + if (!handler) { + return; } + if (!schemas || schemas.length) { + return; + } + try { + // route --> Controller & method + const clazz = handler.__class__; + const method = handler.__method__; + if (!clazz || !method) { + return; + } - if (input) { - route.validate.type = input.type; - - // class --> class-validator-jsonschema --> swagger schema - each(['query', 'params', 'body'], key => { - const clazz = input[key]; - if (clazz) { - const schema = schemas[clazz.name]; - if (schema) { - route.validate[key] = {swagger: schema}; - } + // get configs from @decorators + const docs = Reflect.getMetadata('Docs', clazz, method); + const input = Reflect.getMetadata('ValidateInput', clazz, method); + const output = Reflect.getMetadata('ValidateOutput', clazz, method); + + route.validate = {}; + if (docs) { + route.meta = { swagger: docs }; + } else { + route.meta = { swagger: { summary: route.path } }; } - }); - } - if (output) { - route.validate.output = {}; - for (let key in output) { - const schema = schemas[output[key].name]; - if (schema) { - route.validate.output[key] = {body: { - swagger: schema - }}; + if (input) { + route.validate.type = input.type; + + // class --> class-validator-jsonschema --> swagger schema + each(['query', 'params', 'body'], (key) => { + const clazz = input[key]; + if (clazz) { + const schema = schemas[clazz.name]; + if (schema) { + route.validate[key] = { swagger: schema }; + } + } + }); } - } + + if (output) { + route.validate.output = {}; + for (const key in output) { + const schema = schemas[output[key].name]; + if (schema) { + route.validate.output[key] = { + body: { + swagger: schema, + }, + }; + } + } + } + } catch (err) { + // eslint-disable-next-line no-console + console.warn(`[joi-router-docs] Fail to generate docs for \`${route.path}\`, reason: `, err); } - } catch (err) { - console.warn(`[joi-router-docs] Fail to generate docs for \`${route.path}\`, reason: `, err); - return; - } -} \ No newline at end of file +} diff --git a/src/index.js b/src/index.js index 0ad477f..1f6fdae 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1 @@ -'use strict'; - exports.SwaggerAPI = require('./api'); diff --git a/test/test-api.js b/test/test-api.js index a8aa1d8..f44c522 100644 --- a/test/test-api.js +++ b/test/test-api.js @@ -1,198 +1,193 @@ const assert = require('power-assert'); const Router = require('@koa-better-modules/joi-router'); -const Joi = Router.Joi; - -const { SwaggerAPI } = require('../'); - -describe('API', function () { - it('should success with valid data', function () { - const generator = new SwaggerAPI(); - const router = new Router(); - - router.get('/signup', { - meta: { - swagger: { - summary: 'User Signup' - } - }, - validate: { - type: 'json', - body: { - username: Joi.string().alphanum().min(3).max(30).required() - }, - output: { - 200: { - body: { - userId: Joi.string().description('Newly created user id') - } - } - } - }, - handler: () => { - } - }); - - generator.addJoiRouter(router); - const spec = generator.generateSpec({ - info: { - title: 'Example API', - version: '1.1' - }, - basePath: '/' - }); - assert(['info', 'basePath', 'swagger', 'paths', 'tags'].every(v => v in spec)); - }); - - it('should success with empty default response', function () { - const generator = new SwaggerAPI(); - const router = new Router(); - - router.get('/empty-default-response', { - validate: { - output: { - 201: { - body: { - ok: Joi.bool() - } - } - } - }, - handler: () => { - } - }); +const { SwaggerAPI } = require('../src'); - generator.addJoiRouter(router); - const spec = generator.generateSpec({ - info: { - title: 'Example API', - version: '1.1' - }, - basePath: '/' - }, { defaultResponses: null }); - assert(!('200' in spec.paths['/empty-default-response'].get.responses)); - }); - - it('should success with output placed outside of validate', function () { - const generator = new SwaggerAPI(); - const router = new Router(); - - router.get('/output-outside-validate', { - validate: { - output: { - 201: { - body: { - ok: Joi.bool() - } - } - } - }, - handler: () => { - } - }); +const Joi = Router.Joi; - generator.addJoiRouter(router); - const spec = generator.generateSpec({ - info: { - title: 'Example API', - version: '1.1' - }, - basePath: '/' - }); - assert( - ['200', '201'].every(v => v in spec.paths['/output-outside-validate'].get.responses) - ); - }); - - it('should consider the router prefix', function () { - const generator = new SwaggerAPI(); - const router = new Router(); - router.prefix('/api'); - - router.get('/signup', { - meta: { - swagger: { - summary: 'User Signup' - } - }, - validate: {}, - handler: () => { - } +describe('API', () => { + it('should success with valid data', () => { + const generator = new SwaggerAPI(); + const router = new Router(); + + router.get('/signup', { + meta: { + swagger: { + summary: 'User Signup', + }, + }, + validate: { + type: 'json', + body: { + username: Joi.string().alphanum().min(3).max(30).required(), + }, + output: { + 200: { + body: { + userId: Joi.string().description('Newly created user id'), + }, + }, + }, + }, + handler: () => {}, + }); + + generator.addJoiRouter(router); + const spec = generator.generateSpec({ + info: { + title: 'Example API', + version: '1.1', + }, + basePath: '/', + }); + assert(['info', 'basePath', 'swagger', 'paths', 'tags'].every((v) => v in spec)); }); - generator.addJoiRouter(router); - const spec = generator.generateSpec({ - info: { - title: 'Example API', - version: '1.1' - }, - basePath: '/' - }); - assert(['/api/signup'].every(r => r in spec.paths)); - }); - - it('should consider the prefix option over JoiRouter', function () { - const generator = new SwaggerAPI(); - const router = new Router(); - router.prefix('/api'); - - router.get('/signup', { - meta: { - swagger: { - summary: 'User Signup' - } - }, - validate: {}, - handler: () => { - } + it('should success with empty default response', () => { + const generator = new SwaggerAPI(); + const router = new Router(); + + router.get('/empty-default-response', { + validate: { + output: { + 201: { + body: { + ok: Joi.bool(), + }, + }, + }, + }, + handler: () => {}, + }); + + generator.addJoiRouter(router); + const spec = generator.generateSpec( + { + info: { + title: 'Example API', + version: '1.1', + }, + basePath: '/', + }, + { defaultResponses: null } + ); + assert(!('200' in spec.paths['/empty-default-response'].get.responses)); }); - generator.addJoiRouter(router, { prefix: '/other-api' }); - const spec = generator.generateSpec({ - info: { - title: 'Example API', - version: '1.1' - }, - basePath: '/' + it('should success with output placed outside of validate', () => { + const generator = new SwaggerAPI(); + const router = new Router(); + + router.get('/output-outside-validate', { + validate: { + output: { + 201: { + body: { + ok: Joi.bool(), + }, + }, + }, + }, + handler: () => {}, + }); + + generator.addJoiRouter(router); + const spec = generator.generateSpec({ + info: { + title: 'Example API', + version: '1.1', + }, + basePath: '/', + }); + assert(['200', '201'].every((v) => v in spec.paths['/output-outside-validate'].get.responses)); }); - assert(['/other-api/signup'].every(r => r in spec.paths)); - }); - - it('should return $ref with references', function () { - const generator = new SwaggerAPI(); - const router = new Router(); - const ProfileJoi = Joi.object({ - profileName: Joi.string(), + it('should consider the router prefix', () => { + const generator = new SwaggerAPI(); + const router = new Router(); + router.prefix('/api'); + + router.get('/signup', { + meta: { + swagger: { + summary: 'User Signup', + }, + }, + validate: {}, + handler: () => {}, + }); + + generator.addJoiRouter(router); + const spec = generator.generateSpec({ + info: { + title: 'Example API', + version: '1.1', + }, + basePath: '/', + }); + assert(['/api/signup'].every((r) => r in spec.paths)); }); - router.get('/signup', { - validate: { - type: 'json', - body: ProfileJoi, - ref: '#/definitions/Profile', - output: { - 200: { - body: ProfileJoi, - ref: '#/definitions/Profile', - } - } - }, - handler: () => { - } + it('should consider the prefix option over JoiRouter', () => { + const generator = new SwaggerAPI(); + const router = new Router(); + router.prefix('/api'); + + router.get('/signup', { + meta: { + swagger: { + summary: 'User Signup', + }, + }, + validate: {}, + handler: () => {}, + }); + + generator.addJoiRouter(router, { prefix: '/other-api' }); + const spec = generator.generateSpec({ + info: { + title: 'Example API', + version: '1.1', + }, + basePath: '/', + }); + assert(['/other-api/signup'].every((r) => r in spec.paths)); }); - generator.addJoiRouter(router); - const spec = generator.generateSpec({ - info: { - title: 'Example API', - version: '1.1' - }, - basePath: '/', - definitions: { - Profile: ProfileJoi - }, + it('should return $ref with references', () => { + const generator = new SwaggerAPI(); + const router = new Router(); + + const ProfileJoi = Joi.object({ + profileName: Joi.string(), + }); + + router.get('/signup', { + validate: { + type: 'json', + body: ProfileJoi, + ref: '#/definitions/Profile', + output: { + 200: { + body: ProfileJoi, + ref: '#/definitions/Profile', + }, + }, + }, + handler: () => {}, + }); + + generator.addJoiRouter(router); + const spec = generator.generateSpec({ + info: { + title: 'Example API', + version: '1.1', + }, + basePath: '/', + definitions: { + Profile: ProfileJoi, + }, + }); + assert(spec.paths['/signup'].get.parameters[0].schema.$ref === '#/definitions/Profile'); + assert(spec.paths['/signup'].get.responses[200].schema.$ref === '#/definitions/Profile'); }); - assert(spec.paths['/signup'].get.parameters[0].schema.$ref === '#/definitions/Profile'); - assert(spec.paths['/signup'].get.responses[200].schema.$ref === '#/definitions/Profile'); - }); });