76 Commits
2.3.0 ... 2.4.0

Author SHA1 Message Date
Blake Erickson
0aca50c66c Update changelog with 2.4.0 info 2021-02-09 11:51:34 -07:00
Blake Erickson
9644a16bce Merge branch 'splitpayments-dry-run-env' 2021-02-09 08:07:35 -07:00
Blake Erickson
52939874d6 Clean up README merge conflict 2021-02-09 08:05:58 -07:00
Blake Erickson
7ef900ec1d version bump 2021-02-07 07:55:52 -07:00
Blake Erickson
c0142093d4 Update swagger-ui to 3.42.0 2021-02-07 07:47:52 -07:00
Blake Erickson
1f4ecb3c10 update changelog 2021-02-07 06:31:47 -07:00
Blake Erickson
aa4e6f2070 Include example definition in test app
From changes in this commit: eadaf34ef6

we need to include the new output in the test-app swagger.json file
2021-02-06 17:42:23 -07:00
Blake Erickson
eadaf34ef6 Merge pull request #394 from donny741/master
Fix response examples
2021-02-06 07:30:53 -07:00
Donatas Povilaitis
cbaf6cd3e4 Fix response examples 2021-01-29 20:47:46 +02:00
Blake Erickson
670c94cc44 Update version to 2.3.2 in changelog 2021-01-27 14:53:15 -07:00
Blake Erickson
b86d3063a8 Merge pull request #371 from rswag/jamie
PR Grooming
2021-01-27 07:59:59 -07:00
Blake Erickson
7f88c5d1f3 Merge branch 'master' into jamie 2021-01-27 05:18:35 -07:00
Blake Erickson
d645df207e Merge pull request #392 from pfilipow/master
don't clobber response content
2021-01-26 18:10:21 -07:00
Paul Filipow
b63dd343af safe navigation in build_query_string_part 2021-01-26 13:57:44 -08:00
Paul Filipow
bd48e40529 add schema to mime type instead of overwriting 2021-01-25 21:12:16 -08:00
Paul Filipow
465950a65c don't clobber response content 2021-01-25 16:11:54 -08:00
Blake Erickson
be160c5552 Merge pull request #391 from codeart/master
Inherit consumes from swagger schema. Addresses #320.
2021-01-25 14:30:33 -07:00
Alexander
ad95f1098a Inherit consumes from swagger schema. Addresses #320. 2021-01-22 13:25:27 +02:00
Jamie Macey
83567e0ee2 Merge pull request #372 from rswag/spec-deprecations
Clean up spec ouput
2020-10-18 17:59:40 -07:00
Jamie Macey
206c088d74 WIP-branch changelog updates 2020-10-17 14:16:21 -07:00
Jamie Macey
5a60dee838 Add a spec for #342 body/required 2020-10-17 14:15:41 -07:00
Jamie Macey
13a3977c2c Merge remote-tracking branch 'bspellacy/master' into jamie 2020-10-17 13:56:19 -07:00
Jamie Macey
0d1a742f6f empty content is now pruned 2020-10-17 13:44:04 -07:00
Jamie Macey
c62bfda91d Merge branch 'output-specs' into jamie 2020-10-17 13:43:05 -07:00
Jamie Macey
c161de3899 Merge branch 'spec-deprecations' into jamie 2020-10-17 13:43:00 -07:00
Jamie Macey
ab457743a8 also move away from deprecated type: basic 2020-10-17 13:38:56 -07:00
Jamie Macey
3e10b09f23 swap deprecated SecurityDefinitions for SecuritySchemes 2020-10-17 13:38:56 -07:00
Jamie Macey
d090516f48 properly list servers for openapi v3 2020-10-17 13:38:56 -07:00
Jamie Macey
91a27852f2 more flex in this check 2020-10-17 13:38:56 -07:00
Jamie Macey
68e64dba2c OpenAPI currently at v 3.0.3 2020-10-17 13:38:56 -07:00
Jamie Macey
f6630cc7a6 fix a bug related to upgrade_request_type! translation 2020-10-17 13:38:56 -07:00
Jamie Macey
7e1a79220c start standing up exhaustive output unit tests for OpenAPI v3 2020-10-17 13:38:56 -07:00
Jamie Macey
b37c7905cd stub controller to help with future testing 2020-10-17 13:38:56 -07:00
Jamie Macey
0a9487b328 make spec output less noisy 2020-10-17 13:34:17 -07:00
Jamie Macey
b3fa5ba54e Merge remote-tracking branch 'psmandzich/master' into jamie 2020-10-17 13:21:28 -07:00
Jamie Macey
7e2c52b242 Merge remote-tracking branch 'olegkyz/bugfix/examples-content' into jamie 2020-10-17 13:14:47 -07:00
Jamie Macey
8d673068af Merge pull request #369 from jamie/actions
CI via Github Actions
2020-10-17 08:20:36 -07:00
Greg Myers
d558bd4774 Merge pull request #334 from akofink/issue-333
Update wording in gemspecs to include OpenAPI
2020-10-14 23:08:13 +01:00
Jamie Macey
c990348f0b update cache hash 2020-10-12 13:56:22 -07:00
Jamie Macey
38c861f5e0 strip prelude 2020-10-12 13:51:42 -07:00
Jamie Macey
1cd7df89f1 update conditional tags 2020-10-12 13:48:24 -07:00
Jamie Macey
04059808d5 fix matrix syntax 2020-10-12 13:45:38 -07:00
Jamie Macey
4c06f95a68 run multiple ruby/rails versions 2020-10-12 13:43:58 -07:00
Jamie Macey
08b1678e53 skip a test requiring headless firefox capybara driver 2020-10-12 13:37:57 -07:00
Jamie Macey
27b015b3d2 ensure we run all specs if there are early failures 2020-10-12 13:29:28 -07:00
Jamie Macey
eb14eba7eb build individually 2020-10-12 13:26:31 -07:00
Jamie Macey
e419635209 cache gems/npm 2020-10-12 13:20:46 -07:00
Jamie Macey
ea89b0b0d5 Github Actions CI 2020-10-12 13:16:17 -07:00
Jamie Macey
423e86c463 bump ruby-version, stricter npm lockfile 2020-10-12 13:13:53 -07:00
Greg Myers
89f95694a4 Merge pull request #349 from MikeSmithEU/patch-1
Fix typo in test-app/app/controllers/blogs_controller.rb
2020-08-21 17:08:13 +01:00
Greg Myers
6133179aa5 Merge pull request #312 from levidavidmurray/master
Update README.md
2020-08-21 17:07:12 +01:00
Greg Myers
89e69915f8 Merge pull request #335 from zkatemor/master
Update version Swagger UI to 3.28.0
2020-08-17 15:04:31 +01:00
Greg Myers
4e300b2255 Update changelog, readme
Swagger-ui version no.
2020-08-17 15:04:22 +01:00
Greg Myers
3cb3f4a651 Merge pull request #324 from graceful-potato/fix_route_parser
Fix routes method in RoutePareser
2020-08-17 15:03:00 +01:00
MikeSmithEU
267007ef1b Update blogs_controller.rb
Fix typo
2020-07-22 11:23:01 +02:00
psmandzich
347f9da32e enable swagger empty body responses 2020-07-21 09:50:47 +02:00
Brennan Spellacy
30002e5b98 Add required field 2020-07-01 20:20:41 -07:00
Ekaterina Zababurina
a7944d48d6 Update Swagger UI to 3.28.0 2020-06-30 09:45:55 +03:00
Ekaterina Zababurina
015b1c8b56 Update Swagger UI to 3.27.0 2020-06-25 13:37:23 +03:00
Ekaterina Zababurina
36e79da0fd Update swagger UI to 3.26.2 2020-06-16 10:15:35 +03:00
Ekaterina Zababurina
3aff6bca1f Update version Swagger UI to 3.26.0 2020-06-10 09:19:57 +03:00
Andrew Kofink
f0fba9c093 Update wording in gemspecs to include OpenAPI
Fixes #333

