mirror of
https://github.com/ditkrg/rswag.git
synced 2026-01-22 22:06:43 +00:00
Merge branch 'origin/master' into feat/add-basic-auth
This commit is contained in:
commit
6e706c04d7
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
## Describe the bug
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
## Steps to Test or Reproduce
|
||||||
|
Please provide an example repo or the steps to reproduce the behavior.
|
||||||
|
|
||||||
|
## Expected behavior
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
## Additional context
|
||||||
|
Add any other context about the problem here.
|
||||||
|
|
||||||
|
## Rswag Version
|
||||||
|
The version of rswag are you using.
|
||||||
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
## Is your feature request related to a problem? Please describe.
|
||||||
|
A clear and concise description of what the problem is.
|
||||||
|
|
||||||
|
## Describe the solution you'd like
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
## Describe alternatives you've considered
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
## Additional context
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
15
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal file
15
.github/PULL_REQUEST_TEMPLATE/pull_request_template.md
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
## Problem
|
||||||
|
A clear and concise description of what the problem is.
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
A clear and concise description of what the solution is.
|
||||||
|
|
||||||
|
### Related Issues
|
||||||
|
Links to any related issues.
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
- [ ] Added tests
|
||||||
|
- [ ] Changelog updated
|
||||||
|
|
||||||
|
### Steps to Test or Reproduce
|
||||||
|
Outline the steps to test or reproduce the PR here.
|
||||||
@ -1 +1 @@
|
|||||||
2.3.1
|
2.6.3
|
||||||
|
|||||||
26
.travis.yml
26
.travis.yml
@ -1,30 +1,35 @@
|
|||||||
language: ruby
|
language: ruby
|
||||||
|
|
||||||
|
dist: bionic
|
||||||
|
services:
|
||||||
|
- xvfb
|
||||||
|
|
||||||
rvm:
|
rvm:
|
||||||
- 2.2.5
|
- 2.6.3
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
- RAILS_VERSION=6.0.0
|
||||||
- RAILS_VERSION=5.2.0
|
- RAILS_VERSION=5.2.0
|
||||||
- RAILS_VERSION=4.2.0
|
|
||||||
- RAILS_VERSION=3.2.22
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- libqtwebkit-dev
|
||||||
|
- libqtwebkit4
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- /home/travis/.rvm/gems/ruby-2.2.5
|
- /home/travis/.rvm/gems/ruby-2.6.3
|
||||||
|
|
||||||
install: ./ci/build.sh
|
install: ./ci/build.sh
|
||||||
|
|
||||||
before_script:
|
|
||||||
- export DISPLAY=:99.0
|
|
||||||
- sh -e /etc/init.d/xvfb start
|
|
||||||
- sleep 3
|
|
||||||
|
|
||||||
script: ./ci/test.sh
|
script: ./ci/test.sh
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
- stage: publish components
|
- stage: publish components
|
||||||
script: 'cd rswag-api'
|
script: 'cd rswag-api'
|
||||||
|
if: tag IS present
|
||||||
deploy:
|
deploy:
|
||||||
gemspec: rswag-api.gemspec
|
gemspec: rswag-api.gemspec
|
||||||
provider: rubygems
|
provider: rubygems
|
||||||
@ -35,6 +40,7 @@ jobs:
|
|||||||
|
|
||||||
- stage: publish components
|
- stage: publish components
|
||||||
script: 'cd rswag-specs'
|
script: 'cd rswag-specs'
|
||||||
|
if: tag IS present
|
||||||
deploy:
|
deploy:
|
||||||
gemspec: rswag-specs.gemspec
|
gemspec: rswag-specs.gemspec
|
||||||
provider: rubygems
|
provider: rubygems
|
||||||
@ -45,6 +51,7 @@ jobs:
|
|||||||
|
|
||||||
- stage: publish components
|
- stage: publish components
|
||||||
script: 'cd rswag-ui'
|
script: 'cd rswag-ui'
|
||||||
|
if: tag IS present
|
||||||
deploy:
|
deploy:
|
||||||
gemspec: rswag-ui.gemspec
|
gemspec: rswag-ui.gemspec
|
||||||
provider: rubygems
|
provider: rubygems
|
||||||
@ -56,6 +63,7 @@ jobs:
|
|||||||
|
|
||||||
- stage: publish rswag
|
- stage: publish rswag
|
||||||
script: 'cd rswag'
|
script: 'cd rswag'
|
||||||
|
if: tag IS present
|
||||||
deploy:
|
deploy:
|
||||||
gemspec: rswag.gemspec
|
gemspec: rswag.gemspec
|
||||||
provider: rubygems
|
provider: rubygems
|
||||||
|
|||||||
39
CHANGELOG.md
Normal file
39
CHANGELOG.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# rswag
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
### Changed
|
||||||
|
### Deprecated
|
||||||
|
### Removed
|
||||||
|
### Fixed
|
||||||
|
### Security
|
||||||
|
|
||||||
|
## [2.2.0] - 2019-11-01
|
||||||
|
### Added
|
||||||
|
- New swagger_format config option for setting YAML output [#251](https://github.com/rswag/rswag/pull/251)
|
||||||
|
### Changed
|
||||||
|
- rswag-api will serve yaml files as yaml [#251](https://github.com/rswag/rswag/pull/251)
|
||||||
|
|
||||||
|
## [2.1.1] - 2019-10-18
|
||||||
|
### Fixed
|
||||||
|
- Fix incorrect require reference for swagger_generator [#248](https://github.com/rswag/rswag/issues/248)
|
||||||
|
|
||||||
|
## [2.1.0] - 2019-10-17
|
||||||
|
### Added
|
||||||
|
- New Spec Generator [#75](https://github.com/rswag/rswag/pull/75)
|
||||||
|
- Support for Options and Trace verbs; You must use a framework that supports this, for Options Rails 6.1+ Rails 6 does not support Trace. [#237](https://github.com/rswag/rswag/pull/75)
|
||||||
|
### Changed
|
||||||
|
- Update swagger-ui to 3.18.2 [#240](https://github.com/rswag/rswag/pull/240)
|
||||||
|
|
||||||
|
## [2.0.6] - 2019-10-03
|
||||||
|
### Added
|
||||||
|
- Support for Rails 6 [#228](https://github.com/rswag/rswag/pull/228)
|
||||||
|
- Support for Windows paths [#176](https://github.com/rswag/rswag/pull/176)
|
||||||
|
### Changed
|
||||||
|
- Show response body when error code is not expected [#117](https://github.com/rswag/rswag/pull/177)
|
||||||
|
|
||||||
|
## [2.0.5] - 2018-07-10
|
||||||
59
CONTRIBUTING.md
Normal file
59
CONTRIBUTING.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
## Fork, then clone the repo:
|
||||||
|
```
|
||||||
|
git clone git@github.com:rswag/rswag.git
|
||||||
|
cd rswag
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build
|
||||||
|
Set up your machine:
|
||||||
|
```
|
||||||
|
./ci/build.sh
|
||||||
|
```
|
||||||
|
Or manually
|
||||||
|
```
|
||||||
|
bundle
|
||||||
|
cd test-app
|
||||||
|
bundle exec rake db:setup
|
||||||
|
cd -
|
||||||
|
|
||||||
|
cd rswag-ui
|
||||||
|
npm install
|
||||||
|
cd -
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test
|
||||||
|
Make sure the tests pass:
|
||||||
|
```
|
||||||
|
./ci/test.sh
|
||||||
|
```
|
||||||
|
or manually
|
||||||
|
```
|
||||||
|
cd test-app
|
||||||
|
bundle exec rspec
|
||||||
|
```
|
||||||
|
|
||||||
|
Make your change. Add tests for your change. Make the tests pass:
|
||||||
|
|
||||||
|
```
|
||||||
|
bundle exec rspec
|
||||||
|
```
|
||||||
|
|
||||||
|
Push to your fork and [submit a Pull Request][pr].
|
||||||
|
|
||||||
|
[pr]: https://github.com/rswag/rswag/compare/
|
||||||
|
|
||||||
|
## Release
|
||||||
|
(for maintainers)
|
||||||
|
|
||||||
|
Update the changelog.md, putting the new version number in and moving the Unreleased marker.
|
||||||
|
|
||||||
|
Merge the changes into master you wish to release.
|
||||||
|
|
||||||
|
Add and push a new git tag, annotated tags preferred:
|
||||||
|
```
|
||||||
|
git tag -s 2.0.6 -m 'v2.0.6'
|
||||||
|
```
|
||||||
|
|
||||||
|
Travis will detect the tag and release all gems with that tag version number.
|
||||||
9
Gemfile
9
Gemfile
@ -9,11 +9,16 @@ gem 'rails', "#{rails_version}"
|
|||||||
case rails_version.split('.').first
|
case rails_version.split('.').first
|
||||||
when '3'
|
when '3'
|
||||||
gem 'strong_parameters'
|
gem 'strong_parameters'
|
||||||
when '4', '5'
|
when '4', '5', '6'
|
||||||
gem 'responders'
|
gem 'responders'
|
||||||
end
|
end
|
||||||
|
|
||||||
gem 'sqlite3'
|
case rails_version.split('.').first
|
||||||
|
when '3', '4', '5'
|
||||||
|
gem 'sqlite3', '~> 1.3.6'
|
||||||
|
when '6'
|
||||||
|
gem 'sqlite3', '~> 1.4.1'
|
||||||
|
end
|
||||||
|
|
||||||
gem 'rswag-api', path: './rswag-api'
|
gem 'rswag-api', path: './rswag-api'
|
||||||
gem 'rswag-ui', path: './rswag-ui'
|
gem 'rswag-ui', path: './rswag-ui'
|
||||||
|
|||||||
100
README.md
100
README.md
@ -1,10 +1,11 @@
|
|||||||
rswag
|
rswag
|
||||||
=========
|
=========
|
||||||
[](https://travis-ci.org/domaindrivendev/rswag)
|
[](https://travis-ci.org/rswag/rswag)
|
||||||
|
[](https://codeclimate.com/github/rswag/rswag/maintainability)
|
||||||
|
|
||||||
[Swagger](http://swagger.io) tooling for Rails API's. Generate beautiful API documentation, including a UI to explore and test operations, directly from your rspec integration tests.
|
[Swagger](http://swagger.io) tooling for Rails API's. Generate beautiful API documentation, including a UI to explore and test operations, directly from your rspec integration tests.
|
||||||
|
|
||||||
Rswag extends rspec-rails "request specs" with a Swagger-based DSL for describing and testing API operations. You describe your API operations with a succinct, intuitive syntax, and it automaticaly runs the tests. Once you have green tests, run a rake task to auto-generate corresponding Swagger files and expose them as JSON endpoints. Rswag also provides an embedded version of the awesome [swagger-ui](https://github.com/swagger-api/swagger-ui) that's powered by the exposed JSON. This toolchain makes it seamless to go from integration specs, which youre probably doing in some form already, to living documentation for your API consumers.
|
Rswag extends rspec-rails "request specs" with a Swagger-based DSL for describing and testing API operations. You describe your API operations with a succinct, intuitive syntax, and it automaticaly runs the tests. Once you have green tests, run a rake task to auto-generate corresponding Swagger files and expose them as YAML or JSON endpoints. Rswag also provides an embedded version of the awesome [swagger-ui](https://github.com/swagger-api/swagger-ui) that's powered by the exposed file. This toolchain makes it seamless to go from integration specs, which youre probably doing in some form already, to living documentation for your API consumers.
|
||||||
|
|
||||||
And that's not all ...
|
And that's not all ...
|
||||||
|
|
||||||
@ -14,9 +15,9 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
|
|||||||
|
|
||||||
|Rswag Version|Swagger (OpenAPI) Spec.|swagger-ui|
|
|Rswag Version|Swagger (OpenAPI) Spec.|swagger-ui|
|
||||||
|----------|----------|----------|
|
|----------|----------|----------|
|
||||||
|[master](https://github.com/domaindrivendev/rswag/tree/master)|2.0|3.17.3|
|
|[master](https://github.com/rswag/rswag/tree/master)|2.0|3.18.2|
|
||||||
|[2.0.5](https://github.com/domaindrivendev/rswag/tree/2.0.4)|2.0|3.17.3|
|
|[2.2.0](https://github.com/rswag/rswag/tree/2.2.0)|2.0|3.18.2|
|
||||||
|[1.6.0](https://github.com/domaindrivendev/rswag/tree/1.6.0)|2.0|2.2.5|
|
|[1.6.0](https://github.com/rswag/rswag/tree/1.6.0)|2.0|2.2.5|
|
||||||
|
|
||||||
## Getting Started ##
|
## Getting Started ##
|
||||||
|
|
||||||
@ -26,14 +27,15 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
|
|||||||
gem 'rswag'
|
gem 'rswag'
|
||||||
```
|
```
|
||||||
|
|
||||||
or if you like to avoid loading rspec in other bundler groups.
|
or if you like to avoid loading rspec in other bundler groups load the rswag-specs component separately.
|
||||||
|
Note: Adding it to the :development group is not strictly necessary, but without it, generators and rake tasks must be preceded by RAILS_ENV=test.
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# Gemfile
|
# Gemfile
|
||||||
gem 'rswag-api'
|
gem 'rswag-api'
|
||||||
gem 'rswag-ui'
|
gem 'rswag-ui'
|
||||||
|
|
||||||
group :test do
|
group :development, :test do
|
||||||
gem 'rspec-rails'
|
gem 'rspec-rails'
|
||||||
gem 'rswag-specs'
|
gem 'rswag-specs'
|
||||||
end
|
end
|
||||||
@ -48,7 +50,8 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
|
|||||||
Or run the install generators for each package separately if you installed Rswag as separate gems, as indicated above:
|
Or run the install generators for each package separately if you installed Rswag as separate gems, as indicated above:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
rails g rswag:api:install rswag:ui:install
|
rails g rswag:api:install
|
||||||
|
rails g rswag:ui:install
|
||||||
RAILS_ENV=test rails g rswag:specs:install
|
RAILS_ENV=test rails g rswag:specs:install
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -120,12 +123,17 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
There is also a generator which can help get you started `rails generate rspec:swagger API::MyController`
|
||||||
|
|
||||||
|
|
||||||
4. Generate the Swagger JSON file(s)
|
4. Generate the Swagger JSON file(s)
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
rake rswag:specs:swaggerize
|
rake rswag:specs:swaggerize
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This common command is also aliased as `rake rswag`.
|
||||||
|
|
||||||
5. Spin up your app and check out the awesome, auto-generated docs at _/api-docs_!
|
5. Spin up your app and check out the awesome, auto-generated docs at _/api-docs_!
|
||||||
|
|
||||||
## The rspec DSL ##
|
## The rspec DSL ##
|
||||||
@ -185,7 +193,7 @@ describe 'Blogs API' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
*Note:* the OAI v3 may be released soon(ish?) and include a nullable property. This may have an effect on the need/use of custom extension to the draft. Do not use this property if you don't understand the implications.
|
*Note:* OAI v3 has a nullable property. Rswag will work to support this soon. This may have an effect on the need/use of custom extension to the draft. Do not use this property if you don't understand the implications.
|
||||||
<https://github.com/OAI/OpenAPI-Specification/issues/229#issuecomment-280376087>
|
<https://github.com/OAI/OpenAPI-Specification/issues/229#issuecomment-280376087>
|
||||||
|
|
||||||
### Global Metadata ###
|
### Global Metadata ###
|
||||||
@ -202,16 +210,18 @@ RSpec.configure do |config|
|
|||||||
swagger: '2.0',
|
swagger: '2.0',
|
||||||
info: {
|
info: {
|
||||||
title: 'API V1',
|
title: 'API V1',
|
||||||
version: 'v1'
|
version: 'v1',
|
||||||
|
description: 'This is the first version of my API'
|
||||||
},
|
},
|
||||||
basePath: '/api/v1'
|
basePath: '/api/v1'
|
||||||
},
|
},
|
||||||
|
|
||||||
'v2/swagger.json' => {
|
'v2/swagger.yaml' => {
|
||||||
swagger: '2.0',
|
openapi: '3.0.0',
|
||||||
info: {
|
info: {
|
||||||
title: 'API V2',
|
title: 'API V2',
|
||||||
version: 'v2'
|
version: 'v2',
|
||||||
|
description: 'This is the second version of my API'
|
||||||
},
|
},
|
||||||
basePath: '/api/v2'
|
basePath: '/api/v2'
|
||||||
}
|
}
|
||||||
@ -219,11 +229,12 @@ RSpec.configure do |config|
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
__NOTE__: By default, the paths, operations and responses defined in your spec files will be associated with the first Swagger document in _swagger_helper.rb_. If you're using multiple documents, you'll need to tag the individual specs with their target document name:
|
#### Supporting multiple versions of API ####
|
||||||
|
By default, the paths, operations and responses defined in your spec files will be associated with the first Swagger document in _swagger_helper.rb_. If your API has multiple versions, you should be using separate documents to describe each of them. In order to assign a file with a given version of API, you'll need to add the ```swagger_doc``` tag to each spec specifying its target document name:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# spec/integration/v2/blogs_spec.rb
|
# spec/integration/v2/blogs_spec.rb
|
||||||
describe 'Blogs API', swagger_doc: 'v2/swagger.json' do
|
describe 'Blogs API', swagger_doc: 'v2/swagger.yaml' do
|
||||||
|
|
||||||
path '/blogs' do
|
path '/blogs' do
|
||||||
...
|
...
|
||||||
@ -233,6 +244,25 @@ describe 'Blogs API', swagger_doc: 'v2/swagger.json' do
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Formatting the description literals: ####
|
||||||
|
Swagger supports the Markdown syntax to format strings. This can be especially handy if you were to provide a long description of a given API version or endpoint. Use [this guide](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) for reference.
|
||||||
|
|
||||||
|
__NOTE:__ There is one difference between the official Markdown syntax and Swagger interpretation, namely tables. To create a table like this:
|
||||||
|
|
||||||
|
| Column1 | Collumn2 |
|
||||||
|
| ------- | -------- |
|
||||||
|
| cell1 | cell2 |
|
||||||
|
|
||||||
|
you should use the folowing syntax, making sure there are no whitespaces at the start of any of the lines:
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
| Column1 | Collumn2 |
|
||||||
|
| ------- | -------- |
|
||||||
|
| cell1 | cell2 |
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
### Specifying/Testing API Security ###
|
### Specifying/Testing API Security ###
|
||||||
|
|
||||||
Swagger allows for the specification of different security schemes and their applicability to operations in an API. To leverage this in rswag, you define the schemes globally in _swagger_helper.rb_ and then use the "security" attribute at the operation level to specify which schemes, if any, are applicable to that operation. Swagger supports :basic, :apiKey and :oauth2 scheme types. See [the spec](http://swagger.io/specification/#security-definitions-object-109) for more info.
|
Swagger allows for the specification of different security schemes and their applicability to operations in an API. To leverage this in rswag, you define the schemes globally in _swagger_helper.rb_ and then use the "security" attribute at the operation level to specify which schemes, if any, are applicable to that operation. Swagger supports :basic, :apiKey and :oauth2 scheme types. See [the spec](http://swagger.io/specification/#security-definitions-object-109) for more info.
|
||||||
@ -309,6 +339,15 @@ end
|
|||||||
|
|
||||||
__NOTE__: If you do change this, you'll also need to update the rswag-api.rb initializer (assuming you're using rswag-api). More on this later.
|
__NOTE__: If you do change this, you'll also need to update the rswag-api.rb initializer (assuming you're using rswag-api). More on this later.
|
||||||
|
|
||||||
|
### Input Location for Rspec Tests ###
|
||||||
|
|
||||||
|
By default, rswag will search for integration tests in _spec/requests_, _spec/api_ and _spec/integration_. If you want to use tests from other locations, provide the PATTERN argument to rake:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# search for tests in spec/swagger
|
||||||
|
rake rswag:specs:swaggerize PATTERN="spec/swagger/**/*_spec.rb"
|
||||||
|
```
|
||||||
|
|
||||||
### Referenced Parameters and Schema Definitions ###
|
### Referenced Parameters and Schema Definitions ###
|
||||||
|
|
||||||
Swagger allows you to describe JSON structures inline with your operation descriptions OR as referenced globals. For example, you might have a standard response structure for all failed operations. Rather than repeating the schema in every operation spec, you can define it globally and provide a reference to it in each spec:
|
Swagger allows you to describe JSON structures inline with your operation descriptions OR as referenced globals. For example, you might have a standard response structure for all failed operations. Rather than repeating the schema in every operation spec, you can define it globally and provide a reference to it in each spec:
|
||||||
@ -421,6 +460,35 @@ RSpec.configure do |config|
|
|||||||
config.swagger_dry_run = false
|
config.swagger_dry_run = false
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Running tests without documenting ###
|
||||||
|
|
||||||
|
If you want to use Rswag for testing without adding it to you swagger docs, you can provide the document tag:
|
||||||
|
```ruby
|
||||||
|
describe 'Blogs API' do
|
||||||
|
path '/blogs/{blog_id}' do
|
||||||
|
get 'Retrieves a blog' do
|
||||||
|
# documentation is now disabled for this response only
|
||||||
|
response 200, 'blog found', document: false do
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also reenable documentation for specific responses only:
|
||||||
|
```ruby
|
||||||
|
# documentation is now disabled
|
||||||
|
describe 'Blogs API', document: false do
|
||||||
|
path '/blogs/{blog_id}' do
|
||||||
|
get 'Retrieves a blog' do
|
||||||
|
# documentation is reenabled for this response only
|
||||||
|
response 200, 'blog found', document: true do
|
||||||
|
...
|
||||||
|
end
|
||||||
|
|
||||||
|
response 401, 'special case' do
|
||||||
|
...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Route Prefix for Swagger JSON Endpoints ###
|
### Route Prefix for Swagger JSON Endpoints ###
|
||||||
|
|
||||||
The functionality to expose Swagger files, such as those generated by rswag-specs, as JSON endpoints is implemented as a Rails Engine. As with any Engine, you can change it's mount prefix in _routes.rb_:
|
The functionality to expose Swagger files, such as those generated by rswag-specs, as JSON endpoints is implemented as a Rails Engine. As with any Engine, you can change it's mount prefix in _routes.rb_:
|
||||||
@ -464,7 +532,7 @@ Rswag::Api.configure do |c|
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
Note how the filter is passed the rack env for the current request. This provides a lot of flexibilty. For example, you can assign the "host" property (as shown) or you could inspect session information or an Authoriation header and remove operations based on user permissions.
|
Note how the filter is passed the rack env for the current request. This provides a lot of flexibilty. For example, you can assign the "host" property (as shown) or you could inspect session information or an Authorization header and remove operations based on user permissions.
|
||||||
|
|
||||||
### Enable Swagger Endpoints for swagger-ui ###
|
### Enable Swagger Endpoints for swagger-ui ###
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
require 'json'
|
require 'json'
|
||||||
|
require 'yaml'
|
||||||
|
require 'rack/mime'
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
module Api
|
module Api
|
||||||
class Middleware
|
class Middleware
|
||||||
|
|
||||||
def initialize(app, config)
|
def initialize(app, config)
|
||||||
@app = app
|
@app = app
|
||||||
@ -14,24 +16,46 @@ module Rswag
|
|||||||
filename = "#{@config.resolve_swagger_root(env)}/#{path}"
|
filename = "#{@config.resolve_swagger_root(env)}/#{path}"
|
||||||
|
|
||||||
if env['REQUEST_METHOD'] == 'GET' && File.file?(filename)
|
if env['REQUEST_METHOD'] == 'GET' && File.file?(filename)
|
||||||
swagger = load_json(filename)
|
swagger = parse_file(filename)
|
||||||
@config.swagger_filter.call(swagger, env) unless @config.swagger_filter.nil?
|
@config.swagger_filter.call(swagger, env) unless @config.swagger_filter.nil?
|
||||||
|
mime = Rack::Mime.mime_type(::File.extname(path), 'text/plain')
|
||||||
|
body = unload_swagger(filename, swagger)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'200',
|
'200',
|
||||||
{ 'Content-Type' => 'application/json' },
|
{ 'Content-Type' => mime },
|
||||||
[ JSON.dump(swagger) ]
|
[ body ]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
return @app.call(env)
|
return @app.call(env)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def parse_file(filename)
|
||||||
|
if /\.ya?ml$/ === filename
|
||||||
|
load_yaml(filename)
|
||||||
|
else
|
||||||
|
load_json(filename)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_yaml(filename)
|
||||||
|
YAML.safe_load(File.read(filename))
|
||||||
|
end
|
||||||
|
|
||||||
def load_json(filename)
|
def load_json(filename)
|
||||||
JSON.parse(File.read(filename))
|
JSON.parse(File.read(filename))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unload_swagger(filename, swagger)
|
||||||
|
if /\.ya?ml$/ === filename
|
||||||
|
YAML.dump(swagger)
|
||||||
|
else
|
||||||
|
JSON.dump(swagger)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -13,5 +13,5 @@ Gem::Specification.new do |s|
|
|||||||
|
|
||||||
s.files = Dir["{lib}/**/*"] + ["MIT-LICENSE", "Rakefile"]
|
s.files = Dir["{lib}/**/*"] + ["MIT-LICENSE", "Rakefile"]
|
||||||
|
|
||||||
s.add_dependency 'railties', '>= 3.1', '< 6.0'
|
s.add_dependency 'railties', '>= 3.1', '< 7.0'
|
||||||
end
|
end
|
||||||
|
|||||||
5
rswag-api/spec/rswag/api/fixtures/swagger/v1/swagger.yml
Normal file
5
rswag-api/spec/rswag/api/fixtures/swagger/v1/swagger.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
swagger: '2.0'
|
||||||
|
info:
|
||||||
|
title: API V1
|
||||||
|
version: v1
|
||||||
|
paths: {}
|
||||||
@ -76,6 +76,21 @@ module Rswag
|
|||||||
expect(response[2].join).to include('"host":"tempuri.org"')
|
expect(response[2].join).to include('"host":"tempuri.org"')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when a path maps to a yaml swagger file' do
|
||||||
|
let(:env) { env_defaults.merge('PATH_INFO' => 'v1/swagger.yml') }
|
||||||
|
|
||||||
|
it 'returns a 200 status' do
|
||||||
|
expect(response.length).to eql(3)
|
||||||
|
expect(response.first).to eql('200')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns contents of the swagger file' do
|
||||||
|
expect(response.length).to eql(3)
|
||||||
|
expect(response[1]).to include( 'Content-Type' => 'text/yaml')
|
||||||
|
expect(response[2].join).to include('title: API V1')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
9
rswag-specs/lib/generators/rspec/USAGE
Normal file
9
rswag-specs/lib/generators/rspec/USAGE
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Description:
|
||||||
|
This creates an RSpec request spec to define Swagger documentation for a
|
||||||
|
controller. It will create a test for each of the controller's methods.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
rails generate rspec:swagger V3::AccountsController
|
||||||
|
|
||||||
|
This will create:
|
||||||
|
spec/requests/v3/accounts_spec.rb
|
||||||
22
rswag-specs/lib/generators/rspec/swagger_generator.rb
Normal file
22
rswag-specs/lib/generators/rspec/swagger_generator.rb
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
require 'rswag/route_parser'
|
||||||
|
require 'rails/generators'
|
||||||
|
|
||||||
|
module Rspec
|
||||||
|
class SwaggerGenerator < ::Rails::Generators::NamedBase
|
||||||
|
source_root File.expand_path('../templates', __FILE__)
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@routes = Rswag::RouteParser.new(controller_path).routes
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_spec_file
|
||||||
|
template 'spec.rb', File.join('spec', 'requests', "#{controller_path}_spec.rb")
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def controller_path
|
||||||
|
file_path.chomp('_controller')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
30
rswag-specs/lib/generators/rspec/templates/spec.rb
Normal file
30
rswag-specs/lib/generators/rspec/templates/spec.rb
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
require 'swagger_helper'
|
||||||
|
|
||||||
|
RSpec.describe '<%= controller_path %>', type: :request do
|
||||||
|
<% @routes.each do | template, path_item | %>
|
||||||
|
path '<%= template %>' do
|
||||||
|
<% unless path_item[:params].empty? -%>
|
||||||
|
# You'll want to customize the parameter types...
|
||||||
|
<% path_item[:params].each do |param| -%>
|
||||||
|
parameter name: '<%= param %>', in: :path, type: :string, description: '<%= param %>'
|
||||||
|
<% end -%>
|
||||||
|
<% end -%>
|
||||||
|
<% path_item[:actions].each do | action, details | %>
|
||||||
|
<%= action %>('<%= details[:summary] %>') do
|
||||||
|
response(200, 'successful') do
|
||||||
|
<% unless path_item[:params].empty? -%>
|
||||||
|
<% path_item[:params].each do |param| -%>
|
||||||
|
let(:<%= param %>) { '123' }
|
||||||
|
<% end -%>
|
||||||
|
<% end -%>
|
||||||
|
|
||||||
|
after do |example|
|
||||||
|
example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) }
|
||||||
|
end
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
<% end -%>
|
||||||
|
end
|
||||||
|
<% end -%>
|
||||||
|
end
|
||||||
@ -4,7 +4,7 @@ RSpec.configure do |config|
|
|||||||
# Specify a root folder where Swagger JSON files are generated
|
# Specify a root folder where Swagger JSON files are generated
|
||||||
# NOTE: If you're using the rswag-api to serve API descriptions, you'll need
|
# NOTE: If you're using the rswag-api to serve API descriptions, you'll need
|
||||||
# to ensure that it's configured to serve Swagger from the same folder
|
# to ensure that it's configured to serve Swagger from the same folder
|
||||||
config.swagger_root = Rails.root.to_s + '/swagger'
|
config.swagger_root = Rails.root.join('swagger').to_s
|
||||||
|
|
||||||
# Define one or more Swagger documents and provide global metadata for each one
|
# Define one or more Swagger documents and provide global metadata for each one
|
||||||
# When you run the 'rswag:specs:swaggerize' rake task, the complete Swagger will
|
# When you run the 'rswag:specs:swaggerize' rake task, the complete Swagger will
|
||||||
@ -13,8 +13,8 @@ RSpec.configure do |config|
|
|||||||
# document below. You can override this behavior by adding a swagger_doc tag to the
|
# document below. You can override this behavior by adding a swagger_doc tag to the
|
||||||
# the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json'
|
# the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json'
|
||||||
config.swagger_docs = {
|
config.swagger_docs = {
|
||||||
'v1/swagger.json' => {
|
'v1/swagger.yaml' => {
|
||||||
swagger: '2.0',
|
openapi: '3.0.1',
|
||||||
info: {
|
info: {
|
||||||
title: 'API V1',
|
title: 'API V1',
|
||||||
version: 'v1'
|
version: 'v1'
|
||||||
@ -22,4 +22,10 @@ RSpec.configure do |config|
|
|||||||
paths: {}
|
paths: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Specify the format of the output Swagger file when running 'rswag:specs:swaggerize'.
|
||||||
|
# The swagger_docs configuration option has the filename including format in
|
||||||
|
# the key, this may want to be changed to avoid putting yaml in json files.
|
||||||
|
# Defaults to json. Accepts ':json' and ':yaml'.
|
||||||
|
config.swagger_format = :yaml
|
||||||
end
|
end
|
||||||
|
|||||||
58
rswag-specs/lib/rswag/route_parser.rb
Normal file
58
rswag-specs/lib/rswag/route_parser.rb
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
module Rswag
|
||||||
|
class RouteParser
|
||||||
|
attr_reader :controller
|
||||||
|
|
||||||
|
def initialize(controller)
|
||||||
|
@controller = controller
|
||||||
|
end
|
||||||
|
|
||||||
|
def routes
|
||||||
|
::Rails.application.routes.routes.select do |route|
|
||||||
|
route.defaults[:controller] == controller
|
||||||
|
end.reduce({}) do |tree, route|
|
||||||
|
path = path_from(route)
|
||||||
|
verb = verb_from(route)
|
||||||
|
tree[path] ||= { params: params_from(route), actions: {} }
|
||||||
|
tree[path][:actions][verb] = { summary: summary_from(route) }
|
||||||
|
tree
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def path_from(route)
|
||||||
|
route.path.spec.to_s
|
||||||
|
.chomp('(.:format)') # Ignore any format suffix
|
||||||
|
.gsub(/:([^\/.?]+)/, '{\1}') # Convert :id to {id}
|
||||||
|
end
|
||||||
|
|
||||||
|
def verb_from(route)
|
||||||
|
verb = route.verb
|
||||||
|
if verb.kind_of? String
|
||||||
|
verb.downcase
|
||||||
|
else
|
||||||
|
verb.source.gsub(/[$^]/, '').downcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def summary_from(route)
|
||||||
|
verb = route.requirements[:action]
|
||||||
|
noun = route.requirements[:controller].split('/').last.singularize
|
||||||
|
|
||||||
|
# Apply a few customizations to make things more readable
|
||||||
|
case verb
|
||||||
|
when 'index'
|
||||||
|
verb = 'list'
|
||||||
|
noun = noun.pluralize
|
||||||
|
when 'destroy'
|
||||||
|
verb = 'delete'
|
||||||
|
end
|
||||||
|
|
||||||
|
"#{verb} #{noun}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def params_from(route)
|
||||||
|
route.segments - ['format']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -12,6 +12,7 @@ module Rswag
|
|||||||
c.add_setting :swagger_root
|
c.add_setting :swagger_root
|
||||||
c.add_setting :swagger_docs
|
c.add_setting :swagger_docs
|
||||||
c.add_setting :swagger_dry_run
|
c.add_setting :swagger_dry_run
|
||||||
|
c.add_setting :swagger_format
|
||||||
c.extend ExampleGroupHelpers, type: :request
|
c.extend ExampleGroupHelpers, type: :request
|
||||||
c.include ExampleHelpers, type: :request
|
c.include ExampleHelpers, type: :request
|
||||||
end
|
end
|
||||||
|
|||||||
@ -31,6 +31,14 @@ module Rswag
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def swagger_format
|
||||||
|
@swagger_format ||= begin
|
||||||
|
@rspec_config.swagger_format = :json if @rspec_config.swagger_format.nil? || @rspec_config.swagger_format.empty?
|
||||||
|
raise ConfigurationError, "Unknown swagger_format '#{@rspec_config.swagger_format}'" unless [:json, :yaml].include?(@rspec_config.swagger_format)
|
||||||
|
@rspec_config.swagger_format
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def get_swagger_doc(name)
|
def get_swagger_doc(name)
|
||||||
return swagger_docs.values.first if name.nil?
|
return swagger_docs.values.first if name.nil?
|
||||||
raise ConfigurationError, "Unknown swagger_doc '#{name}'" unless swagger_docs[name]
|
raise ConfigurationError, "Unknown swagger_doc '#{name}'" unless swagger_docs[name]
|
||||||
|
|||||||
@ -7,7 +7,7 @@ module Rswag
|
|||||||
describe(template, metadata, &block)
|
describe(template, metadata, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
[ :get, :post, :patch, :put, :delete, :head ].each do |verb|
|
[ :get, :post, :patch, :put, :delete, :head, :options, :trace ].each do |verb|
|
||||||
define_method(verb) do |summary, &block|
|
define_method(verb) do |summary, &block|
|
||||||
api_metadata = { operation: { verb: verb, summary: summary } }
|
api_metadata = { operation: { verb: verb, summary: summary } }
|
||||||
describe(verb, api_metadata, &block)
|
describe(verb, api_metadata, &block)
|
||||||
|
|||||||
@ -3,7 +3,7 @@ require 'json-schema'
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
class ExtendedSchema < JSON::Schema::Draft4
|
class ExtendedSchema < JSON::Schema::Draft4
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super
|
super
|
||||||
@attributes['type'] = ExtendedTypeAttribute
|
@attributes['type'] = ExtendedTypeAttribute
|
||||||
@ -13,7 +13,7 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
class ExtendedTypeAttribute < JSON::Schema::TypeV4Attribute
|
class ExtendedTypeAttribute < JSON::Schema::TypeV4Attribute
|
||||||
|
|
||||||
def self.validate(current_schema, data, fragments, processor, validator, options={})
|
def self.validate(current_schema, data, fragments, processor, validator, options={})
|
||||||
return if data.nil? && current_schema.schema['x-nullable'] == true
|
return if data.nil? && current_schema.schema['x-nullable'] == true
|
||||||
super
|
super
|
||||||
|
|||||||
@ -5,6 +5,10 @@ module Rswag
|
|||||||
rake_tasks do
|
rake_tasks do
|
||||||
load File.expand_path('../../../tasks/rswag-specs_tasks.rake', __FILE__)
|
load File.expand_path('../../../tasks/rswag-specs_tasks.rake', __FILE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
generators do
|
||||||
|
require 'generators/rspec/swagger_generator.rb'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -54,7 +54,7 @@ module Rswag
|
|||||||
definitions[key]
|
definitions[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_verb(request, metadata)
|
def add_verb(request, metadata)
|
||||||
request[:verb] = metadata[:operation][:verb]
|
request[:verb] = metadata[:operation][:verb]
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Content-Type header
|
# Content-Type header
|
||||||
consumes = metadata[:operation][:consumes] || swagger_doc[:consumes]
|
consumes = metadata[:operation][:consumes] || swagger_doc[:consumes]
|
||||||
if consumes
|
if consumes
|
||||||
content_type = example.respond_to?(:'Content-Type') ? example.send(:'Content-Type') : consumes.first
|
content_type = example.respond_to?(:'Content-Type') ? example.send(:'Content-Type') : consumes.first
|
||||||
tuples << [ 'Content-Type', content_type ]
|
tuples << [ 'Content-Type', content_type ]
|
||||||
|
|||||||
@ -14,17 +14,19 @@ module Rswag
|
|||||||
def validate!(metadata, response)
|
def validate!(metadata, response)
|
||||||
swagger_doc = @config.get_swagger_doc(metadata[:swagger_doc])
|
swagger_doc = @config.get_swagger_doc(metadata[:swagger_doc])
|
||||||
|
|
||||||
validate_code!(metadata, response.code)
|
validate_code!(metadata, response)
|
||||||
validate_headers!(metadata, response.headers)
|
validate_headers!(metadata, response.headers)
|
||||||
validate_body!(metadata, swagger_doc, response.body)
|
validate_body!(metadata, swagger_doc, response.body)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def validate_code!(metadata, code)
|
def validate_code!(metadata, response)
|
||||||
expected = metadata[:response][:code].to_s
|
expected = metadata[:response][:code].to_s
|
||||||
if code != expected
|
if response.code != expected
|
||||||
raise UnexpectedResponse, "Expected response code '#{code}' to match '#{expected}'"
|
raise UnexpectedResponse,
|
||||||
|
"Expected response code '#{response.code}' to match '#{expected}'\n" \
|
||||||
|
"Response body: #{response.body}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,11 @@ module Rswag
|
|||||||
metadata = notification.metadata
|
metadata = notification.metadata
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# !metadata[:document] won't work, since nil means we should generate
|
||||||
|
# docs.
|
||||||
|
return if metadata[:document] == false
|
||||||
return unless metadata.has_key?(:response)
|
return unless metadata.has_key?(:response)
|
||||||
|
|
||||||
swagger_doc = @config.get_swagger_doc(metadata[:swagger_doc])
|
swagger_doc = @config.get_swagger_doc(metadata[:swagger_doc])
|
||||||
swagger_doc.deep_merge!(metadata_to_swagger(metadata))
|
swagger_doc.deep_merge!(metadata_to_swagger(metadata))
|
||||||
end
|
end
|
||||||
@ -37,7 +41,7 @@ module Rswag
|
|||||||
FileUtils.mkdir_p dirname unless File.exists?(dirname)
|
FileUtils.mkdir_p dirname unless File.exists?(dirname)
|
||||||
|
|
||||||
File.open(file_path, 'w') do |file|
|
File.open(file_path, 'w') do |file|
|
||||||
file.write(JSON.pretty_generate(doc))
|
file.write(pretty_generate(doc))
|
||||||
end
|
end
|
||||||
|
|
||||||
@output.puts "Swagger doc generated at #{file_path}"
|
@output.puts "Swagger doc generated at #{file_path}"
|
||||||
@ -46,6 +50,20 @@ module Rswag
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def pretty_generate(doc)
|
||||||
|
if @config.swagger_format == :yaml
|
||||||
|
clean_doc = yaml_prepare(doc)
|
||||||
|
YAML.dump(clean_doc)
|
||||||
|
else # config errors are thrown in 'def swagger_format', no throw needed here
|
||||||
|
JSON.pretty_generate(doc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def yaml_prepare(doc)
|
||||||
|
json_doc = JSON.pretty_generate(doc)
|
||||||
|
clean_doc = JSON.parse(json_doc)
|
||||||
|
end
|
||||||
|
|
||||||
def metadata_to_swagger(metadata)
|
def metadata_to_swagger(metadata)
|
||||||
response_code = metadata[:response][:code]
|
response_code = metadata[:response][:code]
|
||||||
response = metadata[:response].reject { |k,v| k == :code }
|
response = metadata[:response].reject { |k,v| k == :code }
|
||||||
|
|||||||
@ -5,7 +5,10 @@ namespace :rswag do
|
|||||||
|
|
||||||
desc 'Generate Swagger JSON files from integration specs'
|
desc 'Generate Swagger JSON files from integration specs'
|
||||||
RSpec::Core::RakeTask.new('swaggerize') do |t|
|
RSpec::Core::RakeTask.new('swaggerize') do |t|
|
||||||
t.pattern = 'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb'
|
t.pattern = ENV.fetch(
|
||||||
|
'PATTERN',
|
||||||
|
'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb'
|
||||||
|
)
|
||||||
|
|
||||||
# NOTE: rspec 2.x support
|
# NOTE: rspec 2.x support
|
||||||
if Rswag::Specs::RSPEC_VERSION > 2 && Rswag::Specs.config.swagger_dry_run
|
if Rswag::Specs::RSPEC_VERSION > 2 && Rswag::Specs.config.swagger_dry_run
|
||||||
@ -16,3 +19,5 @@ namespace :rswag do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
task :rswag => ['rswag:specs:swaggerize']
|
||||||
|
|||||||
@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|||||||
|
|
||||||
s.files = Dir["{lib}/**/*"] + ["MIT-LICENSE", "Rakefile" ]
|
s.files = Dir["{lib}/**/*"] + ["MIT-LICENSE", "Rakefile" ]
|
||||||
|
|
||||||
s.add_dependency 'activesupport', '>= 3.1', '< 6.0'
|
s.add_dependency 'activesupport', '>= 3.1', '< 7.0'
|
||||||
s.add_dependency 'railties', '>= 3.1', '< 6.0'
|
s.add_dependency 'railties', '>= 3.1', '< 7.0'
|
||||||
s.add_dependency 'json-schema', '~> 2.2'
|
s.add_dependency 'json-schema', '~> 2.2'
|
||||||
end
|
end
|
||||||
|
|||||||
44
rswag-specs/spec/generators/rspec/swagger_generator_spec.rb
Normal file
44
rswag-specs/spec/generators/rspec/swagger_generator_spec.rb
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
require 'generator_spec'
|
||||||
|
require 'generators/rspec/swagger_generator'
|
||||||
|
require 'tmpdir'
|
||||||
|
|
||||||
|
module Rspec
|
||||||
|
describe SwaggerGenerator do
|
||||||
|
include GeneratorSpec::TestCase
|
||||||
|
destination Dir.mktmpdir
|
||||||
|
|
||||||
|
before(:all) do
|
||||||
|
prepare_destination
|
||||||
|
fixtures_dir = File.expand_path('../fixtures', __FILE__)
|
||||||
|
FileUtils.cp_r("#{fixtures_dir}/spec", destination_root)
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:all) do
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'installs the swagger_helper for rspec' do
|
||||||
|
allow_any_instance_of(Rswag::RouteParser).to receive(:routes).and_return(fake_routes)
|
||||||
|
run_generator ['Posts::CommentsController']
|
||||||
|
|
||||||
|
assert_file('spec/requests/posts/comments_spec.rb') do |content|
|
||||||
|
assert_match(/parameter name: 'post_id', in: :path, type: :string/, content)
|
||||||
|
assert_match(/patch\('update_comments comment'\)/, content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fake_routes
|
||||||
|
{
|
||||||
|
"/posts/{post_id}/comments/{id}" => {
|
||||||
|
:params => ["post_id", "id"],
|
||||||
|
:actions => {
|
||||||
|
"get" => { :summary=>"show comment" },
|
||||||
|
"patch" => { :summary=>"update_comments comment" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -4,7 +4,7 @@ require 'generators/rswag/specs/install/install_generator'
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe InstallGenerator do
|
RSpec.describe InstallGenerator do
|
||||||
include GeneratorSpec::TestCase
|
include GeneratorSpec::TestCase
|
||||||
destination File.expand_path('../tmp', __FILE__)
|
destination File.expand_path('../tmp', __FILE__)
|
||||||
|
|
||||||
|
|||||||
@ -3,10 +3,12 @@ require 'rswag/specs/configuration'
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe Configuration do
|
RSpec.describe Configuration do
|
||||||
subject { described_class.new(rspec_config) }
|
subject { described_class.new(rspec_config) }
|
||||||
|
|
||||||
let(:rspec_config) { OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs) }
|
let(:rspec_config) do
|
||||||
|
OpenStruct.new(swagger_root: swagger_root, swagger_docs: swagger_docs, swagger_format: swagger_format)
|
||||||
|
end
|
||||||
let(:swagger_root) { 'foobar' }
|
let(:swagger_root) { 'foobar' }
|
||||||
let(:swagger_docs) do
|
let(:swagger_docs) do
|
||||||
{
|
{
|
||||||
@ -14,6 +16,7 @@ module Rswag
|
|||||||
'v2/swagger.json' => { info: { title: 'v2' } }
|
'v2/swagger.json' => { info: { title: 'v2' } }
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
let(:swagger_format) { :yaml }
|
||||||
|
|
||||||
describe '#swagger_root' do
|
describe '#swagger_root' do
|
||||||
let(:response) { subject.swagger_root }
|
let(:response) { subject.swagger_root }
|
||||||
@ -46,6 +49,26 @@ module Rswag
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#swagger_format' do
|
||||||
|
let(:response) { subject.swagger_format }
|
||||||
|
|
||||||
|
context 'provided in rspec config' do
|
||||||
|
it { expect(response).to be_an_instance_of(Symbol) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'unsupported format provided' do
|
||||||
|
let(:swagger_format) { :xml }
|
||||||
|
|
||||||
|
it { expect { response }.to raise_error ConfigurationError }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'not provided' do
|
||||||
|
let(:swagger_format) { nil }
|
||||||
|
|
||||||
|
it { expect(response).to eq(:json) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#get_swagger_doc(tag=nil)' do
|
describe '#get_swagger_doc(tag=nil)' do
|
||||||
let(:swagger_doc) { subject.get_swagger_doc(tag) }
|
let(:swagger_doc) { subject.get_swagger_doc(tag) }
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ require 'rswag/specs/example_group_helpers'
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe ExampleGroupHelpers do
|
RSpec.describe ExampleGroupHelpers do
|
||||||
subject { double('example_group') }
|
subject { double('example_group') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
@ -24,7 +24,7 @@ module Rswag
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#get|post|patch|put|delete|head(verb, summary)' do
|
describe '#get|post|patch|put|delete|head|options|trace(verb, summary)' do
|
||||||
before { subject.post('Creates a blog') }
|
before { subject.post('Creates a blog') }
|
||||||
|
|
||||||
it "delegates to 'describe' with 'operation' metadata" do
|
it "delegates to 'describe' with 'operation' metadata" do
|
||||||
|
|||||||
@ -3,7 +3,7 @@ require 'rswag/specs/example_helpers'
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe ExampleHelpers do
|
RSpec.describe ExampleHelpers do
|
||||||
subject { double('example') }
|
subject { double('example') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
@ -12,7 +12,7 @@ module Rswag
|
|||||||
allow(config).to receive(:get_swagger_doc).and_return(swagger_doc)
|
allow(config).to receive(:get_swagger_doc).and_return(swagger_doc)
|
||||||
stub_const('Rswag::Specs::RAILS_VERSION', 3)
|
stub_const('Rswag::Specs::RAILS_VERSION', 3)
|
||||||
end
|
end
|
||||||
let(:config) { double('config') }
|
let(:config) { double('config') }
|
||||||
let(:swagger_doc) do
|
let(:swagger_doc) do
|
||||||
{
|
{
|
||||||
securityDefinitions: {
|
securityDefinitions: {
|
||||||
|
|||||||
@ -3,13 +3,13 @@ require 'rswag/specs/request_factory'
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe RequestFactory do
|
RSpec.describe RequestFactory do
|
||||||
subject { RequestFactory.new(config) }
|
subject { RequestFactory.new(config) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(config).to receive(:get_swagger_doc).and_return(swagger_doc)
|
allow(config).to receive(:get_swagger_doc).and_return(swagger_doc)
|
||||||
end
|
end
|
||||||
let(:config) { double('config') }
|
let(:config) { double('config') }
|
||||||
let(:swagger_doc) { {} }
|
let(:swagger_doc) { {} }
|
||||||
let(:example) { double('example') }
|
let(:example) { double('example') }
|
||||||
let(:metadata) do
|
let(:metadata) do
|
||||||
|
|||||||
@ -3,13 +3,13 @@ require 'rswag/specs/response_validator'
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe ResponseValidator do
|
RSpec.describe ResponseValidator do
|
||||||
subject { ResponseValidator.new(config) }
|
subject { ResponseValidator.new(config) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(config).to receive(:get_swagger_doc).and_return(swagger_doc)
|
allow(config).to receive(:get_swagger_doc).and_return(swagger_doc)
|
||||||
end
|
end
|
||||||
let(:config) { double('config') }
|
let(:config) { double('config') }
|
||||||
let(:swagger_doc) { {} }
|
let(:swagger_doc) { {} }
|
||||||
let(:example) { double('example') }
|
let(:example) { double('example') }
|
||||||
let(:metadata) do
|
let(:metadata) do
|
||||||
|
|||||||
@ -3,8 +3,8 @@ require 'ostruct'
|
|||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
describe SwaggerFormatter do
|
RSpec.describe SwaggerFormatter do
|
||||||
subject { described_class.new(output, config) }
|
subject { described_class.new(output, config) }
|
||||||
|
|
||||||
# Mock out some infrastructure
|
# Mock out some infrastructure
|
||||||
@ -26,41 +26,71 @@ module Rswag
|
|||||||
{
|
{
|
||||||
path_item: { template: '/blogs' },
|
path_item: { template: '/blogs' },
|
||||||
operation: { verb: :post, summary: 'Creates a blog' },
|
operation: { verb: :post, summary: 'Creates a blog' },
|
||||||
response: { code: '201', description: 'blog created' }
|
response: { code: '201', description: 'blog created' },
|
||||||
|
document: document
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'converts to swagger and merges into the corresponding swagger doc' do
|
context 'with the document tag set to false' do
|
||||||
expect(swagger_doc).to match(
|
let(:document) { false }
|
||||||
paths: {
|
|
||||||
'/blogs' => {
|
it 'does not update the swagger doc' do
|
||||||
post: {
|
expect(swagger_doc).to be_empty
|
||||||
summary: 'Creates a blog',
|
end
|
||||||
responses: {
|
end
|
||||||
'201' => { description: 'blog created' }
|
|
||||||
|
context 'with the document tag set to anything but false' do
|
||||||
|
# anything works, including its absence when specifying responses.
|
||||||
|
let(:document) { nil }
|
||||||
|
|
||||||
|
it 'converts to swagger and merges into the corresponding swagger doc' do
|
||||||
|
expect(swagger_doc).to match(
|
||||||
|
paths: {
|
||||||
|
'/blogs' => {
|
||||||
|
post: {
|
||||||
|
summary: 'Creates a blog',
|
||||||
|
responses: {
|
||||||
|
'201' => { description: 'blog created' }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#stop' do
|
describe '#stop' do
|
||||||
before do
|
before do
|
||||||
FileUtils.rm_r(swagger_root) if File.exists?(swagger_root)
|
FileUtils.rm_r(swagger_root) if File.exists?(swagger_root)
|
||||||
allow(config).to receive(:swagger_docs).and_return(
|
allow(config).to receive(:swagger_docs).and_return(
|
||||||
'v1/swagger.json' => { info: { version: 'v1' } },
|
'v1/swagger.json' => { info: { version: 'v1' } },
|
||||||
'v2/swagger.json' => { info: { version: 'v2' } }
|
'v2/swagger.json' => { info: { version: 'v2' } }
|
||||||
)
|
)
|
||||||
|
allow(config).to receive(:swagger_format).and_return(swagger_format)
|
||||||
subject.stop(notification)
|
subject.stop(notification)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:notification) { double('notification') }
|
let(:notification) { double('notification') }
|
||||||
|
context 'with default format' do
|
||||||
|
let(:swagger_format) { :json }
|
||||||
|
|
||||||
it 'writes the swagger_doc(s) to file' do
|
it 'writes the swagger_doc(s) to file' do
|
||||||
expect(File).to exist("#{swagger_root}/v1/swagger.json")
|
expect(File).to exist("#{swagger_root}/v1/swagger.json")
|
||||||
expect(File).to exist("#{swagger_root}/v2/swagger.json")
|
expect(File).to exist("#{swagger_root}/v2/swagger.json")
|
||||||
|
expect { JSON.parse(File.read("#{swagger_root}/v2/swagger.json")) }.not_to raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with yaml format' do
|
||||||
|
let(:swagger_format) { :yaml }
|
||||||
|
|
||||||
|
it 'writes the swagger_doc(s) as yaml' do
|
||||||
|
expect(File).to exist("#{swagger_root}/v1/swagger.json")
|
||||||
|
expect { JSON.parse(File.read("#{swagger_root}/v1/swagger.json")) }.to raise_error(JSON::ParserError)
|
||||||
|
# Psych::DisallowedClass would be raised if we do not pre-process ruby symbols
|
||||||
|
expect { YAML.safe_load(File.read("#{swagger_root}/v1/swagger.json")) }.not_to raise_error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
after do
|
after do
|
||||||
|
|||||||
@ -2,11 +2,11 @@ Rswag::Ui.configure do |c|
|
|||||||
|
|
||||||
# List the Swagger endpoints that you want to be documented through the swagger-ui
|
# List the Swagger endpoints that you want to be documented through the swagger-ui
|
||||||
# The first parameter is the path (absolute or relative to the UI host) to the corresponding
|
# The first parameter is the path (absolute or relative to the UI host) to the corresponding
|
||||||
# JSON endpoint and the second is a title that will be displayed in the document selector
|
# endpoint and the second is a title that will be displayed in the document selector
|
||||||
# NOTE: If you're using rspec-api to expose Swagger files (under swagger_root) as JSON endpoints,
|
# NOTE: If you're using rspec-api to expose Swagger files (under swagger_root) as JSON or YAML endpoints,
|
||||||
# then the list below should correspond to the relative paths for those endpoints
|
# then the list below should correspond to the relative paths for those endpoints
|
||||||
|
|
||||||
c.swagger_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs'
|
c.swagger_endpoint '/api-docs/v1/swagger.yaml', 'API V1 Docs'
|
||||||
|
|
||||||
# Add Basic Auth in case your API is private
|
# Add Basic Auth in case your API is private
|
||||||
# c.basic_auth_enabled = true
|
# c.basic_auth_enabled = true
|
||||||
|
|||||||
6
rswag-ui/package-lock.json
generated
6
rswag-ui/package-lock.json
generated
@ -5,9 +5,9 @@
|
|||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"swagger-ui-dist": {
|
"swagger-ui-dist": {
|
||||||
"version": "3.17.3",
|
"version": "3.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.17.3.tgz",
|
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.18.2.tgz",
|
||||||
"integrity": "sha1-37lkCMzEZ3UVX3NpGQxdSyAW/lw="
|
"integrity": "sha512-pWAEiKkgWUJvjmLW9AojudnutJ+NTn5g6OdNLj1iIJWwCkoy40K3Upwa24DqFbmIE4vLX4XplND61hp2L+s5vg=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"swagger-ui-dist": "3.17.3"
|
"swagger-ui-dist": "3.18.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,6 @@ Gem::Specification.new do |s|
|
|||||||
|
|
||||||
s.files = Dir.glob("{lib,node_modules}/**/*") + ["MIT-LICENSE", "Rakefile" ]
|
s.files = Dir.glob("{lib,node_modules}/**/*") + ["MIT-LICENSE", "Rakefile" ]
|
||||||
|
|
||||||
s.add_dependency 'actionpack', '>=3.1', '< 6.0'
|
s.add_dependency 'actionpack', '>=3.1', '< 7.0'
|
||||||
s.add_dependency 'railties', '>= 3.1', '< 6.0'
|
s.add_dependency 'railties', '>= 3.1', '< 7.0'
|
||||||
end
|
end
|
||||||
|
|||||||
2
test-app/app/assets/config/manifest.js
Normal file
2
test-app/app/assets/config/manifest.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
//= link_tree ../images
|
||||||
|
//= link_directory ../stylesheets .css
|
||||||
0
test-app/app/assets/images/.keep
Normal file
0
test-app/app/assets/images/.keep
Normal file
@ -1,6 +1,6 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
feature 'swagger-ui', js: true do
|
RSpec.feature 'swagger-ui', js: true do
|
||||||
|
|
||||||
scenario 'browsing api-docs' do
|
scenario 'browsing api-docs' do
|
||||||
visit '/api-docs'
|
visit '/api-docs'
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
require 'swagger_helper'
|
require 'swagger_helper'
|
||||||
|
|
||||||
describe 'Auth Tests API', type: :request, swagger_doc: 'v1/swagger.json' do
|
RSpec.describe 'Auth Tests API', type: :request, swagger_doc: 'v1/swagger.json' do
|
||||||
|
|
||||||
path '/auth-tests/basic' do
|
path '/auth-tests/basic' do
|
||||||
post 'Authenticates with basic auth' do
|
post 'Authenticates with basic auth' do
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
require 'swagger_helper'
|
require 'swagger_helper'
|
||||||
|
|
||||||
describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
|
RSpec.describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
|
||||||
let(:api_key) { 'fake_key' }
|
let(:api_key) { 'fake_key' }
|
||||||
|
|
||||||
path '/blogs' do
|
path '/blogs' do
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rake'
|
require 'rake'
|
||||||
|
|
||||||
describe 'rswag:specs:swaggerize' do
|
RSpec.describe 'rswag:specs:swaggerize' do
|
||||||
let(:swagger_root) { Rails.root.to_s + '/swagger' }
|
let(:swagger_root) { Rails.root.to_s + '/swagger' }
|
||||||
before do
|
before do
|
||||||
TestApp::Application.load_tasks
|
TestApp::Application.load_tasks
|
||||||
FileUtils.rm_r(swagger_root) if File.exists?(swagger_root)
|
FileUtils.rm_r(swagger_root) if File.exists?(swagger_root)
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user