UNPKG

fastify-swagger

Version:

Serve Swagger/OpenAPI documentation for Fastify, supporting dynamic generation

576 lines (494 loc) 14.9 kB
'use strict' const t = require('tap') const test = t.test const Fastify = require('fastify') const Swagger = require('swagger-parser') const yaml = require('js-yaml') const fastifySwagger = require('../index') const { schemaQuerystring, schemaBody, schemaParams, schemaSecurity } = require('../examples/options') let { swaggerOption } = require('../examples/options') const resolve = require('path').resolve const readFileSync = require('fs').readFileSync swaggerOption = { ...swaggerOption, exposeRoute: true } const schemaParamsWithoutDesc = { schema: { params: { type: 'object', properties: { id: { type: 'string' } } } } } const schemaParamsWithKey = { schema: { params: { type: 'object', properties: { id: { type: 'string', description: 'user id' }, key: { type: 'string', description: 'just some random key' } } } } } test('/documentation/json route', t => { t.plan(2) const fastify = Fastify() fastify.register(fastifySwagger, swaggerOption) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.inject({ method: 'GET', url: '/documentation/json' }, (err, res) => { t.error(err) const payload = JSON.parse(res.payload) Swagger.validate(payload) .then(function (api) { t.pass('valid swagger object') }) .catch(function (err) { t.fail(err) }) }) }) test('/documentation/uiConfig route', t => { t.plan(2) const fastify = Fastify() const uiConfig = { docExpansion: 'full' } const opts = { ...swaggerOption, uiConfig } fastify.register(fastifySwagger, opts) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.inject({ method: 'GET', url: '/documentation/uiConfig' }, (err, res) => { t.error(err) const payload = JSON.parse(res.payload) t.match(payload, uiConfig, 'uiConfig should be valid') }) }) test('/documentation/initOAuth route', t => { t.plan(2) const fastify = Fastify() const initOAuth = { scopes: ['openid', 'profile', 'email', 'offline_access'] } const opts = { ...swaggerOption, initOAuth } fastify.register(fastifySwagger, opts) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.inject({ method: 'GET', url: '/documentation/initOAuth' }, (err, res) => { t.error(err) const payload = JSON.parse(res.payload) t.match(payload, initOAuth, 'initOAuth should be valid') }) }) test('fastify.swagger should return a valid swagger yaml', t => { t.plan(4) const fastify = Fastify() fastify.register(fastifySwagger, swaggerOption) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.all('/parametersWithoutDesc/:id', schemaParamsWithoutDesc, () => {}) fastify.inject({ method: 'GET', url: '/documentation/yaml' }, (err, res) => { t.error(err) t.equal(typeof res.payload, 'string') t.equal(res.headers['content-type'], 'application/x-yaml') try { yaml.load(res.payload) t.pass('valid swagger yaml') } catch (err) { t.fail(err) } }) }) test('/documentation should redirect to ./documentation/static/index.html', t => { t.plan(4) const fastify = Fastify() fastify.register(fastifySwagger, swaggerOption) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.inject({ method: 'GET', url: '/documentation' }, (err, res) => { t.error(err) t.equal(res.statusCode, 302) t.equal(res.headers.location, './documentation/static/index.html') t.equal(typeof res.payload, 'string') }) }) test('/documentation/ should redirect to ./static/index.html', t => { t.plan(4) const fastify = Fastify() fastify.register(fastifySwagger, swaggerOption) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.inject({ method: 'GET', url: '/documentation/' }, (err, res) => { t.error(err) t.equal(res.statusCode, 302) t.equal(res.headers.location, './static/index.html') t.equal(typeof res.payload, 'string') }) }) test('/v1/documentation should redirect to ./documentation/static/index.html', t => { t.plan(4) const fastify = Fastify() const opts = JSON.parse(JSON.stringify(swaggerOption)) opts.routePrefix = '/v1/documentation' fastify.register(fastifySwagger, opts) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.inject({ method: 'GET', url: '/v1/documentation' }, (err, res) => { t.error(err) t.equal(res.statusCode, 302) t.equal(res.headers.location, './documentation/static/index.html') t.equal(typeof res.payload, 'string') }) }) test('/v1/documentation/ should redirect to ./static/index.html', t => { t.plan(4) const fastify = Fastify() const opts = JSON.parse(JSON.stringify(swaggerOption)) opts.routePrefix = '/v1/documentation' fastify.register(fastifySwagger, opts) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.inject({ method: 'GET', url: '/v1/documentation/' }, (err, res) => { t.error(err) t.equal(res.statusCode, 302) t.equal(res.headers.location, './static/index.html') t.equal(typeof res.payload, 'string') }) }) test('/v1/foobar should redirect to ./foobar/static/index.html - in plugin', t => { t.plan(4) const fastify = Fastify() fastify.register(function (fastify, options, next) { const opts = JSON.parse(JSON.stringify(swaggerOption)) opts.routePrefix = '/foobar' fastify.register(fastifySwagger, opts) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) next() }, { prefix: '/v1' }) fastify.inject({ method: 'GET', url: '/v1/foobar' }, (err, res) => { t.error(err) t.equal(res.statusCode, 302) t.equal(res.headers.location, './foobar/static/index.html') t.equal(typeof res.payload, 'string') }) }) test('/v1/foobar/ should redirect to ./static/index.html - in plugin', t => { t.plan(4) const fastify = Fastify() fastify.register(function (fastify, options, next) { const opts = JSON.parse(JSON.stringify(swaggerOption)) opts.routePrefix = '/foobar' fastify.register(fastifySwagger, opts) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) next() }, { prefix: '/v1' }) fastify.inject({ method: 'GET', url: '/v1/foobar/' }, (err, res) => { t.error(err) t.equal(res.statusCode, 302) t.equal(res.headers.location, './static/index.html') t.equal(typeof res.payload, 'string') }) }) test('with routePrefix: \'/\' should redirect to ./static/index.html', t => { t.plan(4) const fastify = Fastify() const opts = JSON.parse(JSON.stringify(swaggerOption)) opts.routePrefix = '/' fastify.register(fastifySwagger, opts) fastify.get('/foo', () => {}) fastify.inject({ method: 'GET', url: '/' }, (err, res) => { t.error(err) t.equal(res.statusCode, 302) t.equal(res.headers.location, './static/index.html') t.equal(typeof res.payload, 'string') }) }) test('/documentation/static/:file should send back the correct file', t => { t.plan(24) const fastify = Fastify() fastify.register(fastifySwagger, swaggerOption) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.inject({ method: 'GET', url: '/documentation/' }, (err, res) => { t.error(err) t.equal(res.statusCode, 302) t.equal(res.headers.location, './static/index.html') }) fastify.ready(() => { fastify.inject({ method: 'GET', url: '/documentation/static/' }, (err, res) => { t.error(err) t.equal(typeof res.payload, 'string') t.equal(res.headers['content-type'], 'text/html; charset=UTF-8') t.equal( readFileSync( resolve(__dirname, '..', 'static', 'index.html'), 'utf8' ), res.payload ) t.ok(res.payload.indexOf('resolveUrl') !== -1) }) }) fastify.inject({ method: 'GET', url: '/documentation/static/oauth2-redirect.html' }, (err, res) => { t.error(err) t.equal(typeof res.payload, 'string') t.equal(res.headers['content-type'], 'text/html; charset=UTF-8') t.equal( readFileSync( resolve(__dirname, '..', 'static', 'oauth2-redirect.html'), 'utf8' ), res.payload ) }) fastify.inject({ method: 'GET', url: '/documentation/static/swagger-ui.css' }, (err, res) => { t.error(err) t.equal(typeof res.payload, 'string') t.equal(res.headers['content-type'], 'text/css; charset=UTF-8') t.equal( readFileSync( resolve(__dirname, '..', 'static', 'swagger-ui.css'), 'utf8' ), res.payload ) }) fastify.inject({ method: 'GET', url: '/documentation/static/swagger-ui-bundle.js' }, (err, res) => { t.error(err) t.equal(typeof res.payload, 'string') t.equal(res.headers['content-type'], 'application/javascript; charset=UTF-8') t.equal( readFileSync( resolve(__dirname, '..', 'static', 'swagger-ui-bundle.js'), 'utf8' ), res.payload ) }) fastify.inject({ method: 'GET', url: '/documentation/static/swagger-ui-standalone-preset.js' }, (err, res) => { t.error(err) t.equal(typeof res.payload, 'string') t.equal(res.headers['content-type'], 'application/javascript; charset=UTF-8') t.equal( readFileSync( resolve(__dirname, '..', 'static', 'swagger-ui-standalone-preset.js'), 'utf8' ), res.payload ) }) }) test('/documentation/static/:file 404', t => { t.plan(3) const fastify = Fastify() fastify.register(fastifySwagger, swaggerOption) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.inject({ method: 'GET', url: '/documentation/static/stuff.css' }, (err, res) => { t.error(err) const payload = JSON.parse(res.payload) t.equal(res.statusCode, 404) t.match(payload, { error: 'Not Found', statusCode: 404 }) }) }) test('/documentation2/json route (overwrite)', t => { t.plan(2) const fastify = Fastify() const swaggerOptionWithRouteOverwrite = JSON.parse(JSON.stringify(swaggerOption)) swaggerOptionWithRouteOverwrite.routePrefix = '/documentation2' fastify.register(fastifySwagger, swaggerOptionWithRouteOverwrite) fastify.get('/', () => {}) fastify.post('/', () => {}) fastify.get('/example', schemaQuerystring, () => {}) fastify.post('/example', schemaBody, () => {}) fastify.get('/parameters/:id', schemaParams, () => {}) fastify.get('/example1', schemaSecurity, () => {}) fastify.get('/parameters/:id/:key', schemaParamsWithKey, () => {}) fastify.inject({ method: 'GET', url: '/documentation2/json' }, (err, res) => { t.error(err) const payload = JSON.parse(res.payload) Swagger.validate(payload) .then(function (api) { t.pass('valid swagger object') }) .catch(function (err) { t.fail(err) }) }) }) test('/documentation/:myfile should return 404 in dynamic mode', t => { t.plan(2) const fastify = Fastify() fastify.register(fastifySwagger, swaggerOption) fastify.inject({ method: 'GET', url: '/documentation/swagger-ui.js' }, (err, res) => { t.error(err) t.equal(res.statusCode, 404) }) }) test('/documentation/:myfile should run custom NotFoundHandler in dynamic mode', t => { t.plan(2) const fastify = Fastify() const notFoundHandler = function (req, reply) { reply.code(410).send() } fastify.setNotFoundHandler(notFoundHandler) fastify.register(fastifySwagger, swaggerOption) fastify.inject({ method: 'GET', url: '/documentation/swagger-ui.js' }, (err, res) => { t.error(err) t.equal(res.statusCode, 410) }) }) test('/documentation/ should redirect to ./static/index.html', t => { t.plan(3) const fastify = Fastify() fastify.register(fastifySwagger, swaggerOption) fastify.inject({ method: 'GET', url: '/documentation/' }, (err, res) => { t.error(err) t.equal(res.statusCode, 302) t.equal(res.headers.location, './static/index.html') }) })