Mentioning OpenAPI in the gemspec allows users on rubygems.org to find
this gem easily. Those who are still looking for Swagger tools will also
continue to find the gem, since I've mentioned that OpenAPI used to be
called Swagger.

Signed-off-by: Andrew Kofink <akofink@redhat.com>
2020-06-08 12:00:08 -04:00
Greg Myers
bec4f16676 fix #322 2020-06-06 19:56:28 +01:00
Oleg Yakovenko
7c3c357291 cleanup 2020-06-04 17:22:20 +03:00
Oleg Yakovenko
81c110022e example json actualized 2020-06-04 17:19:15 +03:00
Oleg Yakovenko
1ff396fb56 example group helper adjustment 2020-06-04 17:17:30 +03:00
Oleg Yakovenko
9c297317b2 keep examples content 2020-06-04 16:21:43 +03:00
Graceful Potato
3b85f09acf Fix RoutePareser#routes and add spec for it 2020-05-26 02:01:38 +03:00
Levi Murray
9727ec34b7 Update README.md 2020-05-03 13:58:04 -07:00
Greg Myers
7ceedab4cb Update CHANGELOG.md 2020-04-08 23:12:33 +01:00
Greg Myers
e7fb44fcf5 Merge pull request #296 from rswag/BookOfGreg-patch-1
fix #295
2020-04-08 23:10:55 +01:00
Greg Myers
2797866224 fix #295 2020-04-08 23:09:31 +01:00
Greg Myers
4de6661f4c Update README.md 2020-04-08 23:08:46 +01:00
Igor Kapkov
9916d3f0b0 Merge branch 'master' into dry-run-env 2020-03-25 16:59:30 +11:00
Igor Kapkov
56eec5948e Update readme 2020-03-25 16:57:58 +11:00
Igor Kapkov
7b01ae1aa1 Add ENV var to configure --dry-run 2020-01-22 15:54:24 +11:00
33 changed files with 963 additions and 106 deletions

59
.github/workflows/ruby.yml vendored Normal file
View File

@@ -0,0 +1,59 @@
name: Ruby
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
ruby: [2.6, 2.7]
rails: [5.2.4.4, 6.0.3.4]
env:
RAILS_VERSION: ${{ matrix.rails }}
steps:
- uses: actions/checkout@v2
- uses: ruby/setup-ruby@v1
with: { ruby-version: 2.6 }
- uses: actions/cache@v2
id: cache
with:
path: |
rswag-ui/node_modules
vendor/bundle
key: ${{ runner.os }}-ruby_${{ matrix.ruby }}-rails_${{ matrix.rails }}-${{ hashFiles('Gemfile', '**/package-lock.json') }}
- name: Install dependencies
run: |
bundle install
cd rswag-ui && npm install
- name: rswag-api
run: |
cd rswag-api
bundle exec rspec
- name: rswag-specs
if: success() || failure()
run: |
cd rswag-specs
bundle exec rspec
- name: rswag-ui
if: success() || failure()
run: |
cd rswag-ui
bundle exec rspec
- name: test-app
if: success() || failure()
run: |
cd test-app
bundle exec rake db:migrate db:test:prepare
bundle exec rspec

View File

@@ -1 +1 @@
2.6.3 2.7.2

View File

