├── mocha.opts ├── .travis.yml ├── .npmignore ├── .editorconfig ├── .istanbul.yml ├── .gitignore ├── .github ├── issue_template.md ├── pull_request_template.md └── contributing.md ├── LICENSE ├── example ├── app.js └── docs.html ├── test └── index.test.js ├── package.json ├── lib ├── utils.js └── index.js ├── README.md └── CHANGELOG.md /mocha.opts: -------------------------------------------------------------------------------- 1 | --recursive test/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - 8 5 | - 6 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .jshintrc 3 | .travis.yml 4 | .istanbul.yml 5 | .babelrc 6 | .idea/ 7 | .vscode/ 8 | test/ 9 | coverage/ 10 | .github/ 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | verbose: false 2 | instrumentation: 3 | root: ./lib/ 4 | include-all-sources: true 5 | reporting: 6 | print: summary 7 | reports: 8 | - html 9 | - text 10 | - lcov 11 | watermarks: 12 | statements: [50, 80] 13 | lines: [50, 80] 14 | functions: [50, 80] 15 | branches: [50, 80] 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # Compiled binary addons (http://nodejs.org/api/addons.html) 22 | build/Release 23 | 24 | # Dependency directory 25 | # Commenting this out is preferred by some people, see 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 27 | node_modules 28 | 29 | # Users Environment Variables 30 | .lock-wscript 31 | 32 | dist/ 33 | /nbproject/ 34 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ### Steps to reproduce 2 | 3 | (First please check that this issue is not already solved as [described 4 | here](https://github.com/feathersjs/feathers/blob/master/.github/contributing.md#report-a-bug)) 5 | 6 | - [ ] Tell us what broke. The more detailed the better. 7 | - [ ] If you can, please create a simple example that reproduces the issue and link to a gist, jsbin, repo, etc. 8 | 9 | ### Expected behavior 10 | Tell us what should happen 11 | 12 | ### Actual behavior 13 | Tell us what happens instead 14 | 15 | ### System configuration 16 | 17 | Tell us about the applicable parts of your setup. 18 | 19 | **Module versions** (especially the part that's not working): 20 | 21 | **NodeJS version**: 22 | 23 | **Operating System**: 24 | 25 | **Browser Version**: 26 | 27 | **React Native Version**: 28 | 29 | **Module Loader**: -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Summary 2 | 3 | (If you have not already please refer to the contributing guideline as [described 4 | here](https://github.com/feathersjs/feathers/blob/master/.github/contributing.md#pull-requests)) 5 | 6 | - [ ] Tell us about the problem your pull request is solving. 7 | - [ ] Are there any open issues that are related to this? 8 | - [ ] Is this PR dependent on PRs in other repos? 9 | 10 | If so, please mention them to keep the conversations linked together. 11 | 12 | ### Other Information 13 | 14 | If there's anything else that's important and relevant to your pull 15 | request, mention that information here. This could include 16 | benchmarks, or other information. 17 | 18 | Your PR will be reviewed by a core team member and they will work with you to get your changes merged in a timely manner. If merged your PR will automatically be added to the changelog in the next release. 19 | 20 | If your changes involve documentation updates please mention that and link the appropriate PR in [feathers-docs](https://github.com/feathersjs/feathers-docs). 21 | 22 | Thanks for contributing to Feathers! :heart: -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Feathers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | const feathers = require('feathers'); 2 | const rest = require('feathers-rest'); 3 | const memory = require('feathers-memory'); 4 | const path = require('path'); 5 | const bodyParser = require('body-parser'); 6 | const swagger = require('../lib'); 7 | const messageService = memory(); 8 | 9 | messageService.docs = { 10 | description: 'A service to send and receive messages', 11 | definitions: { 12 | messages: { 13 | 'type': 'object', 14 | 'required': [ 15 | 'text' 16 | ], 17 | 'properties': { 18 | 'text': { 19 | 'type': 'string', 20 | 'description': 'The message text' 21 | }, 22 | 'userId': { 23 | 'type': 'string', 24 | 'description': 'The id of the user that send the message' 25 | } 26 | } 27 | }, 28 | 'messages list': { 29 | 'type': 'array' 30 | } 31 | } 32 | }; 33 | 34 | const serveStatic = require('serve-static'); 35 | const distPath = require.resolve('swagger-ui-dist'); 36 | 37 | // alternatively point to local file: 38 | // const uiIndex = path.join(__dirname, 'docs.html') 39 | const uiIndex = path.join(__dirname, 'docs.html'); 40 | 41 | const app = feathers() 42 | .use(bodyParser.json()) 43 | .use(bodyParser.urlencoded({ 44 | extended: true 45 | })) 46 | .use(serveStatic(distPath)) 47 | .configure(rest()) 48 | 49 | .configure(swagger({ 50 | docsPath: '/docs', 51 | uiIndex, 52 | info: { 53 | title: 'A test', 54 | description: 'A description' 55 | } 56 | })) 57 | .use('/messages', messageService); 58 | 59 | console.log('Simple feathers-swagger example running on http://localhost:3030/docs/'); 60 | 61 | app.listen(3030); 62 | -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-expressions */ 2 | const { expect } = require('chai'); 3 | 4 | const buzzard = require('@feathersjs/feathers'); 5 | const expressify = require('@feathersjs/express'); 6 | const feathers = require('feathers'); 7 | const rest = require('feathers-rest'); 8 | const memory = require('feathers-memory'); 9 | const rp = require('request-promise'); 10 | const swagger = require('../lib'); 11 | 12 | describe('feathers-swagger', () => { 13 | describe('basic functionality', () => { 14 | let server; 15 | 16 | before(done => { 17 | const app = feathers() 18 | .configure(rest()) 19 | .configure( 20 | swagger({ 21 | docsPath: '/docs', 22 | info: { 23 | title: 'A test', 24 | description: 'A description' 25 | }, 26 | idType: 'string' 27 | }) 28 | ) 29 | .use('/messages', memory()); 30 | 31 | server = app.listen(6776, () => done()); 32 | }); 33 | 34 | after(done => server.close(done)); 35 | 36 | it('supports basic functionality with a simple app', () => { 37 | return rp({ 38 | url: 'http://localhost:6776/docs', 39 | json: true 40 | }).then(docs => { 41 | expect(docs.info.title).to.equal('A test'); 42 | expect(docs.info.description).to.equal('A description'); 43 | expect(docs.paths['/messages']).to.exist; 44 | }); 45 | }); 46 | 47 | it('supports id types in config', () => { 48 | return rp({ 49 | url: 'http://localhost:6776/docs', 50 | json: true 51 | }).then(docs => { 52 | const messagesIdParam = docs.paths['/messages/{id}'].get.parameters[0]; 53 | expect(messagesIdParam.type).to.equal('string'); 54 | }); 55 | }); 56 | }); 57 | 58 | describe('support for Buzzard', () => { 59 | it('should support Buzzard provider syntax', () => { 60 | const app = expressify(buzzard()) 61 | .configure(swagger({})) 62 | .use('/messages', memory()); 63 | 64 | return new Promise((resolve, reject) => { 65 | const server = app.listen(9001, () => { 66 | resolve(server.close()); 67 | }); 68 | }); 69 | }); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "feathers-swagger", 3 | "description": "Add documentation to your Featherjs services and feed them to Swagger UI.", 4 | "version": "0.7.2", 5 | "homepage": "https://github.com/feathersjs-ecosystem/feathers-swagger", 6 | "main": "lib/", 7 | "keywords": [ 8 | "feathers", 9 | "feathers-plugin" 10 | ], 11 | "license": "MIT", 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/feathersjs-ecosystem/feathers-swagger.git" 15 | }, 16 | "author": { 17 | "name": "Feathers contributors", 18 | "email": "hello@feathersjs.com", 19 | "url": "https://feathersjs.com" 20 | }, 21 | "contributors": [], 22 | "bugs": { 23 | "url": "https://github.com/feathersjs-ecosystem/feathers-swagger/issues" 24 | }, 25 | "engines": { 26 | "node": ">= 6" 27 | }, 28 | "scripts": { 29 | "publish": "git push origin --tags && npm run changelog && git push origin", 30 | "release:patch": "npm version patch && npm publish", 31 | "release:minor": "npm version minor && npm publish", 32 | "release:major": "npm version major && npm publish", 33 | "changelog": "github_changelog_generator && git add CHANGELOG.md && git commit -am \"Updating changelog\"", 34 | "lint": "semistandard --fix", 35 | "mocha": "mocha --opts mocha.opts", 36 | "coverage": "istanbul cover node_modules/mocha/bin/_mocha -- --opts mocha.opts", 37 | "test": "npm run lint && npm run coverage", 38 | "start": "npm run compile && node example/simple/app" 39 | }, 40 | "semistandard": { 41 | "sourceType": "module", 42 | "env": [ 43 | "mocha" 44 | ] 45 | }, 46 | "directories": { 47 | "lib": "lib" 48 | }, 49 | "greenkeeper": { 50 | "ignore": [ 51 | "swagger-ui" 52 | ] 53 | }, 54 | "dependencies": { 55 | "debug": "^4.0.0", 56 | "feathers-errors": "^2.5.0", 57 | "serve-static": "^1.12.4", 58 | "swagger-ui": "^3.2.0", 59 | "swagger-ui-dist": "^3.2.0" 60 | }, 61 | "devDependencies": { 62 | "@feathersjs/express": "^1.1.1", 63 | "@feathersjs/feathers": "^3.0.0", 64 | "body-parser": "^1.15.2", 65 | "chai": "^4.0.0", 66 | "cors": "^2.8.1", 67 | "feathers": "^2.0.2", 68 | "feathers-authentication": "^1.0.2", 69 | "feathers-memory": "^2.0.0", 70 | "feathers-rest": "^1.5.2", 71 | "feathers-sequelize": "^3.0.0", 72 | "istanbul": "^1.1.0-alpha.1", 73 | "mocha": "^5.0.0", 74 | "request": "^2.79.0", 75 | "request-promise": "^4.1.1", 76 | "semistandard": "^12.0.0" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /example/docs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Swagger UI 7 | 8 | 9 | 10 | 11 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
69 | 70 | 71 | 72 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | exports.getType = function getType (type) { 2 | switch (type) { 3 | case 'STRING': 4 | case 'CHAR': 5 | case 'TEXT': 6 | case 'BLOB': 7 | case 'DATE': 8 | case 'DATEONLY': 9 | case 'TIME': 10 | case 'NOW': 11 | return 'string'; 12 | case 'INTEGER': 13 | case 'BIGINT': 14 | return 'integer'; 15 | case 'FLOAT': 16 | case 'DOUBLE': 17 | case 'DECIMAL': 18 | return 'number'; 19 | case 'BOOLEAN': 20 | return 'boolean'; 21 | case 'ARRAY': 22 | return 'array'; 23 | default: 24 | return ''; 25 | } 26 | }; 27 | 28 | exports.getFormat = function getFormat (type) { 29 | switch (type) { 30 | case 'INTEGER': 31 | case 'DECIMAL': 32 | return 'int32'; 33 | case 'BIGINT': 34 | return 'int64'; 35 | case 'FLOAT': 36 | return 'float'; 37 | case 'DOUBLE': 38 | return 'double'; 39 | case 'DATE': 40 | case 'DATEONLY': 41 | return 'date'; 42 | case 'TIME': 43 | case 'NOW': 44 | return 'date-time'; 45 | default: 46 | return ''; 47 | } 48 | }; 49 | 50 | exports.property = function property (type, items) { 51 | const result = { 52 | type: exports.getType(type), 53 | format: exports.getFormat(type) 54 | }; 55 | 56 | if (type === 'ARRAY') { 57 | const isUndefined = typeof items === 'undefined'; 58 | const isString = typeof items === 'string'; 59 | 60 | if (isUndefined) { 61 | result.items = { type: exports.getType('INTEGER') }; 62 | } else if (isString) { 63 | result.items = { '$ref': '#/definitions/' + items }; 64 | } else { 65 | result.items = { type: exports.getType(items.key) }; 66 | } 67 | } 68 | 69 | return result; 70 | }; 71 | 72 | exports.definition = function definition (model, options = { type: 'object' }) { 73 | const result = { 74 | type: options.type, 75 | properties: {} 76 | }; 77 | const keys = typeof model.attributes !== 'undefined' ? Object.keys(model.attributes) : []; 78 | 79 | keys.forEach(function (attrName) { 80 | const attr = model.attributes[attrName]; 81 | const attrType = typeof attr.key !== 'undefined' ? attr.key : attr.type.constructor.prototype.key; 82 | const prop = exports.property(attrType, model.attributes[attrName].type); 83 | 84 | result.properties[attrName] = prop; 85 | }); 86 | 87 | const allOf = (options.extends || []).map(item => { 88 | return { 89 | '$ref': '#definitions/' + item 90 | }; 91 | }); 92 | 93 | allOf.push(result); 94 | 95 | return { 96 | description: options.description, 97 | allOf 98 | }; 99 | }; 100 | 101 | exports.tag = function tag (name, options = {}) { 102 | return { 103 | name, 104 | description: options.description || `A ${name} service`, 105 | externalDocs: options.externalDocs || {} 106 | }; 107 | }; 108 | 109 | exports.operation = function operation (method, service, defaults = {}) { 110 | const operation = Object.assign(service.docs[method] || {}, service[method].docs || {}); 111 | 112 | operation.parameters = operation.parameters || defaults.parameters || []; 113 | operation.responses = operation.responses || defaults.responses || {}; 114 | operation.description = operation.description || defaults.description || ''; 115 | operation.summary = operation.summary || defaults.summary || ''; 116 | operation.tags = operation.tags || defaults.tags || []; 117 | operation.consumes = operation.consumes || defaults.consumes || []; 118 | operation.produces = operation.produces || defaults.produces || []; 119 | operation.security = operation.security || defaults.security || {}; 120 | operation.securityDefinitions = operation.securityDefinitions || defaults.securityDefinitions || {}; 121 | // Clean up 122 | delete service.docs[method]; // Remove `find` from `docs` 123 | 124 | return operation; 125 | }; 126 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Feathers 2 | 3 | Thank you for contributing to Feathers! :heart: :tada: 4 | 5 | This repo is the main core and where most issues are reported. Feathers embraces modularity and is broken up across many repos. To make this easier to manage we currently use [Zenhub](https://www.zenhub.com/) for issue triage and visibility. They have a free browser plugin you can install so that you can see what is in flight at any time, but of course you also always see current issues in Github. 6 | 7 | ## Report a bug 8 | 9 | Before creating an issue please make sure you have checked out the docs, specifically the [FAQ](https://docs.feathersjs.com/help/faq.html) section. You might want to also try searching Github. It's pretty likely someone has already asked a similar question. 10 | 11 | If you haven't found your answer please feel free to join our [slack channel](http://slack.feathersjs.com), create an issue on Github, or post on [Stackoverflow](http://stackoverflow.com) using the `feathers` or `feathersjs` tag. We try our best to monitor Stackoverflow but you're likely to get more immediate responses in Slack and Github. 12 | 13 | Issues can be reported in the [issue tracker](https://github.com/feathersjs/feathers/issues). Since feathers combines many modules it can be hard for us to assess the root cause without knowing which modules are being used and what your configuration looks like, so **it helps us immensely if you can link to a simple example that reproduces your issue**. 14 | 15 | ## Report a Security Concern 16 | 17 | We take security very seriously at Feathers. We welcome any peer review of our 100% open source code to ensure nobody's Feathers app is ever compromised or hacked. As a web application developer you are responsible for any security breaches. We do our very best to make sure Feathers is as secure as possible by default. 18 | 19 | In order to give the community time to respond and upgrade we strongly urge you report all security issues to us. Send one of the core team members a PM in [Slack](http://slack.feathersjs.com) or email us at hello@feathersjs.com with details and we will respond ASAP. 20 | 21 | For full details refer to our [Security docs](https://docs.feathersjs.com/SECURITY.html). 22 | 23 | ## Pull Requests 24 | 25 | We :heart: pull requests and we're continually working to make it as easy as possible for people to contribute, including a [Plugin Generator](https://github.com/feathersjs/generator-feathers-plugin) and a [common test suite](https://github.com/feathersjs/feathers-service-tests) for database adapters. 26 | 27 | We prefer small pull requests with minimal code changes. The smaller they are the easier they are to review and merge. A core team member will pick up your PR and review it as soon as they can. They may ask for changes or reject your pull request. This is not a reflection of you as an engineer or a person. Please accept feedback graciously as we will also try to be sensitive when providing it. 28 | 29 | Although we generally accept many PRs they can be rejected for many reasons. We will be as transparent as possible but it may simply be that you do not have the same context or information regarding the roadmap that the core team members have. We value the time you take to put together any contributions so we pledge to always be respectful of that time and will try to be as open as possible so that you don't waste it. :smile: 30 | 31 | **All PRs (except documentation) should be accompanied with tests and pass the linting rules.** 32 | 33 | ### Code style 34 | 35 | Before running the tests from the `test/` folder `npm test` will run ESlint. You can check your code changes individually by running `npm run lint`. 36 | 37 | ### ES6 compilation 38 | 39 | Feathers uses [Babel](https://babeljs.io/) to leverage the latest developments of the JavaScript language. All code and samples are currently written in ES2015. To transpile the code in this repository run 40 | 41 | > npm run compile 42 | 43 | __Note:__ `npm test` will run the compilation automatically before the tests. 44 | 45 | ### Tests 46 | 47 | [Mocha](http://mochajs.org/) tests are located in the `test/` folder and can be run using the `npm run mocha` or `npm test` (with ESLint and code coverage) command. 48 | 49 | ### Documentation 50 | 51 | Feathers documentation is contained in Markdown files in the [feathers-docs](https://github.com/feathersjs/feathers-docs) repository. To change the documentation submit a pull request to that repo, referencing any other PR if applicable, and the docs will be updated with the next release. 52 | 53 | ## External Modules 54 | 55 | If you're written something awesome for Feathers, the Feathers ecosystem, or using Feathers please add it to the [showcase](https://docs.feathersjs.com/why/showcase.html). You also might want to check out the [Plugin Generator](https://github.com/feathersjs/generator-feathers-plugin) that can be used to scaffold plugins to be Feathers compliant from the start. 56 | 57 | If you think it would be a good core module then please contact one of the Feathers core team members in [Slack](http://slack.feathersjs.com) and we can discuss whether it belongs in core and how to get it there. :beers: 58 | 59 | ## Contributor Code of Conduct 60 | 61 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 62 | 63 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 64 | 65 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 66 | 67 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 68 | 69 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 70 | 71 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 72 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const url = require('url'); 3 | const serveStatic = require('serve-static'); 4 | const utils = require('./utils'); 5 | 6 | // Find node_modules root path 7 | let modulesRootPath = require.resolve('swagger-ui-dist'); 8 | modulesRootPath = modulesRootPath.substr(0, modulesRootPath.lastIndexOf('node_modules')); 9 | 10 | const init = module.exports = function (config) { 11 | return function () { 12 | const app = this; 13 | 14 | // Apply configuration 15 | const rootDoc = Object.assign({ 16 | paths: {}, 17 | definitions: {}, 18 | swagger: '2.0', 19 | schemes: ['http'], 20 | tags: [], 21 | basePath: '', 22 | docsPath: '/docs', 23 | consumes: ['application/json'], 24 | produces: ['application/json'], 25 | info: {}, 26 | ignore: { 27 | tags: [] 28 | } 29 | }, config || {}); 30 | 31 | const docsPath = rootDoc.docsPath; 32 | 33 | // Create API for Documentation 34 | app.get(docsPath, function (req, res) { 35 | res.format({ 36 | 'application/json': function () { 37 | res.json(rootDoc); 38 | }, 39 | 40 | 'text/html': function () { 41 | const parsed = url.parse(req.url); 42 | const pathname = parsed.pathname; 43 | 44 | if (pathname[pathname.length - 1] !== '/') { 45 | parsed.pathname = `${req.baseUrl}${pathname}/`; 46 | return res.redirect(301, url.format(parsed)); 47 | } 48 | 49 | if (typeof config.uiIndex === 'function') { 50 | config.uiIndex(req, res); 51 | } else if (typeof config.uiIndex === 'string') { 52 | res.sendFile(config.uiIndex); 53 | } else if (config.uiIndex === true) { 54 | if (req.query.url) { 55 | res.sendFile(path.join(modulesRootPath, 'node_modules/swagger-ui-dist/index.html')); 56 | } else { 57 | // Set swagger url (needed for default UI) 58 | res.redirect('?url=' + encodeURI(config.docsPath)); 59 | } 60 | } else { 61 | res.json(rootDoc); 62 | } 63 | } 64 | }); 65 | }); 66 | 67 | if (typeof config.uiIndex !== 'undefined') { 68 | const uiPath = path.dirname(require.resolve('swagger-ui-dist')); 69 | app.use(docsPath, serveStatic(uiPath)); 70 | } 71 | 72 | app.docs = rootDoc; 73 | 74 | const configurePlugin = function (service, path) { 75 | service.docs = service.docs || {}; 76 | 77 | // Load documentation from service, if available. 78 | const doc = service.docs; 79 | const idName = service.id || 'id'; 80 | const idType = doc.idType || config.idType || 'integer'; 81 | let version = config.versionPrefix ? path.match(config.versionPrefix) : null; 82 | version = version ? ' ' + version[0] : ''; 83 | const apiPath = path.replace(config.prefix, ''); 84 | const group = apiPath.split('/'); 85 | const tag = (apiPath.indexOf('/') > -1 ? group[0] : apiPath) + version; 86 | const model = apiPath.indexOf('/') > -1 ? group[1] : apiPath; 87 | const security = []; 88 | 89 | if (rootDoc.security && Array.isArray(rootDoc.security)) { 90 | rootDoc.security.forEach(function (schema) { 91 | security.push(schema); 92 | }); 93 | } 94 | 95 | if (rootDoc.ignore.tags.indexOf(tag) > -1) { 96 | return; 97 | } 98 | 99 | const pathObj = rootDoc.paths; 100 | const withIdKey = `/${path}/{${idName}}`; 101 | const withoutIdKey = `/${path}`; 102 | const securities = doc.securities || []; 103 | 104 | if (typeof doc.definition !== 'undefined') { 105 | rootDoc.definitions[tag] = doc.definition; 106 | rootDoc.definitions[`${tag} list`] = { 107 | type: 'array', 108 | items: doc.definition 109 | }; 110 | } 111 | if (typeof doc.definitions !== 'undefined') { 112 | rootDoc.definitions = Object.assign(rootDoc.definitions, doc.definitions); 113 | } 114 | 115 | // FIND 116 | if (typeof service.find === 'function') { 117 | let parameters = [ 118 | { 119 | description: "Number of results to return", 120 | in: "query", 121 | name: "$limit", 122 | type: "integer" 123 | }, 124 | { 125 | description: "Number of results to skip", 126 | in: "query", 127 | name: "$skip", 128 | type: "integer" 129 | }, 130 | { 131 | description: "Property to sort results", 132 | in: "query", 133 | name: "$sort", 134 | type: "string" 135 | } 136 | ]; 137 | if ( 138 | rootDoc.findQueryParameters !== undefined && 139 | rootDoc.findQueryParameters.length > 0 140 | ) { 141 | parameters = parameters.filter( 142 | parametersItem => 143 | !rootDoc.findQueryParameters.find( 144 | findQueryParameters => 145 | parametersItem.name === findQueryParameters.name 146 | ) 147 | ); 148 | parameters = rootDoc.findQueryParameters.concat(parameters); 149 | } 150 | pathObj[withoutIdKey] = pathObj[withoutIdKey] || {}; 151 | pathObj[withoutIdKey].get = utils.operation('find', service, { 152 | tags: [tag], 153 | description: 'Retrieves a list of all resources from the service.', 154 | parameters, 155 | responses: { 156 | '200': { 157 | description: 'success', 158 | schema: { 159 | '$ref': '#/definitions/' + `${tag} list` 160 | } 161 | }, 162 | '500': { 163 | description: 'general error' 164 | }, 165 | '401': { 166 | description: 'not authenticated' 167 | } 168 | }, 169 | produces: rootDoc.produces, 170 | consumes: rootDoc.consumes, 171 | security: securities.indexOf('find') > -1 ? security : {} 172 | }); 173 | } 174 | 175 | // GET 176 | if (typeof service.get === 'function') { 177 | pathObj[withIdKey] = pathObj[withIdKey] || {}; 178 | pathObj[withIdKey].get = utils.operation('get', service, { 179 | tags: [tag], 180 | description: 'Retrieves a single resource with the given id from the service.', 181 | parameters: [{ 182 | description: `ID of ${model} to return`, 183 | in: 'path', 184 | required: true, 185 | name: idName, 186 | type: idType 187 | }], 188 | responses: { 189 | '200': { 190 | description: 'success', 191 | schema: { 192 | '$ref': '#/definitions/' + tag 193 | } 194 | }, 195 | '500': { 196 | description: 'general error' 197 | }, 198 | '401': { 199 | description: 'not authenticated' 200 | }, 201 | '404': { 202 | description: 'not found' 203 | } 204 | }, 205 | produces: rootDoc.produces, 206 | consumes: rootDoc.consumes, 207 | security: securities.indexOf('get') > -1 ? security : {} 208 | }); 209 | } 210 | 211 | // CREATE 212 | if (typeof service.create === 'function') { 213 | pathObj[withoutIdKey] = pathObj[withoutIdKey] || {}; 214 | pathObj[withoutIdKey].post = utils.operation('create', service, { 215 | tags: [tag], 216 | description: 'Creates a new resource with data.', 217 | parameters: [{ in: 'body', 218 | name: 'body', 219 | required: true, 220 | schema: { 221 | '$ref': '#/definitions/' + tag 222 | } 223 | }], 224 | responses: { 225 | '201': { 226 | description: 'created' 227 | }, 228 | '500': { 229 | description: 'general error' 230 | }, 231 | '401': { 232 | description: 'not authenticated' 233 | } 234 | }, 235 | produces: rootDoc.produces, 236 | consumes: rootDoc.consumes, 237 | security: securities.indexOf('create') > -1 ? security : {} 238 | }); 239 | } 240 | 241 | // UPDATE 242 | if (typeof service.update === 'function') { 243 | pathObj[withIdKey] = pathObj[withIdKey] || {}; 244 | pathObj[withIdKey].put = utils.operation('update', service, { 245 | tags: [tag], 246 | description: 'Updates the resource identified by id using data.', 247 | parameters: [{ 248 | description: 'ID of ' + model + ' to return', 249 | in: 'path', 250 | required: true, 251 | name: idName, 252 | type: idType 253 | }, { in: 'body', 254 | name: 'body', 255 | required: true, 256 | schema: { 257 | '$ref': '#/definitions/' + tag 258 | } 259 | }], 260 | responses: { 261 | '200': { 262 | description: 'success', 263 | schema: { 264 | '$ref': '#/definitions/' + tag 265 | } 266 | }, 267 | '500': { 268 | description: 'general error' 269 | }, 270 | '401': { 271 | description: 'not authenticated' 272 | }, 273 | '404': { 274 | description: 'not found' 275 | } 276 | }, 277 | produces: rootDoc.produces, 278 | consumes: rootDoc.consumes, 279 | security: securities.indexOf('update') > -1 ? security : {} 280 | }); 281 | } 282 | 283 | // PATCH 284 | if (typeof service.patch === 'function') { 285 | pathObj[withIdKey] = pathObj[withIdKey] || {}; 286 | pathObj[withIdKey].patch = utils.operation('patch', service, { 287 | tags: [tag], 288 | description: 'Updates the resource identified by id using data.', 289 | parameters: [{ 290 | description: 'ID of ' + model + ' to return', 291 | in: 'path', 292 | required: true, 293 | name: idName, 294 | type: idType 295 | }, { in: 'body', 296 | name: 'body', 297 | required: true, 298 | schema: { 299 | '$ref': '#/definitions/' + tag 300 | } 301 | }], 302 | responses: { 303 | '200': { 304 | description: 'success', 305 | schema: { 306 | '$ref': '#/definitions/' + tag 307 | } 308 | }, 309 | '500': { 310 | description: 'general error' 311 | }, 312 | '401': { 313 | description: 'not authenticated' 314 | }, 315 | '404': { 316 | description: 'not found' 317 | } 318 | }, 319 | produces: rootDoc.produces, 320 | consumes: rootDoc.consumes, 321 | security: securities.indexOf('patch') > -1 ? security : {} 322 | }); 323 | } 324 | 325 | // REMOVE 326 | if (typeof service.remove === 'function') { 327 | pathObj[withIdKey] = pathObj[withIdKey] || {}; 328 | pathObj[withIdKey].delete = utils.operation('remove', service, { 329 | tags: [tag], 330 | description: 'Removes the resource with id.', 331 | parameters: [{ 332 | description: 'ID of ' + model + ' to return', 333 | in: 'path', 334 | required: true, 335 | name: idName, 336 | type: idType 337 | }], 338 | responses: { 339 | '200': { 340 | description: 'success', 341 | schema: { 342 | '$ref': '#/definitions/' + tag 343 | } 344 | }, 345 | '500': { 346 | description: 'general error' 347 | }, 348 | '401': { 349 | description: 'not authenticated' 350 | }, 351 | '404': { 352 | description: 'not found' 353 | } 354 | }, 355 | produces: rootDoc.produces, 356 | consumes: rootDoc.consumes, 357 | security: securities.indexOf('remove') > -1 ? security : {} 358 | }); 359 | } 360 | 361 | rootDoc.paths = pathObj; 362 | 363 | const existingTag = rootDoc.tags.find(item => item.name === tag); 364 | if (!existingTag) { 365 | rootDoc.tags.push(utils.tag(tag, doc)); 366 | } else { 367 | Object.assign(existingTag, utils.tag(tag, doc)); 368 | } 369 | }; 370 | 371 | // Optional: Register this plugin as a Feathers provider 372 | if (app.version && parseInt(app.version, 10) >= 3) { 373 | app.mixins.push(configurePlugin); 374 | } else { 375 | app.providers.push((path, service) => configurePlugin(service, path)); 376 | } 377 | }; 378 | }; 379 | 380 | Object.assign(init, utils); 381 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # feathers-swagger 2 | 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/feathersjs-ecosystem/feathers-swagger.svg)](https://greenkeeper.io/) 4 | 5 | [![Build Status](https://travis-ci.org/feathersjs-ecosystem/feathers-swagger.png?branch=master)](https://travis-ci.org/feathersjs-ecosystem/feathers-swagger) 6 | [![Dependency Status](https://img.shields.io/david/feathersjs-ecosystem/feathers-swagger.svg?style=flat-square)](https://david-dm.org/feathersjs-ecosystem/feathers-swagger) 7 | [![Download Status](https://img.shields.io/npm/dm/feathers-swagger.svg?style=flat-square)](https://www.npmjs.com/package/feathers-swagger) 8 | 9 | > Add documentation to your Featherjs services and show them in the Swagger ui. 10 | 11 | This version is configured to work with Swagger UI 3.x 12 | 13 | ## Installation 14 | 15 | ```shell 16 | npm install feathers-swagger --save 17 | ``` 18 | 19 | ## Examples 20 | 21 | > npm install feathers feathers-rest feathers-memory feathers-swagger body-parser 22 | 23 | ### Basic example 24 | 25 | Here's an example of a Feathers server that uses `feathers-swagger`. 26 | 27 | ```js 28 | const feathers = require('feathers'); 29 | const rest = require('feathers-rest'); 30 | const memory = require('feathers-memory'); 31 | const bodyParser = require('body-parser'); 32 | const swagger = require('feathers-swagger'); 33 | 34 | const messageService = memory(); 35 | 36 | // swagger spec for this service, see http://swagger.io/specification/ 37 | messageService.docs = { 38 | description: 'A service to send and receive messages', 39 | definitions: { 40 | messages: { 41 | "type": "object", 42 | "required": [ 43 | "text" 44 | ], 45 | "properties": { 46 | "text": { 47 | "type": "string", 48 | "description": "The message text" 49 | }, 50 | "useId": { 51 | "type": "string", 52 | "description": "The id of the user that sent the message" 53 | } 54 | } 55 | } 56 | } 57 | }; 58 | 59 | const app = feathers() 60 | 61 | .use(bodyParser.json()) 62 | .use(bodyParser.urlencoded({ extended: true })) 63 | .configure(rest()) 64 | .configure(swagger({ 65 | docsPath: '/docs', 66 | info: { 67 | title: 'A test', 68 | description: 'A description' 69 | } 70 | })) 71 | .use('/messages', messageService); 72 | 73 | app.listen(3030); 74 | ``` 75 | 76 | Go to to see the Swagger JSON documentation. 77 | 78 | ### Example with Feathers Generate app 79 | 1. Go into your `src/services/` folder, and open the service you want to edit `PATH.service.js` 80 | 2. Change from this: 81 | ```js 82 | // Initialize our service with any options it requires 83 | app.use('/events', createService(options)); 84 | ``` 85 | to this: 86 | ```js 87 | const events = createService(options) 88 | events.docs = { 89 | //overwrite things here. 90 | //if we want to add a mongoose style $search hook to find, we can write this: 91 | find: { 92 | parameters: [ 93 | { 94 | description: 'Number of results to return', 95 | in: 'query', 96 | name: '$limit', 97 | type: 'integer' 98 | }, 99 | { 100 | description: 'Number of results to skip', 101 | in: 'query', 102 | name: '$skip', 103 | type: 'integer' 104 | }, 105 | { 106 | description: 'Property to sort results', 107 | in: 'query', 108 | name: '$sort', 109 | type: 'string' 110 | }, 111 | { 112 | description: 'Property to query results', 113 | in: 'query', 114 | name: '$search', 115 | type: 'string' 116 | } 117 | ] 118 | }, 119 | //if we want to add the mongoose model to the 'definitions' so it is a named model in the swagger ui: 120 | definitions: { 121 | event: mongooseToJsonLibraryYouImport(Model) //import your own library, use the 'Model' object in this file. 122 | 'event list': { //this library currently configures the return documentation to look for ``${tag} list` 123 | type: 'array', 124 | items: { $ref: '#/definitions/event' } 125 | } 126 | } 127 | } 128 | app.use('/events', events) 129 | ``` 130 | 131 | The overrides work at a property level - if you pass in find.parameters, that whole object will be used, it is not merged in. 132 | you can find more information in the utils.js file to get an idea of what is passed in. 133 | 134 | 135 | ### Example with UI 136 | 137 | The `uiIndex` option allows to set a [Swagger UI](http://swagger.io/swagger-ui/) index file which will host the UI at `docsPath`. 138 | 139 | ```js 140 | const path = require('path'); 141 | const feathers = require('feathers'); 142 | const rest = require('feathers-rest'); 143 | const memory = require('feathers-memory'); 144 | const bodyParser = require('body-parser'); 145 | const swagger = require('feathers-swagger'); 146 | 147 | const messageService = memory(); 148 | 149 | messageService.docs = { 150 | description: 'A service to send and receive messages', 151 | definitions: { 152 | messages: { 153 | "type": "object", 154 | "required": [ 155 | "text" 156 | ], 157 | "properties": { 158 | "text": { 159 | "type": "string", 160 | "description": "The message text" 161 | }, 162 | "useId": { 163 | "type": "string", 164 | "description": "The id of the user that send the message" 165 | } 166 | } 167 | } 168 | } 169 | }; 170 | 171 | const app = feathers() 172 | .use(bodyParser.json()) 173 | .use(bodyParser.urlencoded({ extended: true })) 174 | .configure(rest()) 175 | .configure(swagger({ 176 | docsPath: '/docs', 177 | uiIndex: path.join(__dirname, 'docs.html'), 178 | info: { 179 | title: 'A test', 180 | description: 'A description' 181 | } 182 | })) 183 | .use('/messages', messageService); 184 | 185 | app.listen(3030); 186 | ``` 187 | 188 | Create a `docs.html` page like this: 189 | 190 | ```html 191 | 192 | 193 | 194 | 195 | 196 | 197 | Swagger UI - Simple 198 | 200 | 201 | 202 | 203 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 235 | 236 | 237 | 238 | 240 | 241 | 242 | 243 | 245 | 246 | 247 | 248 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 |
265 | 266 | 270 | 274 | 338 | 339 | 340 | 341 | ``` 342 | 343 | Now will show the documentation in the browser using the Swagger UI. 344 | 345 | You can also use `uiIndex: true` to use the default [Swagger UI](http://swagger.io/swagger-ui/). 346 | 347 | ### Prefixed routes 348 | 349 | If your are using versioned or prefixed routes for your API like `/api//users`, you can configure it using the 350 | `prefix` property so all your services don't end up in the same group. The value of the `prefix` property can be either 351 | a string or a RegEx. 352 | 353 | ```js 354 | const app = feathers() 355 | .use(bodyParser.json()) 356 | .use(bodyParser.urlencoded({ extended: true })) 357 | .configure(rest()) 358 | .configure(swagger({ 359 | prefix: /api\/v\d\//, 360 | docsPath: '/docs', 361 | info: { 362 | title: 'A test', 363 | description: 'A description' 364 | } 365 | })) 366 | .use('/api/v1/messages', messageService); 367 | 368 | app.listen(3030); 369 | ``` 370 | 371 | To display your API version alongside the service name, you can also define a `versionPrefix` to be extracted: 372 | ```js 373 | const app = feathers() 374 | .use(bodyParser.json()) 375 | .use(bodyParser.urlencoded({ extended: true })) 376 | .configure(rest()) 377 | .configure(swagger({ 378 | prefix: /api\/v\d\//, 379 | versionPrefix: /v\d/, 380 | docsPath: '/docs', 381 | info: { 382 | title: 'A test', 383 | description: 'A description' 384 | } 385 | })) 386 | .use('/api/v1/messages', messageService); 387 | 388 | app.listen(3030); 389 | ``` 390 | 391 | ## License 392 | 393 | Copyright (c) 2016 - 2018 394 | 395 | Licensed under the [MIT license](LICENSE). 396 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v0.7.2](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.7.2) (2018-07-24) 4 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.7.1...v0.7.2) 5 | 6 | **Closed issues:** 7 | 8 | - Codeclimate links and badges in readme are dead [\#103](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/103) 9 | 10 | **Merged pull requests:** 11 | 12 | - API key authentication bug fixed, no more "undefined" security object. [\#115](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/115) ([ivanmarjanovic](https://github.com/ivanmarjanovic)) 13 | - added findQueryParameters [\#114](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/114) ([xFuryMaxHD](https://github.com/xFuryMaxHD)) 14 | 15 | ## [v0.7.1](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.7.1) (2018-06-04) 16 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.7.0...v0.7.1) 17 | 18 | **Closed issues:** 19 | 20 | - Log statement in the code: Remove it? [\#110](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/110) 21 | 22 | **Merged pull requests:** 23 | 24 | - Removed ui integration log statement [\#111](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/111) ([andys8](https://github.com/andys8)) 25 | - fix\(documentation\): Codeclimate badges [\#105](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/105) ([andys8](https://github.com/andys8)) 26 | 27 | ## [v0.7.0](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.7.0) (2018-05-29) 28 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.5.1...v0.7.0) 29 | 30 | ## [v0.5.1](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.5.1) (2018-05-29) 31 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.6.0...v0.5.1) 32 | 33 | **Closed issues:** 34 | 35 | - Swagger & Express [\#106](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/106) 36 | - exclude [\#96](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/96) 37 | - Resolve Errors displayed in Swagger UI [\#91](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/91) 38 | - Test [\#65](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/65) 39 | 40 | **Merged pull requests:** 41 | 42 | - Config contains idType to set default for application [\#108](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/108) ([andys8](https://github.com/andys8)) 43 | - fix\(documentation\): Improved markdown highlighting [\#104](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/104) ([andys8](https://github.com/andys8)) 44 | - fix\(documentation\): Array items reference [\#101](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/101) ([jraut](https://github.com/jraut)) 45 | - typo fix [\#95](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/95) ([sarkistlt](https://github.com/sarkistlt)) 46 | - Update mocha to the latest version 🚀 [\#92](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/92) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 47 | - Update semistandard to the latest version 🚀 [\#88](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/88) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 48 | - Update feathers-sequelize to the latest version 🚀 [\#87](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/87) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 49 | - Update feathers-memory to the latest version 🚀 [\#86](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/86) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 50 | 51 | ## [v0.6.0](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.6.0) (2017-11-08) 52 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.5.0...v0.6.0) 53 | 54 | **Closed issues:** 55 | 56 | - Outdated provider signature w/ Buzzard [\#83](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/83) 57 | - Is there a version that works with OAS 2.0? [\#82](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/82) 58 | - Swagger 3.x compatible version [\#78](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/78) 59 | - Did it working with last swagger 3.0 [\#61](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/61) 60 | - \[How to\] add documentation for the /auth/xxx endpoints [\#54](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/54) 61 | 62 | **Merged pull requests:** 63 | 64 | - Update to new plugin infrastructure and fix test and examples [\#85](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/85) ([daffl](https://github.com/daffl)) 65 | - Fix: Adding support for Buzzard provider syntax \(\#83\) [\#84](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/84) ([alexisabril](https://github.com/alexisabril)) 66 | - Update mocha to the latest version 🚀 [\#80](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/80) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 67 | - Support for Swagger UI 3.x [\#79](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/79) ([kristianmandrup](https://github.com/kristianmandrup)) 68 | - Update debug to the latest version 🚀 [\#77](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/77) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 69 | - adding another example to readme [\#75](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/75) ([paul42](https://github.com/paul42)) 70 | - add link and comment about what is being defined [\#74](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/74) ([jamesjnadeau](https://github.com/jamesjnadeau)) 71 | 72 | ## [v0.5.0](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.5.0) (2017-06-08) 73 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.4.0...v0.5.0) 74 | 75 | **Merged pull requests:** 76 | 77 | - Update sequelize to the latest version 🚀 [\#71](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/71) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 78 | - Fix: docs ignored when path already exists \#23 [\#69](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/69) ([psi-4ward](https://github.com/psi-4ward)) 79 | - Quality of life [\#68](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/68) ([christopherjbaker](https://github.com/christopherjbaker)) 80 | - Update chai to the latest version 🚀 [\#66](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/66) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 81 | - Update feathers-sequelize to the latest version 🚀 [\#60](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/60) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 82 | 83 | ## [v0.4.0](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.4.0) (2017-04-26) 84 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.3.5...v0.4.0) 85 | 86 | **Merged pull requests:** 87 | 88 | - Don't add empty paths [\#58](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/58) ([cpsubrian](https://github.com/cpsubrian)) 89 | - Update semistandard to the latest version 🚀 [\#57](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/57) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 90 | - Update dependencies to enable Greenkeeper 🌴 [\#56](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/56) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) 91 | - Allow each method of a service to define docs. [\#52](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/52) ([cpsubrian](https://github.com/cpsubrian)) 92 | - Fixes a couple defaults that should be objects, not arrays. [\#49](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/49) ([cpsubrian](https://github.com/cpsubrian)) 93 | 94 | ## [v0.3.5](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.3.5) (2017-04-01) 95 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.3.4...v0.3.5) 96 | 97 | **Merged pull requests:** 98 | 99 | - Fix extra whitespace if no versionPrefix is passed in config. [\#48](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/48) ([cpsubrian](https://github.com/cpsubrian)) 100 | - Fix 'text/html' redirect not respecting mountpath [\#47](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/47) ([cpsubrian](https://github.com/cpsubrian)) 101 | 102 | ## [v0.3.4](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.3.4) (2017-03-23) 103 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.3.3...v0.3.4) 104 | 105 | **Closed issues:** 106 | 107 | - Definition for prefixed routes [\#39](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/39) 108 | 109 | **Merged pull requests:** 110 | 111 | - Added config option to support api prefixing and versioning \(fix \#39\) [\#41](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/41) ([sinedied](https://github.com/sinedied)) 112 | 113 | ## [v0.3.3](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.3.3) (2017-03-15) 114 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.3.2...v0.3.3) 115 | 116 | **Closed issues:** 117 | 118 | - Use default swagger-ui? [\#38](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/38) 119 | - can i use custom swagger-ui? [\#32](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/32) 120 | - ENOENT: no such file or directory [\#31](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/31) 121 | 122 | **Merged pull requests:** 123 | 124 | - Added option to display default Swagger UI \(closes \#38\) [\#40](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/40) ([sinedied](https://github.com/sinedied)) 125 | - Update feathers-authentication to version 1.0.2 🚀 [\#34](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/34) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) 126 | 127 | ## [v0.3.2](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.3.2) (2016-12-12) 128 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.3.1...v0.3.2) 129 | 130 | **Merged pull requests:** 131 | 132 | - Fix definition [\#27](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/27) ([daffl](https://github.com/daffl)) 133 | 134 | ## [v0.3.1](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.3.1) (2016-11-23) 135 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.3.0...v0.3.1) 136 | 137 | **Closed issues:** 138 | 139 | - Example apps only work when run from root of repo [\#21](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/21) 140 | - Should redirect from /docs to /docs/ [\#20](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/20) 141 | - TypeError: Cannot read property 'name' of undefined [\#19](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/19) 142 | 143 | **Merged pull requests:** 144 | 145 | - Redirect HTML page to always trailing slash and use require.resolve to find UI [\#22](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/22) ([daffl](https://github.com/daffl)) 146 | 147 | ## [v0.3.0](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.3.0) (2016-11-22) 148 | [Full Changelog](https://github.com/feathersjs-ecosystem/feathers-swagger/compare/v0.1.0...v0.3.0) 149 | 150 | ## [v0.1.0](https://github.com/feathersjs-ecosystem/feathers-swagger/tree/v0.1.0) (2016-11-22) 151 | **Implemented enhancements:** 152 | 153 | - Use swagger-node-express [\#5](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/5) 154 | - Parse documentation with Dox \(or similar\) [\#3](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/3) 155 | - Complete support for REST API [\#2](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/2) 156 | 157 | **Fixed bugs:** 158 | 159 | - Remove bower\_components/ [\#4](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/4) 160 | 161 | **Closed issues:** 162 | 163 | - Update npm version [\#16](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/16) 164 | - Problem with path.join [\#13](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/13) 165 | - Please Provide Documentation/Make Example More Accessible [\#11](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/11) 166 | - Documentation Doesn't Exist: What Boneheaded Thing Did I Do? [\#10](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/10) 167 | - Supported in Feathers 2? [\#9](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/9) 168 | - See Swagger API docs [\#1](https://github.com/feathersjs-ecosystem/feathers-swagger/issues/1) 169 | 170 | **Merged pull requests:** 171 | 172 | - Get plugin release ready [\#18](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/18) ([daffl](https://github.com/daffl)) 173 | - support authorization setting and separation of docsPath and basePath [\#17](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/17) ([daffl](https://github.com/daffl)) 174 | - fix path.join windows issue [\#14](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/14) ([superbarne](https://github.com/superbarne)) 175 | - upgrate swagger to 2.0 [\#12](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/12) ([lpreterite](https://github.com/lpreterite)) 176 | - Update README.md [\#8](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/8) ([derek-watson](https://github.com/derek-watson)) 177 | - Add config option to set extension of generated docs [\#7](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/7) ([c10h22](https://github.com/c10h22)) 178 | - Update to feathers 1.2.0 [\#6](https://github.com/feathersjs-ecosystem/feathers-swagger/pull/6) ([c10h22](https://github.com/c10h22)) 179 | 180 | 181 | 182 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* --------------------------------------------------------------------------------