mirror of
https://github.com/ditkrg/rswag.git
synced 2026-01-22 22:06:43 +00:00
Merge branch 'openapi/master' into openapi/merge
This commit is contained in:
commit
b5e210cd96
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,3 +6,5 @@
|
|||||||
**/*/node_modules
|
**/*/node_modules
|
||||||
*.swp
|
*.swp
|
||||||
Gemfile.lock
|
Gemfile.lock
|
||||||
|
/.idea/
|
||||||
|
**/test-app/.byebug_history
|
||||||
|
|||||||
@ -24,6 +24,11 @@ cd -
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Test
|
## Test
|
||||||
|
Initialize the rswag-ui repo with assets.
|
||||||
|
```
|
||||||
|
ci/build.sh
|
||||||
|
```
|
||||||
|
|
||||||
Make sure the tests pass:
|
Make sure the tests pass:
|
||||||
```
|
```
|
||||||
./ci/test.sh
|
./ci/test.sh
|
||||||
|
|||||||
21
Gemfile
21
Gemfile
@ -1,10 +1,12 @@
|
|||||||
source "https://rubygems.org"
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
# Allow the rails version to come from an ENV setting so Travis can test multiple versions.
|
# Allow the rails version to come from an ENV setting so Travis can test multiple versions.
|
||||||
# See http://www.schneems.com/post/50991826838/testing-against-multiple-rails-versions/
|
# See http://www.schneems.com/post/50991826838/testing-against-multiple-rails-versions/
|
||||||
rails_version = ENV['RAILS_VERSION'] || '5.1.2'
|
rails_version = ENV['RAILS_VERSION'] || '5.2.4.2'
|
||||||
|
|
||||||
gem 'rails', "#{rails_version}"
|
gem 'rails', rails_version.to_s
|
||||||
|
|
||||||
case rails_version.split('.').first
|
case rails_version.split('.').first
|
||||||
when '3'
|
when '3'
|
||||||
@ -24,17 +26,22 @@ gem 'rswag-api', path: './rswag-api'
|
|||||||
gem 'rswag-ui', path: './rswag-ui'
|
gem 'rswag-ui', path: './rswag-ui'
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'test-unit'
|
|
||||||
gem 'rspec-rails'
|
|
||||||
gem 'generator_spec'
|
|
||||||
gem 'capybara'
|
gem 'capybara'
|
||||||
gem 'capybara-webkit'
|
gem 'capybara-webkit'
|
||||||
|
gem 'generator_spec'
|
||||||
|
gem 'rspec-rails'
|
||||||
gem 'rswag-specs', path: './rswag-specs'
|
gem 'rswag-specs', path: './rswag-specs'
|
||||||
|
gem 'test-unit'
|
||||||
|
end
|
||||||
|
|
||||||
|
group :development do
|
||||||
|
gem 'rswag-specs', path: './rswag-specs'
|
||||||
|
gem 'rubocop'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :assets do
|
group :assets do
|
||||||
gem 'uglifier'
|
|
||||||
gem 'therubyracer'
|
gem 'therubyracer'
|
||||||
|
gem 'uglifier'
|
||||||
end
|
end
|
||||||
|
|
||||||
gem 'byebug'
|
gem 'byebug'
|
||||||
|
|||||||
358
README.md
358
README.md
@ -3,10 +3,13 @@ rswag
|
|||||||
[](https://travis-ci.org/rswag/rswag)
|
[](https://travis-ci.org/rswag/rswag)
|
||||||
[](https://codeclimate.com/github/rswag/rswag/maintainability)
|
[](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.
|
OpenApi 3.0 and Swagger 2.0 compatible!
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
Api Rswag creates [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.
|
||||||
|
|
||||||
|
|
||||||
And that's not all ...
|
And that's not all ...
|
||||||
|
|
||||||
Once you have an API that can describe itself in Swagger, you've opened the treasure chest of Swagger-based tools including a client generator that can be targeted to a wide range of popular platforms. See [swagger-codegen](https://github.com/swagger-api/swagger-codegen) for more details.
|
Once you have an API that can describe itself in Swagger, you've opened the treasure chest of Swagger-based tools including a client generator that can be targeted to a wide range of popular platforms. See [swagger-codegen](https://github.com/swagger-api/swagger-codegen) for more details.
|
||||||
@ -15,7 +18,7 @@ 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)|2.0|3.18.2|
|
|[master](https://github.com/rswag/rswag/tree/master)|3.0|3.18.2|
|
||||||
|[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|
|
||||||
|
|
||||||
@ -67,8 +70,8 @@ Once you have an API that can describe itself in Swagger, you've opened the trea
|
|||||||
|
|
||||||
post 'Creates a blog' do
|
post 'Creates a blog' do
|
||||||
tags 'Blogs'
|
tags 'Blogs'
|
||||||
consumes 'application/json', 'application/xml'
|
consumes 'application/json'
|
||||||
parameter name: :blog, in: :body, schema: {
|
request_body_json schema: {
|
||||||
type: :object,
|
type: :object,
|
||||||
properties: {
|
properties: {
|
||||||
title: { type: :string },
|
title: { type: :string },
|
||||||
@ -173,7 +176,7 @@ end
|
|||||||
|
|
||||||
### Null Values ###
|
### Null Values ###
|
||||||
|
|
||||||
This library is currently using JSON::Draft4 for validation of response models. It does not support null as a value. So you can add the property 'x-nullable' to a definition to allow null/nil values to pass.
|
This library is currently using JSON::Draft4 for validation of response models. Nullable properties can be supported with the non-standard property 'x-nullable' to a definition to allow null/nil values to pass. Or you can add the new standard ```nullable``` property to a definition.
|
||||||
```ruby
|
```ruby
|
||||||
describe 'Blogs API' do
|
describe 'Blogs API' do
|
||||||
path '/blogs' do
|
path '/blogs' do
|
||||||
@ -184,8 +187,8 @@ describe 'Blogs API' do
|
|||||||
schema type: :object,
|
schema type: :object,
|
||||||
properties: {
|
properties: {
|
||||||
id: { type: :integer },
|
id: { type: :integer },
|
||||||
title: { type: :string },
|
title: { type: :string, nullable: true }, # preferred syntax
|
||||||
content: { type: :string, 'x-nullable': true }
|
content: { type: :string, 'x-nullable': true } # legacy syntax, but still works
|
||||||
}
|
}
|
||||||
....
|
....
|
||||||
end
|
end
|
||||||
@ -193,12 +196,49 @@ describe 'Blogs API' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
*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>
|
### Support for anyOf or AllOf schemas ###
|
||||||
|
|
||||||
|
Open API 3.0 now supports more flexible schema validation with the ```anyOf``` and ```allOf``` directives. rswag will handle these definitions and validate them properly.
|
||||||
|
|
||||||
|
|
||||||
|
Notice the ```schema``` inside the ```response``` section. Placing a ```schema``` method inside the response will validate (and fail the tests)
|
||||||
|
if during the integration test run the endpoint response does not match the response schema. This test validation can handle
|
||||||
|
anyOf and allOf as well. See below:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
request_body_json schema: {
|
||||||
|
:oneOf => [
|
||||||
|
{ '$ref' => '#/components/schemas/blog' },
|
||||||
|
{ '$ref' => '#/components/schemas/flexible_blog' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
examples: :flexible_blog
|
||||||
|
|
||||||
|
let(:flexible_blog) { { blog: { headline: 'my headline', text: 'my text' } } }
|
||||||
|
|
||||||
|
response '201', 'flexible blog created' do
|
||||||
|
schema :oneOf => [{ '$ref' => '#/components/schemas/blog' }, { '$ref' => '#/components/schemas/flexible_blog' }]
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
```
|
||||||
|
This automatic schema validation is a powerful feature of rswag.
|
||||||
|
|
||||||
### Global Metadata ###
|
### Global Metadata ###
|
||||||
|
|
||||||
In addition to paths, operations and responses, Swagger also supports global API metadata. When you install rswag, a file called _swagger_helper.rb_ is added to your spec folder. This is where you define one or more Swagger documents and provide global metadata. Again, the format is based on Swagger so most of the global fields supported by the top level ["Swagger" object](http://swagger.io/specification/#swaggerObject) can be provided with each document definition. As an example, you could define a Swagger document for each version of your API and in each case specify a title, version string and URL basePath:
|
In addition to paths, operations and responses, Swagger also supports global API metadata. When you install rswag, a file called _swagger_helper.rb_ is added to your spec folder. This is where you define one or more Swagger documents and provide global metadata. Again, the format is based on Swagger so most of the global fields supported by the top level ["Swagger" object](http://swagger.io/specification/#swaggerObject) can be provided with each document definition. As an example, you could define a Swagger document for each version of your API and in each case specify a title, version string. In Open API 3.0 the pathing and server definitions have changed a bit [Swagger host/basePath](https://swagger.io/docs/specification/api-host-and-base-path/):
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# spec/swagger_helper.rb
|
# spec/swagger_helper.rb
|
||||||
@ -207,23 +247,41 @@ RSpec.configure do |config|
|
|||||||
|
|
||||||
config.swagger_docs = {
|
config.swagger_docs = {
|
||||||
'v1/swagger.json' => {
|
'v1/swagger.json' => {
|
||||||
swagger: '2.0',
|
openapi: '3.0.1',
|
||||||
info: {
|
info: {
|
||||||
title: 'API V1',
|
title: 'API V1',
|
||||||
version: 'v1',
|
version: 'v1',
|
||||||
description: 'This is the first version of my API'
|
description: 'This is the first version of my API'
|
||||||
},
|
},
|
||||||
basePath: '/api/v1'
|
servers: [
|
||||||
|
{
|
||||||
|
url: 'https://{defaultHost}',
|
||||||
|
variables: {
|
||||||
|
defaultHost: {
|
||||||
|
default: 'www.example.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
'v2/swagger.yaml' => {
|
'v2/swagger.yaml' => {
|
||||||
openapi: '3.0.0',
|
openapi: '3.0.1',
|
||||||
info: {
|
info: {
|
||||||
title: 'API V2',
|
title: 'API V2',
|
||||||
version: 'v2',
|
version: 'v2',
|
||||||
description: 'This is the second version of my API'
|
description: 'This is the second version of my API'
|
||||||
},
|
},
|
||||||
basePath: '/api/v2'
|
servers: [
|
||||||
|
{
|
||||||
|
url: 'https://{defaultHost}',
|
||||||
|
variables: {
|
||||||
|
defaultHost: {
|
||||||
|
default: 'www.example.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -265,7 +323,9 @@ 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. 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, :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
|
||||||
# spec/swagger_helper.rb
|
# spec/swagger_helper.rb
|
||||||
@ -274,15 +334,18 @@ RSpec.configure do |config|
|
|||||||
|
|
||||||
config.swagger_docs = {
|
config.swagger_docs = {
|
||||||
'v1/swagger.json' => {
|
'v1/swagger.json' => {
|
||||||
...
|
... # note the new Open API 3.0 compliant security structure here, under "components"
|
||||||
securityDefinitions: {
|
components: {
|
||||||
basic: {
|
securitySchemes: {
|
||||||
type: :basic
|
basic_auth: {
|
||||||
},
|
type: :http,
|
||||||
apiKey: {
|
scheme: :basic
|
||||||
type: :apiKey,
|
},
|
||||||
name: 'api_key',
|
api_key: {
|
||||||
in: :query
|
type: :apiKey,
|
||||||
|
name: 'api_key',
|
||||||
|
in: :query
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,7 +359,7 @@ describe 'Blogs API' do
|
|||||||
|
|
||||||
post 'Creates a blog' do
|
post 'Creates a blog' do
|
||||||
tags 'Blogs'
|
tags 'Blogs'
|
||||||
security [ basic: [] ]
|
security [ basic_auth: [] ]
|
||||||
...
|
...
|
||||||
|
|
||||||
response '201', 'blog created' do
|
response '201', 'blog created' do
|
||||||
@ -311,9 +374,35 @@ describe 'Blogs API' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# example of documenting an endpoint that handles basic auth and api key based security
|
||||||
|
describe 'Auth examples API' do
|
||||||
|
path '/auth-tests/basic-and-api-key' do
|
||||||
|
post 'Authenticates with basic auth and api key' do
|
||||||
|
tags 'Auth Tests'
|
||||||
|
operationId 'testBasicAndApiKey'
|
||||||
|
security [{ basic_auth: [], api_key: [] }]
|
||||||
|
|
||||||
|
response '204', 'Valid credentials' do
|
||||||
|
let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
|
||||||
|
let(:api_key) { 'foobar' }
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
|
||||||
|
response '401', 'Invalid credentials' do
|
||||||
|
let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
|
||||||
|
let(:api_key) { 'barfoo' }
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
__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
|
__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
|
||||||
|
|
||||||
## Configuration & Customization ##
|
## Configuration & Customization ##
|
||||||
|
|
||||||
@ -322,7 +411,7 @@ The steps described above will get you up and running with minimal setup. Howeve
|
|||||||
|Gem|Description|Added/Updated|
|
|Gem|Description|Added/Updated|
|
||||||
|---------|-----------|-------------|
|
|---------|-----------|-------------|
|
||||||
|__rswag-specs__|Swagger-based DSL for rspec & accompanying rake task for generating Swagger files|_spec/swagger_helper.rb_|
|
|__rswag-specs__|Swagger-based DSL for rspec & accompanying rake task for generating Swagger files|_spec/swagger_helper.rb_|
|
||||||
|__rswag-api__ |Rails Engine that exposes your Swagger files as JSON endpoints|_config/initializers/rswag-api.rb, config/routes.rb_|
|
|__rswag-api__ |Rails Engine that exposes your Swagger files as JSON endpoints|_config/initializers/rswag_api.rb, config/routes.rb_|
|
||||||
|__rswag-ui__ |Rails Engine that includes [swagger-ui](https://github.com/swagger-api/swagger-ui) and powers it from your Swagger endpoints|_config/initializers/rswag-ui.rb, config/routes.rb_|
|
|__rswag-ui__ |Rails Engine that includes [swagger-ui](https://github.com/swagger-api/swagger-ui) and powers it from your Swagger endpoints|_config/initializers/rswag-ui.rb, config/routes.rb_|
|
||||||
|
|
||||||
### Output Location for Generated Swagger Files ###
|
### Output Location for Generated Swagger Files ###
|
||||||
@ -337,7 +426,7 @@ RSpec.configure do |config|
|
|||||||
end
|
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 ###
|
### Input Location for Rspec Tests ###
|
||||||
|
|
||||||
@ -350,28 +439,43 @@ 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.
|
||||||
|
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:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# spec/swagger_helper.rb
|
# spec/swagger_helper.rb
|
||||||
config.swagger_docs = {
|
config.swagger_docs = {
|
||||||
'v1/swagger.json' => {
|
'v1/swagger.json' => {
|
||||||
swagger: '2.0',
|
openapi: '3.0.0',
|
||||||
info: {
|
info: {
|
||||||
title: 'API V1'
|
title: 'API V1'
|
||||||
},
|
},
|
||||||
definitions: {
|
components: {
|
||||||
errors_object: {
|
schemas: {
|
||||||
type: 'object',
|
errors_object: {
|
||||||
properties: {
|
type: 'object',
|
||||||
errors: { '$ref' => '#/definitions/errors_map' }
|
properties: {
|
||||||
}
|
errors: { '$ref' => '#/components/schemas/errors_map' }
|
||||||
},
|
}
|
||||||
errors_map: {
|
},
|
||||||
type: 'object',
|
errors_map: {
|
||||||
additionalProperties: {
|
type: 'object',
|
||||||
type: 'array',
|
additionalProperties: {
|
||||||
items: { type: 'string' }
|
type: 'array',
|
||||||
|
items: { type: 'string' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
blog: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: { type: 'integer' },
|
||||||
|
title: { type: 'string' },
|
||||||
|
content: { type: 'string', nullable: true },
|
||||||
|
thumbnail: { type: 'string', nullable: true }
|
||||||
|
},
|
||||||
|
required: %w[id title]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -386,7 +490,7 @@ describe 'Blogs API' do
|
|||||||
post 'Creates a blog' do
|
post 'Creates a blog' do
|
||||||
|
|
||||||
response 422, 'invalid request' do
|
response 422, 'invalid request' do
|
||||||
schema '$ref' => '#/definitions/errors_object'
|
schema '$ref' => '#/components/schemas/errors_object'
|
||||||
...
|
...
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -398,14 +502,15 @@ describe 'Blogs API' do
|
|||||||
post 'Creates a comment' do
|
post 'Creates a comment' do
|
||||||
|
|
||||||
response 422, 'invalid request' do
|
response 422, 'invalid request' do
|
||||||
schema '$ref' => '#/definitions/errors_object'
|
schema '$ref' => '#/components/schemas/errors_object'
|
||||||
...
|
...
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
### Response headers ###
|
### Response headers ###
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# spec/integration/comments_spec.rb
|
# spec/integration/comments_spec.rb
|
||||||
@ -425,7 +530,7 @@ end
|
|||||||
### Response examples ###
|
### Response examples ###
|
||||||
|
|
||||||
You can provide custom response examples to the generated swagger file by calling the method `examples` inside the response block:
|
You can provide custom response examples to the generated swagger file by calling the method `examples` inside the response block:
|
||||||
|
However, auto generated example responses are now enabled by default in rswag. See below.
|
||||||
```ruby
|
```ruby
|
||||||
# spec/integration/blogs_spec.rb
|
# spec/integration/blogs_spec.rb
|
||||||
describe 'Blogs API' do
|
describe 'Blogs API' do
|
||||||
@ -444,15 +549,14 @@ describe 'Blogs API' do
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
### Enable generation examples from responses ###
|
|
||||||
|
|
||||||
To enable examples generation from responses add callback above run_test! like:
|
### Enable auto generation examples from responses ###
|
||||||
```ruby
|
|
||||||
after do |example|
|
This is now enabled by default in rswag.
|
||||||
example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) }
|
You need to set the ``` config.swagger_dry_run = false``` value in the spec/spec_helper.rb file.
|
||||||
end
|
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.
|
||||||
```
|
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.
|
||||||
You need to disable --dry-run option for Rspec > 3
|
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 application.rb:
|
Add to application.rb:
|
||||||
```ruby
|
```ruby
|
||||||
@ -461,7 +565,7 @@ RSpec.configure do |config|
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running tests without documenting ###
|
#### 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:
|
If you want to use Rswag for testing without adding it to you swagger docs, you can provide the document tag:
|
||||||
```ruby
|
```ruby
|
||||||
@ -489,6 +593,136 @@ describe 'Blogs API', document: false do
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### rswag helper methods #####
|
||||||
|
|
||||||
|
There are some helper methods to help with documenting request bodies.
|
||||||
|
```ruby
|
||||||
|
describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
|
||||||
|
let(:api_key) { 'fake_key' }
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
request_body_json schema: { '$ref' => '#/components/schemas/blog' },
|
||||||
|
examples: :blog
|
||||||
|
|
||||||
|
request_body_text_plain
|
||||||
|
request_body_xml schema: { '$ref' => '#/components/schemas/blog' }
|
||||||
|
|
||||||
|
let(:blog) { { blog: { title: 'foo', content: 'bar' } } }
|
||||||
|
|
||||||
|
response '201', 'blog created' do
|
||||||
|
schema '$ref' => '#/components/schemas/blog'
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
|
||||||
|
response '422', 'invalid request' do
|
||||||
|
schema '$ref' => '#/components/schemas/errors_object'
|
||||||
|
let(:blog) { { blog: { title: 'foo' } } }
|
||||||
|
|
||||||
|
run_test! do |response|
|
||||||
|
expect(response.body).to include("can't be blank")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above example, we see methods ```request_body_json``` ```request_body_plain``` ```request_body_xml```.
|
||||||
|
These methods can be used to describe json, plain text and xml body. They are just wrapper methods to setup posting JSON, plain text or xml into your endpoint.
|
||||||
|
The simplest most common usage is for json formatted body to use the schema: to specify the location of the schema for the request body
|
||||||
|
and the examples: :blog which will create a named example "blog" under the "requestBody / content / application/json / examples" section.
|
||||||
|
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
|
||||||
|
...
|
||||||
|
{"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"examples": {
|
||||||
|
"blog": { // takes the name from examples: :blog above
|
||||||
|
"value": { //this is open api 3.0 structure -> https://swagger.io/docs/specification/adding-examples/
|
||||||
|
"blog": { // here is the actual JSON payload that is submitted to the service, and shows up in swagger UI as an example
|
||||||
|
"title": "foo",
|
||||||
|
"content": "bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test/plain": {
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/xml": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/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.
|
||||||
|
This ```let``` value is used in the integration test to run the test AND captured and injected into the requestBody section.
|
||||||
|
|
||||||
|
##### rswag response examples #####
|
||||||
|
|
||||||
|
In the same way that requestBody examples can be captured and injected into the swagger output, response examples can also be captured.
|
||||||
|
Using the above example, when the integration test is run - the swagger would include the following snippet providing more useful real world examples
|
||||||
|
capturing the response from the execution of the integration test. Again 3.0 swagger changed the structure of how these are documented.
|
||||||
|
|
||||||
|
```json
|
||||||
|
... "responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "blog created",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"example": {
|
||||||
|
"id": 1,
|
||||||
|
"title": "foo",
|
||||||
|
"content": "bar",
|
||||||
|
"thumbnail": null
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "invalid request",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"example": {
|
||||||
|
"errors": {
|
||||||
|
"content": [
|
||||||
|
"can't be blank"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/errors_object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### 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_:
|
||||||
@ -509,7 +743,7 @@ GET http://<hostname>/your-custom-prefix/v1/swagger.json
|
|||||||
|
|
||||||
### Root Location for Swagger Files ###
|
### Root Location for Swagger Files ###
|
||||||
|
|
||||||
You can adjust this in the _rswag-api.rb_ initializer that's installed with __rspec-api__:
|
You can adjust this in the _rswag_api.rb_ initializer that's installed with __rspec-api__:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Rswag::Api.configure do |c|
|
Rswag::Api.configure do |c|
|
||||||
@ -578,3 +812,15 @@ bundle exec rake rswag:ui:copy_assets[public/api-docs]
|
|||||||
```
|
```
|
||||||
|
|
||||||
__NOTE:__: The provided subfolder MUST correspond to the UI mount prefix - "api-docs" by default.
|
__NOTE:__: The provided subfolder MUST correspond to the UI mount prefix - "api-docs" by default.
|
||||||
|
|
||||||
|
|
||||||
|
Notes to test swagger output locally with swagger editor
|
||||||
|
```
|
||||||
|
docker pull 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
|
||||||
|
at ```http://localhost```. From here, you can use the UI to load the generated swagger.json to validate the output.
|
||||||
|
|
||||||
|
|||||||
@ -5,4 +5,4 @@ Example:
|
|||||||
rails generate rswag:api:install
|
rails generate rswag:api:install
|
||||||
|
|
||||||
This will create:
|
This will create:
|
||||||
config/initializers/rswag-api.rb
|
config/initializers/rswag_api.rb
|
||||||
|
|||||||
@ -7,7 +7,7 @@ module Rswag
|
|||||||
source_root File.expand_path('../templates', __FILE__)
|
source_root File.expand_path('../templates', __FILE__)
|
||||||
|
|
||||||
def add_initializer
|
def add_initializer
|
||||||
template('rswag-api.rb', 'config/initializers/rswag-api.rb')
|
template('rswag_api.rb', 'config/initializers/rswag_api.rb')
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_routes
|
def add_routes
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
Rswag::Api.configure do |c|
|
OpenApi::Rswag::Api.configure do |c|
|
||||||
|
|
||||||
# Specify a root folder where Swagger JSON files are located
|
# Specify a root folder where Swagger JSON files are located
|
||||||
# This is used by the Swagger middleware to serve requests for API descriptions
|
# This is used by the Swagger middleware to serve requests for API descriptions
|
||||||
@ -1,17 +1,19 @@
|
|||||||
$:.push File.expand_path("../lib", __FILE__)
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
||||||
|
|
||||||
# Describe your gem and declare its dependencies:
|
# Describe your gem and declare its dependencies:
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = "rswag-api"
|
s.name = 'rswag-api'
|
||||||
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
|
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
|
||||||
s.authors = ["Richie Morris"]
|
s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian']
|
||||||
s.email = ["domaindrivendev@gmail.com"]
|
s.email = ['domaindrivendev@gmail.com']
|
||||||
s.homepage = "https://github.com/domaindrivendev/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 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 Swagger ecosystem by exposing Swagger files, that describe your service, as JSON endpoints'
|
||||||
s.license = "MIT"
|
s.license = 'MIT'
|
||||||
|
|
||||||
s.files = Dir["{lib}/**/*"] + ["MIT-LICENSE", "Rakefile"]
|
s.files = Dir['{lib}/**/*'] + ['MIT-LICENSE', 'Rakefile']
|
||||||
|
|
||||||
s.add_dependency 'railties', '>= 3.1', '< 7.0'
|
s.add_dependency 'railties', '>= 3.1', '< 7.0'
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
require 'generator_spec'
|
require 'generator_spec'
|
||||||
require 'generators/rswag/api/install/install_generator'
|
require 'generators/rswag/api/install/install_generator'
|
||||||
|
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
module Api
|
module Api
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'installs the Rails initializer' do
|
it 'installs the Rails initializer' do
|
||||||
assert_file('config/initializers/rswag-api.rb')
|
assert_file('config/initializers/rswag_api.rb')
|
||||||
end
|
end
|
||||||
|
|
||||||
# Don't know how to test this
|
# Don't know how to test this
|
||||||
@ -25,3 +26,4 @@ module Rswag
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"swagger": "2.0",
|
"openapi": "3.0.1",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "API V1",
|
"title": "API V1",
|
||||||
"version": "v1"
|
"version": "v1"
|
||||||
|
|||||||
@ -19,7 +19,17 @@ RSpec.configure do |config|
|
|||||||
title: 'API V1',
|
title: 'API V1',
|
||||||
version: 'v1'
|
version: 'v1'
|
||||||
},
|
},
|
||||||
paths: {}
|
paths: {},
|
||||||
|
servers: [
|
||||||
|
{
|
||||||
|
url: 'https://{defaultHost}',
|
||||||
|
variables: {
|
||||||
|
defaultHost: {
|
||||||
|
default: 'www.example.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
|
|
||||||
|
|||||||
@ -35,11 +35,107 @@ module Rswag
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
## OA3
|
||||||
|
# # MUST HAVES
|
||||||
|
# # need to run ```npm install``` in rswag-ui dir to get assets to load
|
||||||
|
# # not sure if its an asset issue or what but this should load => http://localhost:3000/api-docs/index.html
|
||||||
|
# # TODO: fix examples in the main README
|
||||||
|
|
||||||
|
# def request_body(attributes)
|
||||||
|
# # can make this generic, and accept any incoming hash (like parameter method)
|
||||||
|
# attributes.compact!
|
||||||
|
|
||||||
|
# if metadata[:operation][:requestBody].blank?
|
||||||
|
# metadata[:operation][:requestBody] = attributes
|
||||||
|
# elsif metadata[:operation][:requestBody] && metadata[:operation][:requestBody][:content]
|
||||||
|
# # merge in
|
||||||
|
# content_hash = metadata[:operation][:requestBody][:content]
|
||||||
|
# incoming_content_hash = attributes[:content]
|
||||||
|
# content_hash.merge!(incoming_content_hash) if incoming_content_hash
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# def request_body_json(schema:, required: true, description: nil, examples: nil)
|
||||||
|
# passed_examples = Array(examples)
|
||||||
|
# content_hash = { 'application/json' => { schema: schema, examples: examples }.compact! || {} }
|
||||||
|
# request_body(description: description, required: required, content: content_hash)
|
||||||
|
# if passed_examples.any?
|
||||||
|
# # the request_factory is going to have to resolve the different ways that the example can be given
|
||||||
|
# # it can contain a 'value' key which is a direct hash (easiest)
|
||||||
|
# # it can contain a 'external_value' key which makes an external call to load the json
|
||||||
|
# # it can contain a '$ref' key. Which points to #/components/examples/blog
|
||||||
|
# passed_examples.each do |passed_example|
|
||||||
|
# if passed_example.is_a?(Symbol)
|
||||||
|
# example_key_name = passed_example
|
||||||
|
# # TODO: write more tests around this adding to the parameter
|
||||||
|
# # if symbol try and use save_request_example
|
||||||
|
# param_attributes = { name: example_key_name, in: :body, required: required, param_value: example_key_name, schema: schema }
|
||||||
|
# parameter(param_attributes)
|
||||||
|
# elsif passed_example.is_a?(Hash) && passed_example[:externalValue]
|
||||||
|
# param_attributes = { name: passed_example, in: :body, required: required, param_value: passed_example[:externalValue], schema: schema }
|
||||||
|
# parameter(param_attributes)
|
||||||
|
# elsif passed_example.is_a?(Hash) && passed_example['$ref']
|
||||||
|
# param_attributes = { name: passed_example, in: :body, required: required, param_value: passed_example['$ref'], schema: schema }
|
||||||
|
# parameter(param_attributes)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# def request_body_text_plain(required: false, description: nil, examples: nil)
|
||||||
|
# content_hash = { 'test/plain' => { schema: {type: :string}, examples: examples }.compact! || {} }
|
||||||
|
# request_body(description: description, required: required, content: content_hash)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# # TODO: add examples to this like we can for json, might be large lift as many assumptions are made on content-type
|
||||||
|
# def request_body_xml(schema:,required: false, description: nil, examples: nil)
|
||||||
|
# passed_examples = Array(examples)
|
||||||
|
# content_hash = { 'application/xml' => { schema: schema, examples: examples }.compact! || {} }
|
||||||
|
# request_body(description: description, required: required, content: content_hash)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# def request_body_multipart(schema:, description: nil)
|
||||||
|
# content_hash = { 'multipart/form-data' => { schema: schema }}
|
||||||
|
# request_body(description: description, content: content_hash)
|
||||||
|
|
||||||
|
# schema.extend(Hashie::Extensions::DeepLocate)
|
||||||
|
# file_properties = schema.deep_locate ->(_k, v, _obj) { v == :binary }
|
||||||
|
# hash_locator = []
|
||||||
|
|
||||||
|
# file_properties.each do |match|
|
||||||
|
# hash_match = schema.deep_locate ->(_k, v, _obj) { v == match }
|
||||||
|
# hash_locator.concat(hash_match) unless hash_match.empty?
|
||||||
|
# end
|
||||||
|
|
||||||
|
# property_hashes = hash_locator.flat_map do |locator|
|
||||||
|
# locator.select { |_k,v| file_properties.include?(v) }
|
||||||
|
# end
|
||||||
|
|
||||||
|
# existing_keys = []
|
||||||
|
# property_hashes.each do |property_hash|
|
||||||
|
# property_hash.keys.each do |k|
|
||||||
|
# if existing_keys.include?(k)
|
||||||
|
# next
|
||||||
|
# else
|
||||||
|
# file_name = k
|
||||||
|
# existing_keys << k
|
||||||
|
# parameter name: file_name, in: :formData, type: :file, required: true
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
|
||||||
def parameter(attributes)
|
def parameter(attributes)
|
||||||
if attributes[:in] && attributes[:in].to_sym == :path
|
if attributes[:in] && attributes[:in].to_sym == :path
|
||||||
attributes[:required] = true
|
attributes[:required] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
## IF OA3
|
||||||
|
# if attributes[:type] && attributes[:schema].nil?
|
||||||
|
# attributes[:schema] = {type: attributes[:type]}
|
||||||
|
# end
|
||||||
|
|
||||||
if metadata.has_key?(:operation)
|
if metadata.has_key?(:operation)
|
||||||
metadata[:operation][:parameters] ||= []
|
metadata[:operation][:parameters] ||= []
|
||||||
metadata[:operation][:parameters] << attributes
|
metadata[:operation][:parameters] << attributes
|
||||||
@ -57,9 +153,21 @@ module Rswag
|
|||||||
def schema(value)
|
def schema(value)
|
||||||
metadata[:response][:schema] = value
|
metadata[:response][:schema] = value
|
||||||
end
|
end
|
||||||
|
## OA3
|
||||||
|
# def schema(value, content_type: 'application/json')
|
||||||
|
# content_hash = {content_type => {schema: value}}
|
||||||
|
# metadata[:response][:content] = content_hash
|
||||||
|
# end
|
||||||
|
|
||||||
def header(name, attributes)
|
def header(name, attributes)
|
||||||
metadata[:response][:headers] ||= {}
|
metadata[:response][:headers] ||= {}
|
||||||
|
|
||||||
|
## OA3
|
||||||
|
# if attributes[:type] && attributes[:schema].nil?
|
||||||
|
# attributes[:schema] = {type: attributes[:type]}
|
||||||
|
# attributes.delete(:type)
|
||||||
|
# end
|
||||||
|
|
||||||
metadata[:response][:headers][name] = attributes
|
metadata[:response][:headers][name] = attributes
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -71,6 +179,46 @@ module Rswag
|
|||||||
metadata[:response][:examples] = example
|
metadata[:response][:examples] = example
|
||||||
end
|
end
|
||||||
|
|
||||||
|
## OA3
|
||||||
|
# # checks the examples in the parameters should be able to add $ref and externalValue examples.
|
||||||
|
# # This syntax would look something like this in the integration _spec.rb file
|
||||||
|
# #
|
||||||
|
# # request_body_json schema: { '$ref' => '#/components/schemas/blog' },
|
||||||
|
# # examples: [:blog, {name: :external_blog,
|
||||||
|
# # externalValue: 'http://api.sample.org/myjson_example'},
|
||||||
|
# # {name: :another_example,
|
||||||
|
# # '$ref' => '#/components/examples/flexible_blog_example'}]
|
||||||
|
# # The first value :blog, points to a let param of the same name, and is used to make the request in the
|
||||||
|
# # integration test (it is used to build the request payload)
|
||||||
|
# #
|
||||||
|
# # The second item in the array shows how to add an externalValue for the examples in the requestBody section
|
||||||
|
# # The third item shows how to add a $ref item that points to the components/examples section of the swagger spec.
|
||||||
|
# #
|
||||||
|
# # NOTE: that the externalValue will produce valid example syntax in the swagger output, but swagger-ui
|
||||||
|
# # will not show it yet
|
||||||
|
# def merge_other_examples!(example_metadata)
|
||||||
|
# # example.metadata[:operation][:requestBody][:content]['application/json'][:examples]
|
||||||
|
# content_node = example_metadata[:operation][:requestBody][:content]['application/json']
|
||||||
|
# return unless content_node
|
||||||
|
|
||||||
|
# external_example = example_metadata[:operation]&.dig(:parameters)&.detect { |p| p[:in] == :body && p[:name].is_a?(Hash) && p[:name][:externalValue] } || {}
|
||||||
|
# ref_example = example_metadata[:operation]&.dig(:parameters)&.detect { |p| p[:in] == :body && p[:name].is_a?(Hash) && p[:name]['$ref'] } || {}
|
||||||
|
# examples_node = content_node[:examples] ||= {}
|
||||||
|
|
||||||
|
# nodes_to_add = []
|
||||||
|
# nodes_to_add << external_example unless external_example.empty?
|
||||||
|
# nodes_to_add << ref_example unless ref_example.empty?
|
||||||
|
|
||||||
|
# nodes_to_add.each do |node|
|
||||||
|
# json_request_examples = examples_node ||= {}
|
||||||
|
# other_name = node[:name][:name]
|
||||||
|
# other_key = node[:name][:externalValue] ? :externalValue : '$ref'
|
||||||
|
# if other_name
|
||||||
|
# json_request_examples.merge!(other_name => {other_key => node[:param_value]})
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
def run_test!(&block)
|
def run_test!(&block)
|
||||||
# NOTE: rspec 2.x support
|
# NOTE: rspec 2.x support
|
||||||
if RSPEC_VERSION < 3
|
if RSPEC_VERSION < 3
|
||||||
@ -91,6 +239,30 @@ module Rswag
|
|||||||
assert_response_matches_metadata(example.metadata, &block)
|
assert_response_matches_metadata(example.metadata, &block)
|
||||||
example.instance_exec(response, &block) if block_given?
|
example.instance_exec(response, &block) if block_given?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
## OA3
|
||||||
|
# after do |example|
|
||||||
|
# body_parameter = example.metadata[:operation]&.dig(:parameters)&.detect { |p| p[:in] == :body && p[:required] }
|
||||||
|
|
||||||
|
# if body_parameter && respond_to?(body_parameter[:name]) && example.metadata[:operation][:requestBody][:content]['application/json']
|
||||||
|
# # save response examples by default
|
||||||
|
# if example.metadata[:response][:examples].nil? || example.metadata[:response][:examples].empty?
|
||||||
|
# example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) } unless response.body.to_s.empty?
|
||||||
|
# end
|
||||||
|
|
||||||
|
# # save request examples using the let(:param_name) { REQUEST_BODY_HASH } syntax in the test
|
||||||
|
# if response.code.to_s =~ /^2\d{2}$/
|
||||||
|
# example.metadata[:operation][:requestBody][:content]['application/json'] = { examples: {} } unless example.metadata[:operation][:requestBody][:content]['application/json'][:examples]
|
||||||
|
# json_request_examples = example.metadata[:operation][:requestBody][:content]['application/json'][:examples]
|
||||||
|
# json_request_examples[body_parameter[:name]] = { value: send(body_parameter[:name]) }
|
||||||
|
|
||||||
|
# example.metadata[:operation][:requestBody][:content]['application/json'][:examples] = json_request_examples
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# self.class.merge_other_examples!(example.metadata) if example.metadata[:operation][:requestBody]
|
||||||
|
|
||||||
|
# end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rswag/specs/request_factory'
|
require 'rswag/specs/request_factory'
|
||||||
require 'rswag/specs/response_validator'
|
require 'rswag/specs/response_validator'
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
module ExampleHelpers
|
module ExampleHelpers
|
||||||
|
|
||||||
def submit_request(metadata)
|
def submit_request(metadata)
|
||||||
request = RequestFactory.new.build_request(metadata, self)
|
request = RequestFactory.new.build_request(metadata, self)
|
||||||
|
|
||||||
@ -19,10 +20,8 @@ module Rswag
|
|||||||
send(
|
send(
|
||||||
request[:verb],
|
request[:verb],
|
||||||
request[:path],
|
request[:path],
|
||||||
{
|
params: request[:payload],
|
||||||
params: request[:payload],
|
headers: request[:headers]
|
||||||
headers: request[:headers]
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'json-schema'
|
require 'json-schema'
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
@ -15,6 +17,8 @@ module Rswag
|
|||||||
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={})
|
||||||
|
## OA3
|
||||||
|
# return if data.nil? && (current_schema.schema['nullable'] == true || current_schema.schema['x-nullable'] == true)
|
||||||
return if data.nil? && current_schema.schema['x-nullable'] == true
|
return if data.nil? && current_schema.schema['x-nullable'] == true
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
class Railtie < ::Rails::Railtie
|
class Railtie < ::Rails::Railtie
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
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'
|
||||||
@ -39,6 +41,10 @@ module Rswag
|
|||||||
def derive_security_params(metadata, swagger_doc)
|
def derive_security_params(metadata, swagger_doc)
|
||||||
requirements = metadata[:operation][:security] || swagger_doc[:security] || []
|
requirements = metadata[:operation][:security] || swagger_doc[:security] || []
|
||||||
scheme_names = requirements.flat_map { |r| r.keys }
|
scheme_names = requirements.flat_map { |r| r.keys }
|
||||||
|
## OA3
|
||||||
|
# scheme_names = requirements.flat_map(&:keys)
|
||||||
|
# components = swagger_doc[:components] || {}
|
||||||
|
# schemes = (components[:securitySchemes] || {}).slice(*scheme_names).values
|
||||||
schemes = (swagger_doc[:securityDefinitions] || {}).slice(*scheme_names).values
|
schemes = (swagger_doc[:securityDefinitions] || {}).slice(*scheme_names).values
|
||||||
|
|
||||||
schemes.map do |scheme|
|
schemes.map do |scheme|
|
||||||
@ -99,7 +105,7 @@ module Rswag
|
|||||||
# Accept header
|
# Accept header
|
||||||
produces = metadata[:operation][:produces] || swagger_doc[:produces]
|
produces = metadata[:operation][:produces] || swagger_doc[:produces]
|
||||||
if produces
|
if produces
|
||||||
accept = example.respond_to?(:'Accept') ? example.send(:'Accept') : produces.first
|
accept = example.respond_to?(:Accept) ? example.send(:Accept) : produces.first
|
||||||
tuples << [ 'Accept', accept ]
|
tuples << [ 'Accept', accept ]
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -145,13 +151,22 @@ module Rswag
|
|||||||
tuples = parameters
|
tuples = parameters
|
||||||
.select { |p| p[:in] == :formData }
|
.select { |p| p[:in] == :formData }
|
||||||
.map { |p| [ p[:name], example.send(p[:name]) ] }
|
.map { |p| [ p[:name], example.send(p[:name]) ] }
|
||||||
Hash[ tuples ]
|
Hash[tuples]
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json_payload(parameters, example)
|
def build_json_payload(parameters, example)
|
||||||
body_param = parameters.select { |p| p[:in] == :body }.first
|
body_param = parameters.select { |p| p[:in] == :body }.first
|
||||||
body_param ? example.send(body_param[:name]).to_json : nil
|
body_param ? example.send(body_param[:name]).to_json : nil
|
||||||
end
|
end
|
||||||
|
## OA3
|
||||||
|
# def build_json_payload(parameters, example)
|
||||||
|
# body_param = parameters.select { |p| p[:in] == :body && p[:name].is_a?(Symbol) }.first
|
||||||
|
# return nil unless body_param
|
||||||
|
|
||||||
|
# source_body_param = example.send(body_param[:name]) if body_param[:name] && example.respond_to?(body_param[:name])
|
||||||
|
# source_body_param ||= body_param[:param_value]
|
||||||
|
# source_body_param ? source_body_param.to_json : nil
|
||||||
|
# end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'active_support/core_ext/hash/slice'
|
require 'active_support/core_ext/hash/slice'
|
||||||
require 'json-schema'
|
require 'json-schema'
|
||||||
require 'json'
|
require 'json'
|
||||||
@ -6,7 +8,6 @@ require 'rswag/specs/extended_schema'
|
|||||||
module Rswag
|
module Rswag
|
||||||
module Specs
|
module Specs
|
||||||
class ResponseValidator
|
class ResponseValidator
|
||||||
|
|
||||||
def initialize(config = ::Rswag::Specs.config)
|
def initialize(config = ::Rswag::Specs.config)
|
||||||
@config = config
|
@config = config
|
||||||
end
|
end
|
||||||
@ -25,8 +26,8 @@ module Rswag
|
|||||||
expected = metadata[:response][:code].to_s
|
expected = metadata[:response][:code].to_s
|
||||||
if response.code != expected
|
if response.code != expected
|
||||||
raise UnexpectedResponse,
|
raise UnexpectedResponse,
|
||||||
"Expected response code '#{response.code}' to match '#{expected}'\n" \
|
"Expected response code '#{response.code}' to match '#{expected}'\n" \
|
||||||
"Response body: #{response.body}"
|
"Response body: #{response.body}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -40,13 +41,33 @@ module Rswag
|
|||||||
def validate_body!(metadata, swagger_doc, body)
|
def validate_body!(metadata, swagger_doc, body)
|
||||||
response_schema = metadata[:response][:schema]
|
response_schema = metadata[:response][:schema]
|
||||||
return if response_schema.nil?
|
return if response_schema.nil?
|
||||||
|
## OA3
|
||||||
|
# test_schemas = extract_schemas(metadata)
|
||||||
|
# return if test_schemas.nil? || test_schemas.empty?
|
||||||
|
|
||||||
|
# OA3
|
||||||
|
# components = swagger_doc[:components] || {}
|
||||||
|
# components_schemas = { components: { schemas: components[:schemas] } }
|
||||||
|
|
||||||
|
# validation_schema = test_schemas[:schema] # response_schema
|
||||||
validation_schema = response_schema
|
validation_schema = response_schema
|
||||||
.merge('$schema' => 'http://tempuri.org/rswag/specs/extended_schema')
|
.merge('$schema' => 'http://tempuri.org/rswag/specs/extended_schema')
|
||||||
.merge(swagger_doc.slice(:definitions))
|
.merge(swagger_doc.slice(:definitions))
|
||||||
|
## OA3
|
||||||
|
# .merge(components_schemas)
|
||||||
|
|
||||||
errors = JSON::Validator.fully_validate(validation_schema, body)
|
errors = JSON::Validator.fully_validate(validation_schema, body)
|
||||||
raise UnexpectedResponse, "Expected response body to match schema: #{errors[0]}" if errors.any?
|
raise UnexpectedResponse, "Expected response body to match schema: #{errors[0]}" if errors.any?
|
||||||
end
|
end
|
||||||
|
## OA3
|
||||||
|
# def extract_schemas(metadata)
|
||||||
|
# metadata[:operation] = {produces: []} if metadata[:operation].nil?
|
||||||
|
# produces = Array(metadata[:operation][:produces])
|
||||||
|
|
||||||
|
# producer_content = produces.first || 'application/json'
|
||||||
|
# response_content = metadata[:response][:content] || {producer_content => {}}
|
||||||
|
# response_content[producer_content]
|
||||||
|
# end
|
||||||
end
|
end
|
||||||
|
|
||||||
class UnexpectedResponse < StandardError; end
|
class UnexpectedResponse < StandardError; end
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'active_support/core_ext/hash/deep_merge'
|
require 'active_support/core_ext/hash/deep_merge'
|
||||||
require 'swagger_helper'
|
require 'swagger_helper'
|
||||||
|
|
||||||
@ -19,10 +21,10 @@ module Rswag
|
|||||||
|
|
||||||
def example_group_finished(notification)
|
def example_group_finished(notification)
|
||||||
# NOTE: rspec 2.x support
|
# NOTE: rspec 2.x support
|
||||||
if RSPEC_VERSION > 2
|
metadata = if RSPEC_VERSION > 2
|
||||||
metadata = notification.group.metadata
|
notification.group.metadata
|
||||||
else
|
else
|
||||||
metadata = notification.metadata
|
notification.metadata
|
||||||
end
|
end
|
||||||
|
|
||||||
# !metadata[:document] won't work, since nil means we should generate
|
# !metadata[:document] won't work, since nil means we should generate
|
||||||
@ -34,8 +36,30 @@ module Rswag
|
|||||||
swagger_doc.deep_merge!(metadata_to_swagger(metadata))
|
swagger_doc.deep_merge!(metadata_to_swagger(metadata))
|
||||||
end
|
end
|
||||||
|
|
||||||
def stop(notification=nil)
|
def stop(_notification=nil)
|
||||||
@config.swagger_docs.each do |url_path, doc|
|
@config.swagger_docs.each do |url_path, doc|
|
||||||
|
|
||||||
|
## OA3
|
||||||
|
# # remove 2.0 parameters
|
||||||
|
# doc[:paths]&.each_pair do |_k, v|
|
||||||
|
# v.each_pair do |_verb, value|
|
||||||
|
# is_hash = value.is_a?(Hash)
|
||||||
|
# if is_hash && value.dig(:parameters)
|
||||||
|
# schema_param = value&.dig(:parameters)&.find{|p| p[:in] == :body && p[:schema] }
|
||||||
|
# if value && schema_param && value&.dig(:requestBody, :content, 'application/json')
|
||||||
|
# value[:requestBody][:content]['application/json'].merge!(schema: schema_param[:schema])
|
||||||
|
# end
|
||||||
|
|
||||||
|
# value[:parameters].reject! { |p| p[:in] == :body || p[:in] == :formData }
|
||||||
|
# value[:parameters].each { |p| p.delete(:type) }
|
||||||
|
# value[:headers].each { |p| p.delete(:type)} if value[:headers]
|
||||||
|
# end
|
||||||
|
|
||||||
|
# value.delete(:consumes) if is_hash && value.dig(:consumes)
|
||||||
|
# value.delete(:produces) if is_hash && value.dig(:produces)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
file_path = File.join(@config.swagger_root, url_path)
|
file_path = File.join(@config.swagger_root, url_path)
|
||||||
dirname = File.dirname(file_path)
|
dirname = File.dirname(file_path)
|
||||||
FileUtils.mkdir_p dirname unless File.exists?(dirname)
|
FileUtils.mkdir_p dirname unless File.exists?(dirname)
|
||||||
@ -61,12 +85,23 @@ module Rswag
|
|||||||
|
|
||||||
def yaml_prepare(doc)
|
def yaml_prepare(doc)
|
||||||
json_doc = JSON.pretty_generate(doc)
|
json_doc = JSON.pretty_generate(doc)
|
||||||
clean_doc = JSON.parse(json_doc)
|
JSON.parse(json_doc)
|
||||||
end
|
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 }
|
||||||
|
## OA3
|
||||||
|
# content_type = metadata[:response][:content].present? ? metadata[:response][:content].keys.first : 'application/json'
|
||||||
|
# # need to merge in to response
|
||||||
|
# if response[:examples]&.dig(content_type)
|
||||||
|
# example = response[:examples].dig(content_type).dup
|
||||||
|
# schema = response.dig(:content, content_type, :schema)
|
||||||
|
# new_hash = {example: example}
|
||||||
|
# new_hash[:schema] = schema if schema
|
||||||
|
# response.merge!(content: { content_type => new_hash })
|
||||||
|
# response.delete(:examples)
|
||||||
|
# end
|
||||||
|
|
||||||
verb = metadata[:operation][:verb]
|
verb = metadata[:operation][:verb]
|
||||||
operation = metadata[:operation]
|
operation = metadata[:operation]
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
$:.push File.expand_path("../lib", __FILE__)
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
||||||
|
|
||||||
# Describe your gem and declare its dependencies:
|
# Describe your gem and declare its dependencies:
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = "rswag-specs"
|
s.name = 'rswag-specs'
|
||||||
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
|
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
|
||||||
s.authors = ["Richie Morris"]
|
s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian']
|
||||||
s.email = ["domaindrivendev@gmail.com"]
|
s.email = ['domaindrivendev@gmail.com']
|
||||||
s.homepage = "https://github.com/domaindrivendev/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 = 'A Swagger-based DSL for rspec-rails & accompanying rake task for generating Swagger 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 Swagger files directly from your rspecs'
|
||||||
s.license = "MIT"
|
s.license = 'MIT'
|
||||||
|
|
||||||
s.files = Dir["{lib}/**/*"] + ["MIT-LICENSE", "Rakefile" ]
|
s.files = Dir['{lib}/**/*'] + ['MIT-LICENSE', 'Rakefile' ]
|
||||||
|
|
||||||
s.add_dependency 'activesupport', '>= 3.1', '< 7.0'
|
s.add_dependency 'activesupport', '>= 3.1', '< 7.0'
|
||||||
s.add_dependency 'railties', '>= 3.1', '< 7.0'
|
s.add_dependency 'railties', '>= 3.1', '< 7.0'
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rswag/specs/configuration'
|
require 'rswag/specs/configuration'
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rswag/specs/example_group_helpers'
|
require 'rswag/specs/example_group_helpers'
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
@ -86,6 +88,40 @@ module Rswag
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
## OA3
|
||||||
|
# describe '#request_body_json(schema)' do
|
||||||
|
# let(:api_metadata) { { path_item: {}, operation: {} } } # i.e. operation defined
|
||||||
|
# context 'when required is not supplied' do
|
||||||
|
# before { subject.request_body_json(schema: { type: 'object' }) }
|
||||||
|
|
||||||
|
# it 'adds required true by default' do
|
||||||
|
# expect(api_metadata[:operation][:requestBody]).to match(
|
||||||
|
# required: true, content: { 'application/json' => { schema: { type: 'object' } } }
|
||||||
|
# )
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# context 'when required is supplied' do
|
||||||
|
# before { subject.request_body_json(schema: { type: 'object' }, required: false) }
|
||||||
|
|
||||||
|
# it 'adds required false' do
|
||||||
|
# expect(api_metadata[:operation][:requestBody]).to match(
|
||||||
|
# required: false, content: { 'application/json' => { schema: { type: 'object' } } }
|
||||||
|
# )
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# context 'when required is supplied' do
|
||||||
|
# before { subject.request_body_json(schema: { type: 'object' }, description: 'my description') }
|
||||||
|
|
||||||
|
# it 'adds description' do
|
||||||
|
# expect(api_metadata[:operation][:requestBody]).to match(
|
||||||
|
# description: 'my description', required: true, content: { 'application/json' => { schema: { type: 'object' } } }
|
||||||
|
# )
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
describe '#parameter(attributes)' do
|
describe '#parameter(attributes)' do
|
||||||
|
|
||||||
context "when called at the 'path' level" do
|
context "when called at the 'path' level" do
|
||||||
@ -147,6 +183,8 @@ module Rswag
|
|||||||
|
|
||||||
it "adds to the 'response' metadata" do
|
it "adds to the 'response' metadata" do
|
||||||
expect(api_metadata[:response][:schema]).to match(type: 'object')
|
expect(api_metadata[:response][:schema]).to match(type: 'object')
|
||||||
|
## OA3
|
||||||
|
# expect(api_metadata[:response][:content]['application/json'][:schema]).to match(type: 'object')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rswag/specs/example_helpers'
|
require 'rswag/specs/example_helpers'
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
@ -24,6 +26,21 @@ module Rswag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
## OA3
|
||||||
|
# let(:swagger_doc) do
|
||||||
|
# {
|
||||||
|
# components: {
|
||||||
|
# securitySchemes: {
|
||||||
|
# api_key: {
|
||||||
|
# type: :apiKey,
|
||||||
|
# name: 'api_key',
|
||||||
|
# in: :query
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# end
|
||||||
|
|
||||||
let(:metadata) do
|
let(:metadata) do
|
||||||
{
|
{
|
||||||
path_item: { template: '/blogs/{blog_id}/comments/{id}' },
|
path_item: { template: '/blogs/{blog_id}/comments/{id}' },
|
||||||
@ -58,8 +75,10 @@ module Rswag
|
|||||||
it "submits a request built from metadata and 'let' values" do
|
it "submits a request built from metadata and 'let' values" do
|
||||||
expect(subject).to have_received(:put).with(
|
expect(subject).to have_received(:put).with(
|
||||||
'/blogs/1/comments/2?q1=foo&api_key=fookey',
|
'/blogs/1/comments/2?q1=foo&api_key=fookey',
|
||||||
"{\"text\":\"Some comment\"}",
|
'{"text":"Some comment"}',
|
||||||
{ 'CONTENT_TYPE' => 'application/json' }
|
{ 'CONTENT_TYPE' => 'application/json' }
|
||||||
|
## OA3
|
||||||
|
# 'CONTENT_TYPE' => 'application/json'
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rswag/specs/request_factory'
|
require 'rswag/specs/request_factory'
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
@ -204,6 +206,8 @@ module Rswag
|
|||||||
context 'basic auth' do
|
context 'basic auth' do
|
||||||
before do
|
before do
|
||||||
swagger_doc[:securityDefinitions] = { basic: { type: :basic } }
|
swagger_doc[:securityDefinitions] = { basic: { type: :basic } }
|
||||||
|
## OA3
|
||||||
|
# swagger_doc[:components] = { securitySchemes: { basic: { type: :basic } } }
|
||||||
metadata[:operation][:security] = [ basic: [] ]
|
metadata[:operation][:security] = [ basic: [] ]
|
||||||
allow(example).to receive(:Authorization).and_return('Basic foobar')
|
allow(example).to receive(:Authorization).and_return('Basic foobar')
|
||||||
end
|
end
|
||||||
@ -216,6 +220,10 @@ module Rswag
|
|||||||
context 'apiKey' do
|
context 'apiKey' do
|
||||||
before do
|
before do
|
||||||
swagger_doc[:securityDefinitions] = { apiKey: { type: :apiKey, name: 'api_key', in: key_location } }
|
swagger_doc[:securityDefinitions] = { apiKey: { type: :apiKey, name: 'api_key', in: key_location } }
|
||||||
|
## OA3
|
||||||
|
# swagger_doc[:components] = { securitySchemes: {
|
||||||
|
# apiKey: { type: :apiKey, name: 'api_key', in: key_location }
|
||||||
|
# } }
|
||||||
metadata[:operation][:security] = [ apiKey: [] ]
|
metadata[:operation][:security] = [ apiKey: [] ]
|
||||||
allow(example).to receive(:api_key).and_return('foobar')
|
allow(example).to receive(:api_key).and_return('foobar')
|
||||||
end
|
end
|
||||||
@ -257,6 +265,10 @@ module Rswag
|
|||||||
context 'oauth2' do
|
context 'oauth2' do
|
||||||
before do
|
before do
|
||||||
swagger_doc[:securityDefinitions] = { oauth2: { type: :oauth2, scopes: [ 'read:blogs' ] } }
|
swagger_doc[:securityDefinitions] = { oauth2: { type: :oauth2, scopes: [ 'read:blogs' ] } }
|
||||||
|
## OA3
|
||||||
|
# swagger_doc[:components] = { securitySchemes: {
|
||||||
|
# oauth2: { type: :oauth2, scopes: ['read:blogs'] }
|
||||||
|
# } }
|
||||||
metadata[:operation][:security] = [ oauth2: [ 'read:blogs' ] ]
|
metadata[:operation][:security] = [ oauth2: [ 'read:blogs' ] ]
|
||||||
allow(example).to receive(:Authorization).and_return('Bearer foobar')
|
allow(example).to receive(:Authorization).and_return('Bearer foobar')
|
||||||
end
|
end
|
||||||
@ -272,6 +284,11 @@ module Rswag
|
|||||||
basic: { type: :basic },
|
basic: { type: :basic },
|
||||||
api_key: { type: :apiKey, name: 'api_key', in: :query }
|
api_key: { type: :apiKey, name: 'api_key', in: :query }
|
||||||
}
|
}
|
||||||
|
## OA3
|
||||||
|
# swagger_doc[:components] = { securitySchemes: {
|
||||||
|
# basic: { type: :basic },
|
||||||
|
# api_key: { type: :apiKey, name: 'api_key', in: :query }
|
||||||
|
# } }
|
||||||
metadata[:operation][:security] = [ { basic: [], api_key: [] } ]
|
metadata[:operation][:security] = [ { basic: [], api_key: [] } ]
|
||||||
allow(example).to receive(:Authorization).and_return('Basic foobar')
|
allow(example).to receive(:Authorization).and_return('Basic foobar')
|
||||||
allow(example).to receive(:api_key).and_return('foobar')
|
allow(example).to receive(:api_key).and_return('foobar')
|
||||||
@ -327,6 +344,8 @@ module Rswag
|
|||||||
context "global security requirements" do
|
context "global security requirements" do
|
||||||
before do
|
before do
|
||||||
swagger_doc[:securityDefinitions] = { apiKey: { type: :apiKey, name: 'api_key', in: :query } }
|
swagger_doc[:securityDefinitions] = { apiKey: { type: :apiKey, name: 'api_key', in: :query } }
|
||||||
|
## OA3
|
||||||
|
# swagger_doc[:components] = { securitySchemes: { apiKey: { type: :apiKey, name: 'api_key', in: :query } } }
|
||||||
swagger_doc[:security] = [ apiKey: [] ]
|
swagger_doc[:security] = [ apiKey: [] ]
|
||||||
allow(example).to receive(:api_key).and_return('foobar')
|
allow(example).to receive(:api_key).and_return('foobar')
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rswag/specs/response_validator'
|
require 'rswag/specs/response_validator'
|
||||||
|
|
||||||
module Rswag
|
module Rswag
|
||||||
@ -22,6 +24,16 @@ module Rswag
|
|||||||
properties: { text: { type: :string } },
|
properties: { text: { type: :string } },
|
||||||
required: [ 'text' ]
|
required: [ 'text' ]
|
||||||
}
|
}
|
||||||
|
## OA3
|
||||||
|
# content: {
|
||||||
|
# 'application/json' => {
|
||||||
|
# schema: {
|
||||||
|
# type: :object,
|
||||||
|
# properties: { text: { type: :string } },
|
||||||
|
# required: ['text']
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -65,6 +77,16 @@ module Rswag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
metadata[:response][:schema] = { '$ref' => '#/definitions/blog' }
|
metadata[:response][:schema] = { '$ref' => '#/definitions/blog' }
|
||||||
|
## OA3
|
||||||
|
# swagger_doc[:components] = {}
|
||||||
|
# swagger_doc[:components][:schemas] = {
|
||||||
|
# 'blog' => {
|
||||||
|
# type: :object,
|
||||||
|
# properties: { foo: { type: :string } },
|
||||||
|
# required: ['foo']
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# metadata[:response][:content]['application/json'][:schema] = { '$ref' => '#/components/schemas/blog' }
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'uses the referenced schema to validate the response body' do
|
it 'uses the referenced schema to validate the response body' do
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rswag/specs/swagger_formatter'
|
require 'rswag/specs/swagger_formatter'
|
||||||
require 'ostruct'
|
require 'ostruct'
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
if base_path?(env)
|
if base_path?(env)
|
||||||
redirect_uri = env['SCRIPT_NAME'].chomp('/') + '/index.html'
|
redirect_uri = env['SCRIPT_NAME'].chomp('/') + '/index.html'
|
||||||
return [ 301, { 'Location' => redirect_uri }, [ ] ]
|
return [ 301, { 'Location' => redirect_uri }, [ ] ]
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
$:.push File.expand_path("../lib", __FILE__)
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
||||||
|
|
||||||
# Describe your gem and declare its dependencies:
|
# Describe your gem and declare its dependencies:
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = "rswag-ui"
|
s.name = 'rswag-ui'
|
||||||
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
|
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
|
||||||
s.authors = ["Richie Morris"]
|
s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian']
|
||||||
s.email = ["domaindrivendev@gmail.com"]
|
s.email = ['domaindrivendev@gmail.com']
|
||||||
s.homepage = "https://github.com/domaindrivendev/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 Swagger endpoints'
|
||||||
s.description = "Expose beautiful API documentation, that's 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'
|
||||||
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' ]
|
||||||
|
|
||||||
s.add_dependency 'actionpack', '>=3.1', '< 7.0'
|
s.add_dependency 'actionpack', '>=3.1', '< 7.0'
|
||||||
s.add_dependency 'railties', '>= 3.1', '< 7.0'
|
s.add_dependency 'railties', '>= 3.1', '< 7.0'
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
$:.push File.expand_path("../lib", __FILE__)
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
||||||
|
|
||||||
# Describe your gem and declare its dependencies:
|
# Describe your gem and declare its dependencies:
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = "rswag"
|
s.name = 'rswag'
|
||||||
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
|
s.version = ENV['TRAVIS_TAG'] || '0.0.0'
|
||||||
s.authors = ["Richie Morris"]
|
s.authors = ['Richie Morris', 'Greg Myers', 'Jay Danielian']
|
||||||
s.email = ["domaindrivendev@gmail.com"]
|
s.email = ['domaindrivendev@gmail.com']
|
||||||
s.homepage = "https://github.com/domaindrivendev/rswag"
|
s.homepage = 'https://github.com/rswag/rswag'
|
||||||
s.summary = "Swagger tooling for Rails API's"
|
s.summary = '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'
|
||||||
s.license = "MIT"
|
s.license = 'MIT'
|
||||||
|
|
||||||
s.files = Dir["{lib}/**/*"] + [ "MIT-LICENSE" ]
|
s.files = Dir['{lib}/**/*'] + [ 'MIT-LICENSE' ]
|
||||||
|
|
||||||
s.add_dependency 'rswag-specs', ENV['TRAVIS_TAG'] || '0.0.0'
|
s.add_dependency 'rswag-specs', ENV['TRAVIS_TAG'] || '0.0.0'
|
||||||
s.add_dependency 'rswag-api', ENV['TRAVIS_TAG'] || '0.0.0'
|
s.add_dependency 'rswag-api', ENV['TRAVIS_TAG'] || '0.0.0'
|
||||||
|
|||||||
@ -22,7 +22,7 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'installs initializer for rswag-api' do
|
it 'installs initializer for rswag-api' do
|
||||||
assert_file('config/rswag-api.rb')
|
assert_file('config/rswag_api.rb')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'installs initializer for rswag-ui' do
|
it 'installs initializer for rswag-ui' do
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
exit
|
|
||||||
env['PATH_INFO']
|
|
||||||
env['SCRIPT_NAME']
|
|
||||||
env
|
|
||||||
@ -5,3 +5,8 @@
|
|||||||
require File.expand_path('../config/application', __FILE__)
|
require File.expand_path('../config/application', __FILE__)
|
||||||
|
|
||||||
TestApp::Application.load_tasks
|
TestApp::Application.load_tasks
|
||||||
|
|
||||||
|
RSpec::Core::RakeTask.new('swaggerize') do |t|
|
||||||
|
t.pattern = 'spec/requests/**/*_spec.rb, spec/api/**/*_spec.rb, spec/integration/**/*_spec.rb'
|
||||||
|
t.rspec_opts = [ '--format Rswag::Specs::SwaggerFormatter', '--order defined' ]
|
||||||
|
end
|
||||||
|
|||||||
@ -8,6 +8,25 @@ class BlogsController < ApplicationController
|
|||||||
respond_with @blog
|
respond_with @blog
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# POST /blogs/flexible
|
||||||
|
def flexible_create
|
||||||
|
|
||||||
|
# contrived example to play around with new anyOf and oneOf
|
||||||
|
# request body definition for 3.0
|
||||||
|
blog_params = params.require(:blog).permit(:title, :content, :headline, :text)
|
||||||
|
|
||||||
|
@blog = Blog.create(blog_params)
|
||||||
|
respond_with @blog
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /blogs/alternate
|
||||||
|
def alternate_create
|
||||||
|
|
||||||
|
# contrived example to show different :examples in the requestBody section
|
||||||
|
@blog = Blog.create(params.require(:blog).permit(:title, :content))
|
||||||
|
respond_with @blog
|
||||||
|
end
|
||||||
|
|
||||||
# Put /blogs/1
|
# Put /blogs/1
|
||||||
def upload
|
def upload
|
||||||
@blog = Blog.find_by_id(params[:id])
|
@blog = Blog.find_by_id(params[:id])
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Blog < ActiveRecord::Base
|
class Blog < ActiveRecord::Base
|
||||||
validates :content, presence: true
|
validates :content, presence: true
|
||||||
|
|
||||||
def as_json(options)
|
alias_attribute :headline, :title
|
||||||
|
alias_attribute :text, :content
|
||||||
|
|
||||||
|
def as_json(_options)
|
||||||
{
|
{
|
||||||
id: id,
|
id: id,
|
||||||
title: title,
|
title: title,
|
||||||
content: nil,
|
content: content,
|
||||||
thumbnail: thumbnail
|
thumbnail: thumbnail
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
TestApp::Application.routes.draw do
|
TestApp::Application.routes.draw do
|
||||||
|
|
||||||
|
post '/blogs/flexible', to: 'blogs#flexible_create'
|
||||||
|
post '/blogs/alternate', to: 'blogs#alternate_create'
|
||||||
resources :blogs
|
resources :blogs
|
||||||
put '/blogs/:id/upload', to: 'blogs#upload'
|
put '/blogs/:id/upload', to: 'blogs#upload'
|
||||||
|
|
||||||
@ -6,6 +9,6 @@ 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'
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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'
|
||||||
operationId 'testBasicAuth'
|
operationId 'testBasicAuth'
|
||||||
security [ basic_auth: [] ]
|
security [basic_auth: []]
|
||||||
|
|
||||||
response '204', 'Valid credentials' do
|
response '204', 'Valid credentials' do
|
||||||
let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
|
let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
|
||||||
@ -24,7 +25,7 @@ RSpec.describe 'Auth Tests API', type: :request, swagger_doc: 'v1/swagger.json'
|
|||||||
post 'Authenticates with an api key' do
|
post 'Authenticates with an api key' do
|
||||||
tags 'Auth Tests'
|
tags 'Auth Tests'
|
||||||
operationId 'testApiKey'
|
operationId 'testApiKey'
|
||||||
security [ api_key: [] ]
|
security [api_key: []]
|
||||||
|
|
||||||
response '204', 'Valid credentials' do
|
response '204', 'Valid credentials' do
|
||||||
let(:api_key) { 'foobar' }
|
let(:api_key) { 'foobar' }
|
||||||
@ -42,7 +43,7 @@ RSpec.describe 'Auth Tests API', type: :request, swagger_doc: 'v1/swagger.json'
|
|||||||
post 'Authenticates with basic auth and api key' do
|
post 'Authenticates with basic auth and api key' do
|
||||||
tags 'Auth Tests'
|
tags 'Auth Tests'
|
||||||
operationId 'testBasicAndApiKey'
|
operationId 'testBasicAndApiKey'
|
||||||
security [ { basic_auth: [], api_key: [] } ]
|
security [{ basic_auth: [], api_key: [] }]
|
||||||
|
|
||||||
response '204', 'Valid credentials' do
|
response '204', 'Valid credentials' do
|
||||||
let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
|
let(:Authorization) { "Basic #{::Base64.strict_encode64('jsmith:jspass')}" }
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'swagger_helper'
|
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
|
||||||
@ -10,18 +12,24 @@ RSpec.describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
|
|||||||
operationId 'createBlog'
|
operationId 'createBlog'
|
||||||
consumes 'application/json'
|
consumes 'application/json'
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
parameter name: :blog, in: :body, schema: { '$ref' => '#/definitions/blog' }
|
|
||||||
|
|
||||||
let(:blog) { { title: 'foo', content: 'bar' } }
|
request_body_json schema: { '$ref' => '#/components/schemas/blog' },
|
||||||
|
examples: :blog
|
||||||
|
|
||||||
|
request_body_text_plain
|
||||||
|
request_body_xml schema: { '$ref' => '#/components/schemas/blog' }
|
||||||
|
|
||||||
|
let(:blog) { { blog: { title: 'foo', content: 'bar' } } }
|
||||||
|
|
||||||
response '201', 'blog created' do
|
response '201', 'blog created' do
|
||||||
|
schema '$ref' => '#/components/schemas/blog'
|
||||||
run_test!
|
run_test!
|
||||||
end
|
end
|
||||||
|
|
||||||
response '422', 'invalid request' do
|
response '422', 'invalid request' do
|
||||||
schema '$ref' => '#/definitions/errors_object'
|
schema '$ref' => '#/components/schemas/errors_object'
|
||||||
|
let(:blog) { { blog: { title: 'foo' } } }
|
||||||
|
|
||||||
let(:blog) { { title: 'foo' } }
|
|
||||||
run_test! do |response|
|
run_test! do |response|
|
||||||
expect(response.body).to include("can't be blank")
|
expect(response.body).to include("can't be blank")
|
||||||
end
|
end
|
||||||
@ -38,18 +46,69 @@ RSpec.describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
|
|||||||
let(:keywords) { 'foo bar' }
|
let(:keywords) { 'foo bar' }
|
||||||
|
|
||||||
response '200', 'success' do
|
response '200', 'success' do
|
||||||
schema type: 'array', items: { '$ref' => '#/definitions/blog' }
|
schema type: 'array', items: { '$ref' => '#/components/schemas/blog' }
|
||||||
|
run_test!
|
||||||
end
|
end
|
||||||
|
|
||||||
response '406', 'unsupported accept header' do
|
response '406', 'unsupported accept header' do
|
||||||
let(:'Accept') { 'application/foo' }
|
let(:Accept) { 'application/foo' }
|
||||||
run_test!
|
run_test!
|
||||||
end
|
end
|
||||||
end
|
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'
|
||||||
|
|
||||||
|
request_body_json schema: {
|
||||||
|
:oneOf => [{'$ref' => '#/components/schemas/blog'},
|
||||||
|
{'$ref' => '#/components/schemas/flexible_blog'}]
|
||||||
|
},
|
||||||
|
examples: :flexible_blog
|
||||||
|
|
||||||
|
let(:flexible_blog) { { blog: { headline: 'my headline', text: 'my text' } } }
|
||||||
|
|
||||||
|
response '201', 'flexible blog created' do
|
||||||
|
schema :oneOf => [{'$ref' => '#/components/schemas/blog'},{'$ref' => '#/components/schemas/flexible_blog'}]
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
path '/blogs/alternate' do
|
||||||
|
post 'Creates a blog - different :examples in requestBody' do
|
||||||
|
tags 'Blogs'
|
||||||
|
description 'Creates a new blog from provided data'
|
||||||
|
operationId 'createAlternateBlog'
|
||||||
|
consumes 'application/json'
|
||||||
|
produces 'application/json'
|
||||||
|
|
||||||
|
# NOTE: the externalValue: http://... is valid 3.0 spec, but swagger-UI does NOT support it yet
|
||||||
|
# https://github.com/swagger-api/swagger-ui/issues/5433
|
||||||
|
request_body_json schema: { '$ref' => '#/components/schemas/blog' },
|
||||||
|
examples: [:blog, {name: :external_blog,
|
||||||
|
externalValue: 'http://api.sample.org/myjson_example'},
|
||||||
|
{name: :another_example,
|
||||||
|
'$ref' => '#/components/examples/flexible_blog_example'}]
|
||||||
|
|
||||||
|
let(:blog) { { blog: { title: 'alt title', content: 'alt bar' } } }
|
||||||
|
|
||||||
|
response '201', 'blog created' do
|
||||||
|
schema '$ref' => '#/components/schemas/blog'
|
||||||
|
run_test!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
path '/blogs/{id}' do
|
path '/blogs/{id}' do
|
||||||
parameter name: :id, in: :path, type: :string
|
|
||||||
|
|
||||||
let(:id) { blog.id }
|
let(:id) { blog.id }
|
||||||
let(:blog) { Blog.create(title: 'foo', content: 'bar', thumbnail: 'thumbnail.png') }
|
let(:blog) { Blog.create(title: 'foo', content: 'bar', thumbnail: 'thumbnail.png') }
|
||||||
@ -60,19 +119,21 @@ RSpec.describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
|
|||||||
operationId 'getBlog'
|
operationId 'getBlog'
|
||||||
produces 'application/json'
|
produces 'application/json'
|
||||||
|
|
||||||
|
parameter name: :id, in: :path, type: :string
|
||||||
|
|
||||||
response '200', 'blog found' do
|
response '200', 'blog found' do
|
||||||
header 'ETag', type: :string
|
header 'ETag', type: :string
|
||||||
header 'Last-Modified', type: :string
|
header 'Last-Modified', type: :string
|
||||||
header 'Cache-Control', type: :string
|
header 'Cache-Control', type: :string
|
||||||
|
|
||||||
schema '$ref' => '#/definitions/blog'
|
schema '$ref' => '#/components/schemas/blog'
|
||||||
|
|
||||||
examples 'application/json' => {
|
examples 'application/json' => {
|
||||||
id: 1,
|
id: 1,
|
||||||
title: 'Hello world!',
|
title: 'Hello world!',
|
||||||
content: 'Hello world and hello universe. Thank you all very much!!!',
|
content: 'Hello world and hello universe. Thank you all very much!!!',
|
||||||
thumbnail: "thumbnail.png"
|
thumbnail: 'thumbnail.png'
|
||||||
}
|
}
|
||||||
|
|
||||||
let(:id) { blog.id }
|
let(:id) { blog.id }
|
||||||
run_test!
|
run_test!
|
||||||
@ -85,21 +146,23 @@ RSpec.describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
path '/blogs/{id}/upload' do
|
|
||||||
parameter name: :id, in: :path, type: :string
|
|
||||||
|
|
||||||
|
path '/blogs/{id}/upload' do
|
||||||
let(:id) { blog.id }
|
let(:id) { blog.id }
|
||||||
let(:blog) { Blog.create(title: 'foo', content: 'bar') }
|
let(:blog) { Blog.create(title: 'foo', content: 'bar') }
|
||||||
|
|
||||||
put 'Uploads a blog thumbnail' do
|
put 'Uploads a blog thumbnail' do
|
||||||
|
parameter name: :id, in: :path, type: :string
|
||||||
|
|
||||||
tags 'Blogs'
|
tags 'Blogs'
|
||||||
description 'Upload a thumbnail for specific blog by id'
|
description 'Upload a thumbnail for specific blog by id'
|
||||||
operationId 'uploadThumbnailBlog'
|
operationId 'uploadThumbnailBlog'
|
||||||
consumes 'multipart/form-data'
|
consumes 'multipart/form-data'
|
||||||
parameter name: :file, :in => :formData, :type => :file, required: true
|
|
||||||
|
request_body_multipart schema: {properties: {:orderId => { type: :integer }, file: { type: :string, format: :binary }} }
|
||||||
|
|
||||||
response '200', 'blog updated' do
|
response '200', 'blog updated' do
|
||||||
let(:file) { Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures/thumbnail.png")) }
|
let(:file) { Rack::Test::UploadedFile.new(Rails.root.join('spec/fixtures/thumbnail.png')) }
|
||||||
run_test!
|
run_test!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
||||||
ENV['RAILS_ENV'] ||= 'test'
|
ENV['RAILS_ENV'] ||= 'test'
|
||||||
require File.expand_path('../../config/environment', __FILE__)
|
require File.expand_path('../config/environment', __dir__)
|
||||||
# Prevent database truncation if the environment is production
|
# Prevent database truncation if the environment is production
|
||||||
abort("The Rails environment is running in production mode!") if Rails.env.production?
|
abort('The Rails environment is running in production mode!') if Rails.env.production?
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rspec/rails'
|
require 'rspec/rails'
|
||||||
# Add additional requires below this line. Rails is not loaded until this point!
|
# Add additional requires below this line. Rails is not loaded until this point!
|
||||||
@ -54,6 +56,4 @@ RSpec.configure do |config|
|
|||||||
Capybara.javascript_driver = :webkit
|
Capybara.javascript_driver = :webkit
|
||||||
end
|
end
|
||||||
|
|
||||||
Capybara::Webkit.configure do |config|
|
Capybara::Webkit.configure(&:block_unknown_urls)
|
||||||
config.block_unknown_urls
|
|
||||||
end
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
require 'rake'
|
require 'rake'
|
||||||
|
|
||||||
@ -5,11 +7,11 @@ 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.exist?(swagger_root)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'generates Swagger JSON files from integration specs' do
|
it 'generates Swagger JSON files from integration specs' do
|
||||||
expect { Rake::Task['rswag:specs:swaggerize'].invoke }.not_to raise_exception
|
Rake::Task['rswag:specs:swaggerize'].invoke
|
||||||
expect(File).to exist("#{swagger_root}/v1/swagger.json")
|
expect(File).to exist("#{swagger_root}/v1/swagger.json")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# This file was generated by the `rails generate rspec:install` command. Conventionally, all
|
# This file was generated by the `rails generate rspec:install` command. Conventionally, all
|
||||||
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
||||||
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
||||||
@ -50,54 +52,4 @@ RSpec.configure do |config|
|
|||||||
config.after(:suite) do
|
config.after(:suite) do
|
||||||
File.delete("#{Rails.root}/tmp/thumbnail.png") if File.file?("#{Rails.root}/tmp/thumbnail.png")
|
File.delete("#{Rails.root}/tmp/thumbnail.png") if File.file?("#{Rails.root}/tmp/thumbnail.png")
|
||||||
end
|
end
|
||||||
|
|
||||||
# The settings below are suggested to provide a good initial experience
|
|
||||||
# with RSpec, but feel free to customize to your heart's content.
|
|
||||||
=begin
|
|
||||||
# This allows you to limit a spec run to individual examples or groups
|
|
||||||
# you care about by tagging them with `:focus` metadata. When nothing
|
|
||||||
# is tagged with `:focus`, all examples get run. RSpec also provides
|
|
||||||
# aliases for `it`, `describe`, and `context` that include `:focus`
|
|
||||||
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
|
||||||
config.filter_run_when_matching :focus
|
|
||||||
|
|
||||||
# Allows RSpec to persist some state between runs in order to support
|
|
||||||
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
|
||||||
# you configure your source control system to ignore this file.
|
|
||||||
config.example_status_persistence_file_path = "spec/examples.txt"
|
|
||||||
|
|
||||||
# Limits the available syntax to the non-monkey patched syntax that is
|
|
||||||
# recommended. For more details, see:
|
|
||||||
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
|
||||||
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
|
||||||
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
|
||||||
config.disable_monkey_patching!
|
|
||||||
|
|
||||||
# Many RSpec users commonly either run the entire suite or an individual
|
|
||||||
# file, and it's useful to allow more verbose output when running an
|
|
||||||
# individual spec file.
|
|
||||||
if config.files_to_run.one?
|
|
||||||
# Use the documentation formatter for detailed output,
|
|
||||||
# unless a formatter has already been configured
|
|
||||||
# (e.g. via a command-line flag).
|
|
||||||
config.default_formatter = 'doc'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Print the 10 slowest examples and example groups at the
|
|
||||||
# end of the spec run, to help surface which specs are running
|
|
||||||
# particularly slow.
|
|
||||||
config.profile_examples = 10
|
|
||||||
|
|
||||||
# Run specs in random order to surface order dependencies. If you find an
|
|
||||||
# order dependency and want to debug it, you can fix the order by providing
|
|
||||||
# the seed, which is printed after each run.
|
|
||||||
# --seed 1234
|
|
||||||
config.order = :random
|
|
||||||
|
|
||||||
# Seed global randomization in this process using the `--seed` CLI option.
|
|
||||||
# Setting this allows you to use `--seed` to deterministically reproduce
|
|
||||||
# test failures related to randomization by passing the same `--seed` value
|
|
||||||
# as the one that triggered the failure.
|
|
||||||
Kernel.srand config.seed
|
|
||||||
=end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
@ -5,7 +7,7 @@ RSpec.configure do |config|
|
|||||||
# 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.to_s + '/swagger'
|
||||||
|
config.swagger_dry_run = false
|
||||||
# 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:to_swagger' rake task, the complete Swagger will
|
# When you run the 'rswag:specs:to_swagger' rake task, the complete Swagger will
|
||||||
# be generated at the provided relative path under swagger_root
|
# be generated at the provided relative path under swagger_root
|
||||||
@ -14,45 +16,79 @@ RSpec.configure do |config|
|
|||||||
# 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.json' => {
|
||||||
swagger: '2.0',
|
openapi: '3.0.0',
|
||||||
info: {
|
info: {
|
||||||
title: 'API V1',
|
title: 'API V1',
|
||||||
version: 'v1'
|
version: 'v1'
|
||||||
},
|
},
|
||||||
paths: {},
|
paths: {},
|
||||||
definitions: {
|
servers: [
|
||||||
errors_object: {
|
{
|
||||||
type: 'object',
|
url: 'https://{defaultHost}',
|
||||||
properties: {
|
variables: {
|
||||||
errors: { '$ref' => '#/definitions/errors_map' }
|
defaultHost: {
|
||||||
|
default: 'www.example.com'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
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'}
|
|
||||||
},
|
|
||||||
required: [ 'id', 'title', 'content', 'thumbnail' ]
|
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
securityDefinitions: {
|
|
||||||
basic_auth: {
|
components: {
|
||||||
type: :basic
|
schemas: {
|
||||||
|
errors_object: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
errors: { '$ref' => '#/components/schemas/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', nullable: true },
|
||||||
|
thumbnail: { type: 'string', nullable: true }
|
||||||
|
},
|
||||||
|
required: %w[id title]
|
||||||
|
},
|
||||||
|
flexible_blog: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: { type: 'integer' },
|
||||||
|
headline: { type: 'string' },
|
||||||
|
text: { type: 'string', nullable: true },
|
||||||
|
thumbnail: { type: 'string', nullable:true }
|
||||||
|
},
|
||||||
|
required: %w[id headline]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
api_key: {
|
examples: {
|
||||||
type: :apiKey,
|
flexible_blog_example: {
|
||||||
name: 'api_key',
|
summary: 'Sample example of a flexible blog',
|
||||||
in: :query
|
value: {
|
||||||
|
id: 1,
|
||||||
|
headline: 'This is a headline',
|
||||||
|
text: 'Some sample text'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
securitySchemes: {
|
||||||
|
basic_auth: {
|
||||||
|
type: :http,
|
||||||
|
scheme: :basic
|
||||||
|
},
|
||||||
|
api_key: {
|
||||||
|
type: :apiKey,
|
||||||
|
name: 'api_key',
|
||||||
|
in: :query
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"swagger": "2.0",
|
"openapi": "3.0.0",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "API V1",
|
"title": "API V1",
|
||||||
"version": "v1"
|
"version": "v1"
|
||||||
@ -88,29 +88,71 @@
|
|||||||
],
|
],
|
||||||
"description": "Creates a new blog from provided data",
|
"description": "Creates a new blog from provided data",
|
||||||
"operationId": "createBlog",
|
"operationId": "createBlog",
|
||||||
"consumes": [
|
"requestBody": {
|
||||||
"application/json"
|
"required": true,
|
||||||
],
|
"content": {
|
||||||
"produces": [
|
"application/json": {
|
||||||
"application/json"
|
"examples": {
|
||||||
],
|
"blog": {
|
||||||
"parameters": [
|
"value": {
|
||||||
{
|
"blog": {
|
||||||
"name": "blog",
|
"title": "foo",
|
||||||
"in": "body",
|
"content": "bar"
|
||||||
"schema": {
|
}
|
||||||
"$ref": "#/definitions/blog"
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test/plain": {
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/xml": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"201": {
|
"201": {
|
||||||
"description": "blog created"
|
"description": "blog created",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"example": {
|
||||||
|
"id": 1,
|
||||||
|
"title": "foo",
|
||||||
|
"content": "bar",
|
||||||
|
"thumbnail": null
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"422": {
|
"422": {
|
||||||
"description": "invalid request",
|
"description": "invalid request",
|
||||||
"schema": {
|
"content": {
|
||||||
"$ref": "#/definitions/errors_object"
|
"application/json": {
|
||||||
|
"example": {
|
||||||
|
"errors": {
|
||||||
|
"content": [
|
||||||
|
"can't be blank"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/errors_object"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,32 +164,158 @@
|
|||||||
],
|
],
|
||||||
"description": "Searches blogs by keywords",
|
"description": "Searches blogs by keywords",
|
||||||
"operationId": "searchBlogs",
|
"operationId": "searchBlogs",
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"name": "keywords",
|
"name": "keywords",
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"type": "string"
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "success",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"406": {
|
"406": {
|
||||||
"description": "unsupported accept header"
|
"description": "unsupported accept header"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/blogs/{id}": {
|
"/blogs/flexible": {
|
||||||
"parameters": [
|
"post": {
|
||||||
{
|
"summary": "Creates a blog flexible body",
|
||||||
"name": "id",
|
"tags": [
|
||||||
"in": "path",
|
"Blogs"
|
||||||
"type": "string",
|
],
|
||||||
"required": true
|
"description": "Creates a flexible blog from provided data",
|
||||||
|
"operationId": "createFlexibleBlog",
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"examples": {
|
||||||
|
"flexible_blog": {
|
||||||
|
"value": {
|
||||||
|
"blog": {
|
||||||
|
"headline": "my headline",
|
||||||
|
"text": "my text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/flexible_blog"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "flexible blog created",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"example": {
|
||||||
|
"id": 1,
|
||||||
|
"title": "my headline",
|
||||||
|
"content": "my text",
|
||||||
|
"thumbnail": null
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/flexible_blog"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
}
|
||||||
|
},
|
||||||
|
"/blogs/alternate": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Creates a blog - different :examples in requestBody",
|
||||||
|
"tags": [
|
||||||
|
"Blogs"
|
||||||
|
],
|
||||||
|
"description": "Creates a new blog from provided data",
|
||||||
|
"operationId": "createAlternateBlog",
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"examples": {
|
||||||
|
"blog": {
|
||||||
|
"value": {
|
||||||
|
"blog": {
|
||||||
|
"title": "alt title",
|
||||||
|
"content": "alt bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"external_blog": {
|
||||||
|
"externalValue": "http://api.sample.org/myjson_example"
|
||||||
|
},
|
||||||
|
"another_example": {
|
||||||
|
"$ref": "#/components/examples/flexible_blog_example"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"parameters": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "blog created",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"example": {
|
||||||
|
"id": 1,
|
||||||
|
"title": "alt title",
|
||||||
|
"content": "alt bar",
|
||||||
|
"thumbnail": null
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/blogs/{id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"summary": "Retrieves a blog",
|
"summary": "Retrieves a blog",
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -155,32 +323,47 @@
|
|||||||
],
|
],
|
||||||
"description": "Retrieves a specific blog by id",
|
"description": "Retrieves a specific blog by id",
|
||||||
"operationId": "getBlog",
|
"operationId": "getBlog",
|
||||||
"produces": [
|
"parameters": [
|
||||||
"application/json"
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "blog found",
|
"description": "blog found",
|
||||||
"headers": {
|
"headers": {
|
||||||
"ETag": {
|
"ETag": {
|
||||||
"type": "string"
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Last-Modified": {
|
"Last-Modified": {
|
||||||
"type": "string"
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Cache-Control": {
|
"Cache-Control": {
|
||||||
"type": "string"
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"schema": {
|
"content": {
|
||||||
"$ref": "#/definitions/blog"
|
|
||||||
},
|
|
||||||
"examples": {
|
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"id": 1,
|
"example": {
|
||||||
"title": "Hello world!",
|
"id": 1,
|
||||||
"content": "Hello world and hello universe. Thank you all very much!!!",
|
"title": "Hello world!",
|
||||||
"thumbnail": "thumbnail.png"
|
"content": "Hello world and hello universe. Thank you all very much!!!",
|
||||||
|
"thumbnail": "thumbnail.png"
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -191,32 +374,40 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/blogs/{id}/upload": {
|
"/blogs/{id}/upload": {
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "id",
|
|
||||||
"in": "path",
|
|
||||||
"type": "string",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"put": {
|
"put": {
|
||||||
"summary": "Uploads a blog thumbnail",
|
"summary": "Uploads a blog thumbnail",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Blogs"
|
"Blogs"
|
||||||
],
|
],
|
||||||
"description": "Upload a thumbnail for specific blog by id",
|
"description": "Upload a thumbnail for specific blog by id",
|
||||||
"operationId": "uploadThumbnailBlog",
|
"operationId": "uploadThumbnailBlog",
|
||||||
"consumes": [
|
"requestBody": {
|
||||||
"multipart/form-data"
|
"content": {
|
||||||
],
|
"multipart/form-data": {
|
||||||
"parameters": [
|
"schema": {
|
||||||
{
|
"properties": {
|
||||||
"name": "file",
|
"orderId": {
|
||||||
"in": "formData",
|
"type": "integer"
|
||||||
"type": "file",
|
},
|
||||||
"required": true
|
"file": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "binary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "blog updated"
|
"description": "blog updated"
|
||||||
@ -225,57 +416,102 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"servers": [
|
||||||
"errors_object": {
|
{
|
||||||
"type": "object",
|
"url": "https://{defaultHost}",
|
||||||
"properties": {
|
"variables": {
|
||||||
"errors": {
|
"defaultHost": {
|
||||||
"$ref": "#/definitions/errors_map"
|
"default": "www.example.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"errors_map": {
|
],
|
||||||
"type": "object",
|
"components": {
|
||||||
"additionalProperties": {
|
"schemas": {
|
||||||
"type": "array",
|
"errors_object": {
|
||||||
"items": {
|
"type": "object",
|
||||||
"type": "string"
|
"properties": {
|
||||||
}
|
"errors": {
|
||||||
}
|
"$ref": "#/components/schemas/errors_map"
|
||||||
},
|
}
|
||||||
"blog": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"title": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"content": {
|
|
||||||
"type": "string",
|
|
||||||
"x-nullable": true
|
|
||||||
},
|
|
||||||
"thumbnail": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"errors_map": {
|
||||||
"id",
|
"type": "object",
|
||||||
"title",
|
"additionalProperties": {
|
||||||
"content",
|
"type": "array",
|
||||||
"thumbnail"
|
"items": {
|
||||||
]
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"securityDefinitions": {
|
},
|
||||||
"basic_auth": {
|
"blog": {
|
||||||
"type": "basic"
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"thumbnail": {
|
||||||
|
"type": "string",
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"api_key": {
|
"examples": {
|
||||||
"type": "apiKey",
|
"flexible_blog_example": {
|
||||||
"name": "api_key",
|
"summary": "Sample example of a flexible blog",
|
||||||
"in": "query"
|
"value": {
|
||||||
|
"id": 1,
|
||||||
|
"headline": "This is a headline",
|
||||||
|
"text": "Some sample text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {
|
||||||
|
"basic_auth": {
|
||||||
|
"type": "http",
|
||||||
|
"scheme": "basic"
|
||||||
|
},
|
||||||
|
"api_key": {
|
||||||
|
"type": "apiKey",
|
||||||
|
"name": "api_key",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user