@@ -1,16 +1,32 @@
# rswag # rswag
All notable changes to this project will be documented in this file. 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/), 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).
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
## [2.4.0] - 2021-02-09
### Added ### Added
### Changed - Added `SWAGGER_DRY_RUN` env variable [#274](https://github.com/rswag/rswag/pull/274)
### Deprecated
### Removed ## [2.3.3] - 2021-02-07
### Fixed ### Fixed
### Security - Include response examples [#394](https://github.com/rswag/rswag/pull/394)
### Changed
- Update swagger-ui to 3.42.0
## [2.3.2] - 2021-01-27
### Added
- RequestBody now supports the `required` flag [#342](https://github.com/rswag/rswag/pull/342)
### Fixed
- Fix response example rendering [#330](https://github.com/rswag/rswag/pull/330)
- Fix empty content block [#347](https://github.com/rswag/rswag/pull/347)
## [2.3.1] - 2020-04-08
### Fixed
- Remove require for byebug [#295](https://github.com/rswag/rswag/issues/295)
## [2.3.0] - 2020-04-05 ## [2.3.0] - 2020-04-05
### Added ### Added
@@ -24,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Update swagger-ui version to 3.23.11 [#239](https://github.com/rswag/rswag/pull/239) - Update swagger-ui version to 3.23.11 [#239](https://github.com/rswag/rswag/pull/239)
- Rails constraint moved from < 6.1 to < 7 [#253](https://github.com/rswag/rswag/pull/253) - Rails constraint moved from < 6.1 to < 7 [#253](https://github.com/rswag/rswag/pull/253)
- Swaggerize now outputs base RSpec text on completion to avoid silent failures [#293](https://github.com/rswag/rswag/pull/293) - Swaggerize now outputs base RSpec text on completion to avoid silent failures [#293](https://github.com/rswag/rswag/pull/293)
- Update swagger-ui version to 3.28.0
## [2.2.0] - 2019-11-01 ## [2.2.0] - 2019-11-01
### Added ### Added

20
MIT-LICENCE Normal file
View File

@@ -0,0 +1,20 @@
Copyright 2015 domaindrivendev
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -18,7 +18,8 @@ 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/rswag/rswag/tree/master)|3.0.3|3.23.11| |[master](https://github.com/rswag/rswag/tree/master)|3.0.3|3.42.0|
|[2.3.0](https://github.com/rswag/rswag/tree/2.3.0)|3.0.3|3.23.11|
|[2.2.0](https://github.com/rswag/rswag/tree/2.2.0)|2.0|3.18.2| |[2.2.0](https://github.com/rswag/rswag/tree/2.2.0)|2.0|3.18.2|
|[1.6.0](https://github.com/rswag/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|
@@ -362,8 +363,8 @@ you should use the folowing syntax, making sure there are no whitespaces at the
### 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. 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. 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, :bearer, :apiKey and :oauth2 and :openIdConnect scheme types. See [the spec](https://swagger.io/docs/specification/authentication/) for more info, as this underwent major changes between Swagger 2.0 and Open API 3.0 Swagger supports :basic, :bearer, :apiKey and :oauth2 and :openIdConnect scheme types. See [the spec](https://swagger.io/docs/specification/authentication/) for more info, as this underwent major changes between Swagger 2.0 and Open API 3.0
```ruby ```ruby
@@ -415,7 +416,7 @@ describe 'Blogs API' do
end end
# example of documenting an endpoint that handles basic auth and api key based security # example of documenting an endpoint that handles basic auth and api key based security
describe 'Auth examples API' do describe 'Auth examples API' do
path '/auth-tests/basic-and-api-key' do path '/auth-tests/basic-and-api-key' do
post 'Authenticates with basic auth and api key' do post 'Authenticates with basic auth and api key' do
tags 'Auth Tests' tags 'Auth Tests'
@@ -436,11 +437,11 @@ describe 'Auth examples API' do
end end
end end
end end
``` ```
__NOTE:__ Depending on the scheme types, you'll be required to assign a corresponding parameter value with each example. __NOTE:__ Depending on the scheme types, you'll be required to assign a corresponding parameter value with each example.
For example, :basic auth is required above and so the :Authorization (header) parameter must be set accordingly For example, :basic auth is required above and so the :Authorization (header) parameter must be set accordingly
## Configuration & Customization ## ## Configuration & Customization ##
@@ -478,9 +479,9 @@ 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. 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. For example, you might have a standard response structure for all failed operations.
Again, this is a structure that changed since swagger 2.0. Notice the new "schemas" section for these. Again, this is a structure that changed since swagger 2.0. Notice the new "schemas" section for these.
Rather than repeating the schema in every operation spec, you can define it globally and provide a reference to it in each spec: Rather than repeating the schema in every operation spec, you can define it globally and provide a reference to it in each spec:
```ruby ```ruby
@@ -515,6 +516,15 @@ config.swagger_docs = {
thumbnail: { type: 'string', nullable: true } thumbnail: { type: 'string', nullable: true }
}, },
required: %w[id title] required: %w[id title]
},
new_blog: {
type: 'object',
properties: {
title: { type: 'string' },
content: { type: 'string', nullable: true },
thumbnail: { type: 'string', format: 'binary', nullable: true }
},
required: %w[title]
} }
} }
} }
@@ -528,6 +538,8 @@ describe 'Blogs API' do
post 'Creates a blog' do post 'Creates a blog' do
parameter name: :new_blog, in: :body, schema: { '$ref' => '#/components/schemas/new_blog' }
response 422, 'invalid request' do response 422, 'invalid request' do
schema '$ref' => '#/components/schemas/errors_object' schema '$ref' => '#/components/schemas/errors_object'
... ...
@@ -548,7 +560,7 @@ end
### Response headers ### ### Response headers ###
In Rswag, you could use `header` method inside the response block to specify header objects for this response. In Rswag, you could use `header` method inside the response block to specify header objects for this response.
Rswag will validate your response headers with those header objects and inject them into the generated swagger file: Rswag will validate your response headers with those header objects and inject them into the generated swagger file:
```ruby ```ruby
@@ -596,19 +608,20 @@ To enable examples generation from responses add callback above run_test! like:
``` ```
after do |example| after do |example|
example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) } example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end end
``` ```
You need to disable --dry-run option for Rspec > 3 #### Dry Run Option ####
<!-- This is now enabled by default in rswag. The `--dry-run` option is enabled by default for Rspec 3, but if you need to
You need to set the ``` config.swagger_dry_run = false``` value in the spec/spec_helper.rb file. disable it you can use the environment varible `SWAGGER_DRY_RUN=0` during the
This is one of the more powerful features of rswag. When rswag runs your integration test suite via ```bundle exec rspec```, it will capture the request and response bodies and output those values in the examples section. generation command or add the following to your `config/environments/test.rb`:
These integration tests are usually written with ```let``` variables for post body parameters, and since its an integration test the service is returning actual values.
We might as well re-use these values and embed them into the generated swagger to provide a more real world example for request/response examples. -->
Add to config/environments/test.rb:
```ruby ```ruby
RSpec.configure do |config| RSpec.configure do |config|
config.swagger_dry_run = false config.swagger_dry_run = false
@@ -644,8 +657,8 @@ describe 'Blogs API', document: false do
``` ```
##### rswag helper methods ##### ##### rswag helper methods #####
<!-- <!--
There are some helper methods to help with documenting request bodies. There are some helper methods to help with documenting request bodies.
```ruby ```ruby
describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
let(:api_key) { 'fake_key' } let(:api_key) { 'fake_key' }
@@ -681,7 +694,7 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
end end
end end
end end
end end
``` ```
In the above example, we see methods ```request_body_json``` ```request_body_plain``` ```request_body_xml```. In the above example, we see methods ```request_body_json``` ```request_body_plain``` ```request_body_xml```.
@@ -691,7 +704,7 @@ and the examples: :blog which will create a named example "blog" under the "requ
Again, documenting request response examples changed in Open API 3.0. The example above would generate a swagger.json snippet that looks like this: Again, documenting request response examples changed in Open API 3.0. The example above would generate a swagger.json snippet that looks like this:
```json ```json
... ...
{"requestBody": { {"requestBody": {
"required": true, "required": true,
"content": { "content": {
@@ -725,9 +738,9 @@ Again, documenting request response examples changed in Open API 3.0. The exampl
} }
``` ```
*NOTE:* for this example request body to work in the tests properly, you need to ``let`` a variable named *blog*. *NOTE:* for this example request body to work in the tests properly, you need to ``let`` a variable named *blog*.
The variable with the matching name (blog in this case) is eval-ed and captured to be placed in the examples section. The variable with the matching name (blog in this case) is eval-ed and captured to be placed in the examples section.
This ```let``` value is used in the integration test to run the test AND captured and injected into the requestBody section. This ```let``` value is used in the integration test to run the test AND captured and injected into the requestBody section.
##### rswag response examples ##### ##### rswag response examples #####
@@ -825,7 +838,7 @@ You can specify custom headers for serving your generated Swagger JSON. For exam
```ruby ```ruby
Rswag::Api.configure do |c| Rswag::Api.configure do |c|
... ...
c.swagger_headers = { 'Content-Type' => 'application/json; charset=UTF-8' } c.swagger_headers = { 'Content-Type' => 'application/json; charset=UTF-8' }
end end
``` ```
@@ -897,6 +910,5 @@ docker pull swaggerapi/swagger-editor
``` ```
docker run -d -p 80:8080 swaggerapi/swagger-editor docker run -d -p 80:8080 swaggerapi/swagger-editor
``` ```
This will run the swagger editor in the docker daemon and can be accessed This will run the swagger editor in the docker daemon and can be accessed
at ```http://localhost```. From here, you can use the UI to load the generated swagger.json to validate the output. at ```http://localhost```. From here, you can use the UI to load the generated swagger.json to validate the output.

View File

@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian'] s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian']
s.email = ['domaindrivendev@gmail.com'] s.email = ['domaindrivendev@gmail.com']
s.homepage = 'https://github.com/rswag/rswag' s.homepage = 'https://github.com/rswag/rswag'
s.summary = 'A Rails Engine that exposes Swagger files as JSON endpoints' s.summary = 'A Rails Engine that exposes OpenAPI (formerly called Swagger) files as JSON endpoints'
s.description = 'Open up your API to the phenomenal Swagger ecosystem by exposing Swagger files, that describe your service, as JSON endpoints' s.description = 'Open up your API to the phenomenal OpenAPI ecosystem by exposing OpenAPI files, that describe your service, as JSON endpoints. More about the OpenAPI initiative here: http://spec.openapis.org/'
s.license = 'MIT' s.license = 'MIT'
s.files = Dir['{lib}/**/*'] + ['MIT-LICENSE', 'Rakefile'] s.files = Dir['{lib}/**/*'] + ['MIT-LICENSE', 'Rakefile']

View File

@@ -19,7 +19,11 @@ RSpec.describe '<%= controller_path %>', type: :request do
<% end -%> <% end -%>
after do |example| after do |example|
example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) } example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end end
run_test! run_test!
end end

View File

@@ -11,7 +11,7 @@ module Rswag
def routes def routes
::Rails.application.routes.routes.select do |route| ::Rails.application.routes.routes.select do |route|
route.defaults[:controller] == controller route.defaults[:controller] == controller
end.each_with_object({}) do |tree, route| end.each_with_object({}) do |route, tree|
path = path_from(route) path = path_from(route)
verb = verb_from(route) verb = verb_from(route)
tree[path] ||= { params: params_from(route), actions: {} } tree[path] ||= { params: params_from(route), actions: {} }

View File

@@ -28,9 +28,11 @@ module Rswag
end end
def swagger_dry_run def swagger_dry_run
@swagger_dry_run ||= begin return @swagger_dry_run if defined? @swagger_dry_run
@rspec_config.swagger_dry_run.nil? || @rspec_config.swagger_dry_run if ENV.key?('SWAGGER_DRY_RUN')
@rspec_config.swagger_dry_run = ENV['SWAGGER_DRY_RUN'] == '1'
end end
@swagger_dry_run = @rspec_config.swagger_dry_run.nil? || @rspec_config.swagger_dry_run
end end
def swagger_format def swagger_format

View File

@@ -72,7 +72,10 @@ module Rswag
def examples(example = nil) def examples(example = nil)
return super() if example.nil? return super() if example.nil?
metadata[:response][:examples] = example metadata[:response][:content] =
example.each_with_object({}) do |(mime, example_object), memo|
memo[mime] = { example: example_object }
end
end end
def run_test!(&block) def run_test!(&block)

View File

@@ -3,7 +3,6 @@
require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/hash/conversions' require 'active_support/core_ext/hash/conversions'
require 'json' require 'json'
require 'byebug'
module Rswag module Rswag
module Specs module Specs
@@ -119,7 +118,8 @@ module Rswag
def build_query_string_part(param, value) def build_query_string_part(param, value)
name = param[:name] name = param[:name]
return "#{name}=#{value}" unless param[:type].to_sym == :array type = param[:type] || param.dig(:schema, :type)
return "#{name}=#{value}" unless type&.to_sym == :array
case param[:collectionFormat] case param[:collectionFormat]
when :ssv when :ssv

View File

@@ -56,9 +56,10 @@ module Rswag
is_hash = value.is_a?(Hash) is_hash = value.is_a?(Hash)
if is_hash && value.dig(:parameters) if is_hash && value.dig(:parameters)
schema_param = value.dig(:parameters)&.find { |p| (p[:in] == :body || p[:in] == :formData) && p[:schema] } schema_param = value.dig(:parameters)&.find { |p| (p[:in] == :body || p[:in] == :formData) && p[:schema] }
mime_list = value.dig(:consumes) mime_list = value.dig(:consumes) || doc[:consumes]
if value && schema_param && mime_list if value && schema_param && mime_list
value[:requestBody] = { content: {} } unless value.dig(:requestBody, :content) value[:requestBody] = { content: {} } unless value.dig(:requestBody, :content)
value[:requestBody][:required] = true if schema_param[:required]
mime_list.each do |mime| mime_list.each do |mime|
value[:requestBody][:content][mime] = { schema: schema_param[:schema] } value[:requestBody][:content][mime] = { schema: schema_param[:schema] }
end end
@@ -129,13 +130,13 @@ module Rswag
end end
def upgrade_content!(mime_list, target_node) def upgrade_content!(mime_list, target_node)
target_node.merge!(content: {})
schema = target_node[:schema] schema = target_node[:schema]
return if mime_list.empty? || schema.nil? return if mime_list.empty? || schema.nil?
target_node[:content] ||= {}
mime_list.each do |mime_type| mime_list.each do |mime_type|
# TODO upgrade to have content-type specific schema # TODO upgrade to have content-type specific schema
target_node[:content][mime_type] = { schema: schema } (target_node[:content][mime_type] ||= {}).merge!(schema: schema)
end end
end end

View File

@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian'] s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian']
s.email = ['domaindrivendev@gmail.com'] s.email = ['domaindrivendev@gmail.com']
s.homepage = 'https://github.com/rswag/rswag' s.homepage = 'https://github.com/rswag/rswag'
s.summary = 'A Swagger-based DSL for rspec-rails & accompanying rake task for generating Swagger files' s.summary = 'An OpenAPI-based (formerly called Swagger) DSL for rspec-rails & accompanying rake task for generating OpenAPI specification files'
s.description = 'Simplify API integration testing with a succinct rspec DSL and generate Swagger files directly from your rspecs' s.description = 'Simplify API integration testing with a succinct rspec DSL and generate OpenAPI specification files directly from your rspecs. More about the OpenAPI initiative here: http://spec.openapis.org/'
s.license = 'MIT' s.license = 'MIT'
s.files = Dir['{lib}/**/*'] + ['MIT-LICENSE', 'Rakefile'] s.files = Dir['{lib}/**/*'] + ['MIT-LICENSE', 'Rakefile']

View File

@@ -0,0 +1,50 @@
# frozen_string_literal: true
RSpec.describe Rswag::RouteParser do
describe "#routes" do
let(:controller) { "api/v1/posts" }
subject { described_class.new(controller) }
let(:routes) do
[
double(
defaults: {
controller: controller
},
path: double(
spec: double(
to_s: "/api/v1/posts/:id(.:format)"
)
),
verb: "GET",
requirements: {
action: "show",
controller: "api/v1/posts"
},
segments: ["id", "format"]
)
]
end
let(:expectation) do
{
"/api/v1/posts/{id}" => {
params: ["id"],
actions: {
"get" => {
summary: "show post"
}
}
}
}
end
before do
allow(::Rails).to receive_message_chain("application.routes.routes") { routes }
end
it "returns correct routes" do
expect(subject.routes).to eq(expectation)
end
end
end

View File

@@ -137,9 +137,10 @@ module Rswag
end end
describe '#examples(example)' do describe '#examples(example)' do
let(:mime) { 'application/json' }
let(:json_example) do let(:json_example) do
{ {
'application/json' => { mime => {
foo: 'bar' foo: 'bar'
} }
} }
@@ -151,7 +152,11 @@ module Rswag
end end
it "adds to the 'response examples' metadata" do it "adds to the 'response examples' metadata" do
expect(api_metadata[:response][:examples]).to eq(json_example) expect(api_metadata[:response][:content]).to match(
mime => {
example: json_example[mime]
}
)
end end
end end
end end

View File

@@ -11,6 +11,8 @@ module Rswag
# Mock out some infrastructure # Mock out some infrastructure
before do before do
allow(config).to receive(:swagger_root).and_return(swagger_root) allow(config).to receive(:swagger_root).and_return(swagger_root)
allow(ActiveSupport::Deprecation).to receive(:warn) # Silence deprecation output from specs
end end
let(:config) { double('config') } let(:config) { double('config') }
let(:output) { double('output').as_null_object } let(:output) { double('output').as_null_object }
@@ -26,10 +28,11 @@ module Rswag
{ {
path_item: { template: '/blogs', parameters: [{ type: :string }] }, path_item: { template: '/blogs', parameters: [{ type: :string }] },
operation: { verb: :post, summary: 'Creates a blog', parameters: [{ type: :string }] }, operation: { verb: :post, summary: 'Creates a blog', parameters: [{ type: :string }] },
response: { code: '201', description: 'blog created', headers: { type: :string }, schema: { '$ref' => '#/definitions/blog' } }, response: response_metadata,
document: document document: document
} }
end end
let(:response_metadata) { { code: '201', description: 'blog created', headers: { type: :string }, schema: { '$ref' => '#/definitions/blog' } } }
context 'with the document tag set to false' do context 'with the document tag set to false' do
let(:swagger_doc) { { swagger: '2.0' } } let(:swagger_doc) { { swagger: '2.0' } }
@@ -127,6 +130,97 @@ module Rswag
) )
end end
context 'with response example' do
let(:response_metadata) do
{
code: '201',
description: 'blog created',
headers: { type: :string },
content: { 'application/json' => { example: { foo: :bar } } },
schema: { '$ref' => '#/definitions/blog' }
}
end
it 'adds example to definition' do
expect(swagger_doc.slice(:paths)).to match(
paths: {
'/blogs' => {
parameters: [{ schema: { type: :string } }],
post: {
parameters: [{ schema: { type: :string } }],
summary: 'Creates a blog',
responses: {
'201' => {
content: {
'application/vnd.my_mime' => {
schema: { '$ref' => '#/definitions/blog' }
},
'application/json' => {
schema: { '$ref' => '#/definitions/blog' },
example: { foo: :bar }
}
},
description: 'blog created',
headers: { schema: { type: :string } }
}
}
}
}
}
)
end
end
context 'with empty content' do
let(:swagger_doc) do
{
openapi: '3.0.1',
basePath: '/foo',
schemes: ['http', 'https'],
host: 'api.example.com',
components: {
securitySchemes: {
myClientCredentials: {
type: :oauth2,
flow: :application,
token_url: :somewhere
},
myAuthorizationCode: {
type: :oauth2,
flow: :accessCode,
token_url: :somewhere
},
myImplicit: {
type: :oauth2,
flow: :implicit,
token_url: :somewhere
}
}
}
}
end
it 'converts query and path params, type: to schema: { type: }' do
expect(swagger_doc.slice(:paths)).to match(
paths: {
'/blogs' => {
parameters: [{ schema: { type: :string } }],
post: {
parameters: [{ schema: { type: :string } }],
summary: 'Creates a blog',
responses: {
'201' => {
description: 'blog created',
headers: { schema: { type: :string } }
}
}
}
}
}
)
end
end
it 'converts basePath, schemas and host to urls' do it 'converts basePath, schemas and host to urls' do
expect(swagger_doc.slice(:servers)).to match( expect(swagger_doc.slice(:servers)).to match(
servers: { servers: {

View File

@@ -5,9 +5,9 @@
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"swagger-ui-dist": { "swagger-ui-dist": {
"version": "3.23.11", "version": "3.42.0",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.23.11.tgz", "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.42.0.tgz",
"integrity": "sha512-ipENHHH/sqpngTpHXUwg55eAOZ7b2UVayUwwuWPA6nQSPhjBVXX4zOPpNKUwQIFOl3oIwVvZF7mqoxH7pMgVzA==" "integrity": "sha512-hTNX6cX7KWtBZgk6ZQSOzsBJhqdCmD5NOIjb6dBPKSnYZidSkIXOcaPMR3+kwxLrj8bDC881bSDlNbLsHikacg=="
} }
} }
} }

View File

@@ -3,6 +3,6 @@
"version": "1.0.0", "version": "1.0.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"swagger-ui-dist": "3.23.11" "swagger-ui-dist": "3.42.0"
} }
} }

View File

@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian'] s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian']
s.email = ['domaindrivendev@gmail.com'] s.email = ['domaindrivendev@gmail.com']
s.homepage = 'https://github.com/rswag/rswag' s.homepage = 'https://github.com/rswag/rswag'
s.summary = 'A Rails Engine that includes swagger-ui and powers it from configured Swagger endpoints' s.summary = 'A Rails Engine that includes swagger-ui and powers it from configured OpenAPI (formerly named Swagger) endpoints'
s.description = 'Expose beautiful API documentation, powered by Swagger JSON endpoints, including a UI to explore and test operations' s.description = 'Expose beautiful API documentation, powered by Swagger JSON endpoints, including a UI to explore and test operations. More about the OpenAPI initiative here: http://spec.openapis.org/'
s.license = 'MIT' s.license = 'MIT'
s.files = Dir.glob('{lib,node_modules}/**/*') + ['MIT-LICENSE', 'Rakefile' ] s.files = Dir.glob('{lib,node_modules}/**/*') + ['MIT-LICENSE', 'Rakefile' ]

View File

@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian'] s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian']
s.email = ['domaindrivendev@gmail.com'] s.email = ['domaindrivendev@gmail.com']
s.homepage = 'https://github.com/rswag/rswag' s.homepage = 'https://github.com/rswag/rswag'
s.summary = 'Swagger tooling for Rails APIs' s.summary = 'OpenAPI (formerly named Swagger) tooling for Rails APIs'
s.description = 'Generate beautiful API documentation, including a UI to explore and test operations, directly from your rspec integration tests' s.description = 'Generate beautiful API documentation, including a UI to explore and test operations, directly from your rspec integration tests. OpenAPI 2 and 3 supported. More about the OpenAPI initiative here: http://spec.openapis.org/'
s.license = 'MIT' s.license = 'MIT'
s.files = Dir['{lib}/**/*'] + [ 'MIT-LICENSE' ] s.files = Dir['{lib}/**/*'] + [ 'MIT-LICENSE' ]

View File

@@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

View File

@@ -0,0 +1,4 @@
/*
Place all the styles related to the matching controller here.
They will automatically be included in application.css.
*/

View File

@@ -32,7 +32,7 @@ class BlogsController < ApplicationController
@blog = Blog.find_by_id(params[:id]) @blog = Blog.find_by_id(params[:id])
return head :not_found if @blog.nil? return head :not_found if @blog.nil?
@blog.thumbnail = save_uploaded_file params[:file] @blog.thumbnail = save_uploaded_file params[:file]
head @blog.save ? :ok : :unprocsessible_entity head @blog.save ? :ok : :unprocessable_entity
end end
# GET /blogs # GET /blogs

View File

@@ -0,0 +1,13 @@
class StubsController < ApplicationController
def index
render plain: 'OK'
end
def create
render plain: 'OK'
end
def show
render plain: 'OK'
end
end

View File

@@ -0,0 +1,2 @@
module StubsHelper
end

View File

@@ -9,6 +9,8 @@ TestApp::Application.routes.draw do
post 'auth-tests/api-key', to: 'auth_tests#api_key' post 'auth-tests/api-key', to: 'auth_tests#api_key'
post 'auth-tests/basic-and-api-key', to: 'auth_tests#basic_and_api_key' post 'auth-tests/basic-and-api-key', to: 'auth_tests#basic_and_api_key'
resources :stubs
mount Rswag::Api::Engine => 'api-docs' mount Rswag::Api::Engine => 'api-docs'
mount Rswag::Ui::Engine => 'api-docs' mount Rswag::Ui::Engine => 'api-docs'
end end

View File

@@ -3,6 +3,7 @@ require 'rails_helper'
RSpec.feature 'swagger-ui', js: true do RSpec.feature 'swagger-ui', js: true do
scenario 'browsing api-docs' do scenario 'browsing api-docs' do
skip "Needs work to run on others' machines"
visit '/api-docs' visit '/api-docs'
expect(page).to have_content('GET /blogs Searches blogs', normalize_ws: true) expect(page).to have_content('GET /blogs Searches blogs', normalize_ws: true)

View File

@@ -3,6 +3,10 @@
require 'swagger_helper' require 'swagger_helper'
RSpec.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
before do
allow(ActiveSupport::Deprecation).to receive(:warn) # Silence deprecation output from specs
end
path '/auth-tests/basic' do path '/auth-tests/basic' do
post 'Authenticates with basic auth' do post 'Authenticates with basic auth' do
tags 'Auth Tests' tags 'Auth Tests'

View File

@@ -3,6 +3,10 @@ require 'swagger_helper'
RSpec.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' }
before do
allow(ActiveSupport::Deprecation).to receive(:warn) # Silence deprecation output from specs
end
path '/blogs' do path '/blogs' do
post 'Creates a blog' do post 'Creates a blog' do
tags 'Blogs' tags 'Blogs'

View File

@@ -0,0 +1,349 @@
require 'swagger_helper'
require 'rswag/specs/swagger_formatter'
# This spec file validates OpenAPI output generated by spec metadata.
# Specifically here, we look at OpenApi 3 as documented at
# https://swagger.io/docs/specification/about/
RSpec.describe 'Generated OpenApi', type: :request, swagger_doc: 'v3/openapi.json' do
before do |example|
output = double('output').as_null_object
swagger_root = File.expand_path('tmp/swagger', __dir__)
config = double('config', swagger_root: swagger_root, get_swagger_doc: swagger_doc )
formatter = Rswag::Specs::SwaggerFormatter.new(output, config)
example_group = OpenStruct.new(group: OpenStruct.new(metadata: example.metadata))
formatter.example_group_finished(example_group)
end
# Framework definition, to be overridden for contexts
let(:swagger_doc) do
{ # That which would be defined in swagger_helper.rb
openapi: api_openapi,
info: {},
servers: api_servers,
produces: api_produces,
components: api_components
}
end
let(:api_openapi) { '3.0.3' }
let(:api_servers) {[{ url: "https://api.example.com/foo" }]}
let(:api_produces) { ['application/json'] }
let(:api_components) { {} }
describe 'Basic Structure'
describe 'API Server and Base Path' do
path '/stubs' do
get 'a summary' do
tags 'Server and Path'
response '200', 'OK' do
run_test!
it 'lists server' do
tree = swagger_doc.dig(:servers)
expect(tree).to eq([
{ url: "https://api.example.com/foo" }
])
end
context "multiple" do
let(:api_servers) {[
{ url: "https://api.example.com/foo" },
{ url: "http://api.example.com/foo" },
]}
it 'lists servers' do
tree = swagger_doc.dig(:servers)
expect(tree).to eq([
{ url: "https://api.example.com/foo" },
{ url: "http://api.example.com/foo" }
])
end
end
context "with variables" do
let(:api_servers) {[{
url: "https://{defaultHost}/foo",
variables: {
defaultHost: {
default: "api.example.com"
}
}
}]}
it 'lists server and variables' do
tree = swagger_doc.dig(:servers)
expect(tree).to eq([{
url: "https://{defaultHost}/foo",
variables: {
defaultHost: {
default: "api.example.com"
}
}
}])
end
end
# TODO: Enum variables, defaults, override at path/operation
end
end
end
end
describe 'Media Types' do
path '/stubs' do
get 'a summary' do
tags 'Media Types'
response '200', 'OK' do
run_test!
it 'declares output as application/json' do
pending "Not yet implemented?"
tree = swagger_doc.dig(:paths, "/stubs", :get, :responses, '200', :content)
expect(tree).to have_key('application/json')
end
end
end
end
end
describe 'Paths and Operations'
describe 'Parameter Serialization' do
describe 'Path Parameters' do
path '/stubs/{a_param}' do
get 'a summary' do
tags 'Parameter Serialization: Query String'
produces 'application/json'
parameter(
name: 'a_param',
in: :path,
)
let(:a_param) { "42" }
response '200', 'OK' do
run_test!
it 'declares parameter in path' do
tree = swagger_doc.dig(:paths, "/stubs/{a_param}", :get, :parameters)
expect(tree.first[:name]).to eq('a_param')
expect(tree.first[:in]).to eq(:path)
end
it 'declares path parameters as required' do
tree = swagger_doc.dig(:paths, "/stubs/{a_param}", :get, :parameters)
expect(tree.first[:required]).to eq(true)
end
end
end
end
end
describe 'Query Parameters' do
path '/stubs' do
get 'a summary' do
tags 'Parameter Serialization: Query String'
produces 'application/json'
parameter(
name: 'a_param',
in: :query,
)
let(:a_param) { "a foo" }
response '200', 'OK' do
run_test!
it 'declares parameter in query string' do
tree = swagger_doc.dig(:paths, "/stubs", :get, :parameters)
expect(tree.first[:name]).to eq('a_param')
expect(tree.first[:in]).to eq(:query)
end
end
# TODO: Serialization (form/spaceDelimited/pipeDelimited/deepObject)
end
end
end
# TODO: Header
# TODO: Cookie
# TODO: Default values
# TODO: Enum
# TODO: Constant
# TODO: Empty/Nullable
# TODO: Examples
# TODO: Deprecated
# TODO: Common Parameters
end
describe 'Request Body' do
path '/stubs' do
post 'body is required' do
tags 'Media Types'
consumes 'multipart/form-data'
parameter name: :file, :in => :formData, :type => :file, required: true
let(:file) { Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures/thumbnail.png")) }
response '200', 'OK' do
run_test!
it 'declares requestBody is required' do
pending "This output is massaged in SwaggerFormatter#stop, and isn't quite ready here to assert"
tree = swagger_doc.dig(:paths, "/stubs", :post, :requestBody)
expect(tree[:required]).to eq(true)
end
end
end
end
end
describe 'Responses'
describe 'Data Models (Schemas)'
describe 'Examples'
describe 'Authentication'
describe 'Links'
describe 'Callbacks'
describe 'Components Section'
describe 'Using $ref'
describe 'Grouping Operations with Tags'
# path '/blogs' do
# post 'Creates a blog' do
# tags 'Blogs'
# description 'Creates a new blog from provided data'
# operationId 'createBlog'
# consumes 'application/json'
# produces 'application/json'
# parameter name: :blog, in: :body, schema: { '$ref' => '#/definitions/blog' }
# let(:blog) { { title: 'foo', content: 'bar' } }
# response '201', 'blog created' do
# # schema '$ref' => '#/definitions/blog'
# run_test!
# end
# response '422', 'invalid request' do
# schema '$ref' => '#/definitions/errors_object'
# let(:blog) { { title: 'foo' } }
# run_test! do |response|
# expect(response.body).to include("can't be blank")
# end
# it 'outputs parameters' do
# pp swagger_doc
# params = swagger_doc.dig(:paths, "/blogs", :post, :parameters)
# expect(params[0][:name]).to eq(:blog)
# end
# end
# end
# get 'Searches blogs' do
# tags 'Blogs'
# description 'Searches blogs by keywords'
# operationId 'searchBlogs'
# produces 'application/json'
# parameter name: :keywords, in: :query, type: 'string'
# let(:keywords) { 'foo bar' }
# response '200', 'success' do
# schema type: 'array', items: { '$ref' => '#/definitions/blog' }
# end
# response '406', 'unsupported accept header' do
# let(:'Accept') { 'application/foo' }
# run_test!
# end
# end
# end
# path '/blogs/flexible' do
# post 'Creates a blog flexible body' do
# tags 'Blogs'
# description 'Creates a flexible blog from provided data'
# operationId 'createFlexibleBlog'
# consumes 'application/json'
# produces 'application/json'
# parameter name: :flexible_blog, in: :body, schema: {
# oneOf: [
# { '$ref' => '#/definitions/blog' },
# { '$ref' => '#/definitions/flexible_blog' }
# ]
# }
# let(:flexible_blog) { { blog: { headline: 'my headline', text: 'my text' } } }
# response '201', 'flexible blog created' do
# schema oneOf: [{ '$ref' => '#/definitions/blog' }, { '$ref' => '#/definitions/flexible_blog' }]
# run_test!
# end
# end
# end
# path '/blogs/{id}' do
# parameter name: :id, in: :path, type: :string
# let(:id) { blog.id }
# let(:blog) { Blog.create(title: 'foo', content: 'bar', thumbnail: 'thumbnail.png') }
# get 'Retrieves a blog' do
# tags 'Blogs'
# description 'Retrieves a specific blog by id'
# operationId 'getBlog'
# produces 'application/json'
# response '200', 'blog found' do
# header 'ETag', type: :string
# header 'Last-Modified', type: :string
# header 'Cache-Control', type: :string
# schema '$ref' => '#/definitions/blog'
# examples 'application/json' => {
# id: 1,
# title: 'Hello world!',
# content: 'Hello world and hello universe. Thank you all very much!!!',
# thumbnail: 'thumbnail.png'
# }
# let(:id) { blog.id }
# run_test!
# end
# response '404', 'blog not found' do
# let(:id) { 'invalid' }
# run_test!
# end
# end
# end
# path '/blogs/{id}/upload' do
# parameter name: :id, in: :path, type: :string
# let(:id) { blog.id }
# let(:blog) { Blog.create(title: 'foo', content: 'bar') }
# put 'Uploads a blog thumbnail' do
# tags 'Blogs'
# description 'Upload a thumbnail for specific blog by id'
# operationId 'uploadThumbnailBlog'
# consumes 'multipart/form-data'
# parameter name: :file, :in => :formData, :type => :file, required: true
# response '200', 'blog updated' do
# let(:file) { Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures/thumbnail.png")) }
# run_test!
# end
# end
# end
end

View File

@@ -67,14 +67,83 @@ RSpec.configure do |config|
required: ['id', 'headline'] required: ['id', 'headline']
} }
}, },
securityDefinitions: { components: {
basic_auth: { securitySchemes: {
type: :basic basic_auth: {
type: :http,
scheme: :basic
},
api_key: {
type: :apiKey,
name: 'api_key',
in: :query
}
}
}
},
'v3/openapi.json' => {
openapi: '3.0.0',
info: {
title: 'API V1',
version: 'v1'
},
paths: {},
servers: [
{
url: 'https://{defaultHost}',
variables: {
defaultHost: {
default: 'www.example.com'
}
}
}
],
definitions: {
errors_object: {
type: 'object',
properties: {
errors: { '$ref' => '#/definitions/errors_map' }
}
}, },
api_key: { errors_map: {
type: :apiKey, type: 'object',
name: 'api_key', additionalProperties: {
in: :query type: 'array',
items: { type: 'string' }
}
},
blog: {
type: 'object',
properties: {
id: { type: 'integer' },
title: { type: 'string' },
content: { type: 'string', 'x-nullable': true },
thumbnail: { type: 'string', 'x-nullable': true}
},
required: [ 'id', 'title' ]
},
flexible_blog: {
type: 'object',
properties: {
id: { type: 'integer' },
headline: { type: 'string' },
text: { type: 'string', nullable: true },
thumbnail: { type: 'string', nullable: true }
},
required: ['id', 'headline']
}
},
components: {
securitySchemes: {
basic_auth: {
type: :http,
scheme: :basic
},
api_key: {
type: :apiKey,
name: 'api_key',
in: :query
}
} }
} }
} }

View File

@@ -21,14 +21,10 @@
], ],
"responses": { "responses": {
"204": { "204": {
"description": "Valid credentials", "description": "Valid credentials"
"content": {
}
}, },
"401": { "401": {
"description": "Invalid credentials", "description": "Invalid credentials"
"content": {
}
} }
} }
} }
@@ -49,14 +45,10 @@
], ],
"responses": { "responses": {
"204": { "204": {
"description": "Valid credentials", "description": "Valid credentials"
"content": {
}
}, },
"401": { "401": {
"description": "Invalid credentials", "description": "Invalid credentials"
"content": {
}
} }
} }
} }
@@ -80,14 +72,10 @@
], ],
"responses": { "responses": {
"204": { "204": {
"description": "Valid credentials", "description": "Valid credentials"
"content": {
}
}, },
"401": { "401": {
"description": "Invalid credentials", "description": "Invalid credentials"
"content": {
}
} }
} }
} }
@@ -105,9 +93,7 @@
], ],
"responses": { "responses": {
"201": { "201": {
"description": "blog created", "description": "blog created"
"content": {
}
}, },
"422": { "422": {
"description": "invalid request", "description": "invalid request",
@@ -148,9 +134,7 @@
], ],
"responses": { "responses": {
"406": { "406": {
"description": "unsupported accept header", "description": "unsupported accept header"
"content": {
}
} }
} }
} }
@@ -235,16 +219,14 @@
"type": "string" "type": "string"
} }
}, },
"examples": {
"application/json": {
"id": 1,
"title": "Hello world!",
"content": "Hello world and hello universe. Thank you all very much!!!",
"thumbnail": "thumbnail.png"
}
},
"content": { "content": {
"application/json": { "application/json": {
"example": {
"id": 1,
"title": "Hello world!",
"content": "Hello world and hello universe. Thank you all very much!!!",
"thumbnail": "thumbnail.png"
},
"schema": { "schema": {
"$ref": "#/definitions/blog" "$ref": "#/definitions/blog"
} }
@@ -252,9 +234,7 @@
} }
}, },
"404": { "404": {
"description": "blog not found", "description": "blog not found"
"content": {
}
} }
} }
} }
@@ -282,9 +262,7 @@
], ],
"responses": { "responses": {
"200": { "200": {
"description": "blog updated", "description": "blog updated"
"content": {
}
} }
}, },
"requestBody": { "requestBody": {
@@ -294,7 +272,8 @@
"type": "file" "type": "file"
} }
} }
} },
"required": true
} }
} }
} }
@@ -377,7 +356,8 @@
"components": { "components": {
"securitySchemes": { "securitySchemes": {
"basic_auth": { "basic_auth": {
"type": "basic" "type": "http",
"scheme": "basic"
}, },
"api_key": { "api_key": {
"type": "apiKey", "type": "apiKey",

View File

@@ -0,0 +1,160 @@
{
"openapi": "3.0.0",
"info": {
"title": "API V1",
"version": "v1"
},
"paths": {
"/stubs": {
"get": {
"summary": "a summary",
"tags": [
"Parameter Serialization: Query String"
],
"responses": {
"200": {
"description": "OK"
}
},
"parameters": [
{
"name": "a_param",
"in": "query"
}
]
},
"post": {
"summary": "body is required",
"tags": [
"Media Types"
],
"parameters": [
],
"responses": {
"200": {
"description": "OK"
}
},
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"type": "file"
}
}
},
"required": true
}
}
},
"/stubs/{a_param}": {
"get": {
"summary": "a summary",
"tags": [
"Parameter Serialization: Query String"
],
"parameters": [
{
"name": "a_param",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
}
},
"servers": [
{
"url": "https://{defaultHost}",
"variables": {
"defaultHost": {
"default": "www.example.com"
}
}
}
],
"definitions": {
"errors_object": {
"type": "object",
"properties": {
"errors": {
"$ref": "#/definitions/errors_map"
}
}
},
"errors_map": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"blog": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "string"
},
"content": {
"type": "string",
"x-nullable": true
},
"thumbnail": {
"type": "string",
"x-nullable": true
}
},
"required": [
"id",
"title"
]
},
"flexible_blog": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"headline": {
"type": "string"
},
"text": {
"type": "string",
"nullable": true
},
"thumbnail": {
"type": "string",
"nullable": true
}
},
"required": [
"id",
"headline"
]
}
},
"components": {
"securitySchemes": {
"basic_auth": {
"type": "http",
"scheme": "basic"
},
"api_key": {
"type": "apiKey",
"name": "api_key",
"in": "query"
}
}
}
}