Merge pull request #371 from rswag/jamie

PR Grooming
This commit is contained in:
Blake Erickson 2021-01-27 07:59:59 -07:00 committed by GitHub
commit b86d3063a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 721 additions and 76 deletions

View File

@ -1,15 +1,17 @@
# rswag # rswag
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
### Added ### Added
- RequestBody now supports the `required` flag [#342](https://github.com/rswag/rswag/pull/342)
### Changed ### Changed
### Deprecated ### Deprecated
### Removed ### Removed
### Fixed ### Fixed
- Fix response example rendering [#330](https://github.com/rswag/rswag/pull/330)
- Fix empty content block [#347](https://github.com/rswag/rswag/pull/347)
### Security ### Security
## [2.3.1] - 2020-04-08 ## [2.3.1] - 2020-04-08

View File

@ -363,8 +363,8 @@ you should use the folowing syntax, making sure there are no whitespaces at the
### Specifying/Testing API Security ### ### Specifying/Testing API Security ###
Swagger allows for the specification of different security schemes and their applicability to operations in an API. Swagger allows for the specification of different security schemes and their applicability to operations in an API.
To leverage this in rswag, you define the schemes globally in _swagger_helper.rb_ and then use the "security" attribute at the operation level to specify which schemes, if any, are applicable to that operation. To leverage this in rswag, you define the schemes globally in _swagger_helper.rb_ and then use the "security" attribute at the operation level to specify which schemes, if any, are applicable to that operation.
Swagger supports :basic, :bearer, :apiKey and :oauth2 and :openIdConnect scheme types. See [the spec](https://swagger.io/docs/specification/authentication/) for more info, as this underwent major changes between Swagger 2.0 and Open API 3.0 Swagger supports :basic, :bearer, :apiKey and :oauth2 and :openIdConnect scheme types. See [the spec](https://swagger.io/docs/specification/authentication/) for more info, as this underwent major changes between Swagger 2.0 and Open API 3.0
```ruby ```ruby
@ -416,7 +416,7 @@ describe 'Blogs API' do
end end
# example of documenting an endpoint that handles basic auth and api key based security # example of documenting an endpoint that handles basic auth and api key based security
describe 'Auth examples API' do describe 'Auth examples API' do
path '/auth-tests/basic-and-api-key' do path '/auth-tests/basic-and-api-key' do
post 'Authenticates with basic auth and api key' do post 'Authenticates with basic auth and api key' do
tags 'Auth Tests' tags 'Auth Tests'
@ -437,11 +437,11 @@ describe 'Auth examples API' do
end end
end end
end end
``` ```
__NOTE:__ Depending on the scheme types, you'll be required to assign a corresponding parameter value with each example. __NOTE:__ Depending on the scheme types, you'll be required to assign a corresponding parameter value with each example.
For example, :basic auth is required above and so the :Authorization (header) parameter must be set accordingly For example, :basic auth is required above and so the :Authorization (header) parameter must be set accordingly
## Configuration & Customization ## ## Configuration & Customization ##
@ -479,9 +479,9 @@ rake rswag:specs:swaggerize PATTERN="spec/swagger/**/*_spec.rb"
### Referenced Parameters and Schema Definitions ### ### Referenced Parameters and Schema Definitions ###
Swagger allows you to describe JSON structures inline with your operation descriptions OR as referenced globals. Swagger allows you to describe JSON structures inline with your operation descriptions OR as referenced globals.
For example, you might have a standard response structure for all failed operations. For example, you might have a standard response structure for all failed operations.
Again, this is a structure that changed since swagger 2.0. Notice the new "schemas" section for these. Again, this is a structure that changed since swagger 2.0. Notice the new "schemas" section for these.
Rather than repeating the schema in every operation spec, you can define it globally and provide a reference to it in each spec: Rather than repeating the schema in every operation spec, you can define it globally and provide a reference to it in each spec:
```ruby ```ruby
@ -560,7 +560,7 @@ end
### Response headers ### ### Response headers ###
In Rswag, you could use `header` method inside the response block to specify header objects for this response. In Rswag, you could use `header` method inside the response block to specify header objects for this response.
Rswag will validate your response headers with those header objects and inject them into the generated swagger file: Rswag will validate your response headers with those header objects and inject them into the generated swagger file:
```ruby ```ruby
@ -608,16 +608,20 @@ To enable examples generation from responses add callback above run_test! like:
``` ```
after do |example| after do |example|
example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) } example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end end
``` ```
You need to disable --dry-run option for Rspec > 3 You need to disable --dry-run option for Rspec > 3
<!-- This is now enabled by default in rswag. <!-- This is now enabled by default in rswag.
You need to set the ``` config.swagger_dry_run = false``` value in the spec/spec_helper.rb file. You need to set the ``` config.swagger_dry_run = false``` value in the spec/spec_helper.rb file.
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. 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. These integration tests are usually written with ```let``` variables for post body parameters, and since its an integration test the service is returning actual values.
We might as well re-use these values and embed them into the generated swagger to provide a more real world example for request/response examples. --> We might as well re-use these values and embed them into the generated swagger to provide a more real world example for request/response examples. -->
Add to config/environments/test.rb: Add to config/environments/test.rb:
@ -656,8 +660,8 @@ describe 'Blogs API', document: false do
``` ```
##### rswag helper methods ##### ##### rswag helper methods #####
<!-- <!--
There are some helper methods to help with documenting request bodies. There are some helper methods to help with documenting request bodies.
```ruby ```ruby
describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
let(:api_key) { 'fake_key' } let(:api_key) { 'fake_key' }
@ -693,7 +697,7 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
end end
end end
end end
end end
``` ```
In the above example, we see methods ```request_body_json``` ```request_body_plain``` ```request_body_xml```. In the above example, we see methods ```request_body_json``` ```request_body_plain``` ```request_body_xml```.
@ -703,7 +707,7 @@ and the examples: :blog which will create a named example "blog" under the "requ
Again, documenting request response examples changed in Open API 3.0. The example above would generate a swagger.json snippet that looks like this: Again, documenting request response examples changed in Open API 3.0. The example above would generate a swagger.json snippet that looks like this:
```json ```json
... ...
{"requestBody": { {"requestBody": {
"required": true, "required": true,
"content": { "content": {
@ -737,9 +741,9 @@ Again, documenting request response examples changed in Open API 3.0. The exampl
} }
``` ```
*NOTE:* for this example request body to work in the tests properly, you need to ``let`` a variable named *blog*. *NOTE:* for this example request body to work in the tests properly, you need to ``let`` a variable named *blog*.
The variable with the matching name (blog in this case) is eval-ed and captured to be placed in the examples section. The variable with the matching name (blog in this case) is eval-ed and captured to be placed in the examples section.
This ```let``` value is used in the integration test to run the test AND captured and injected into the requestBody section. This ```let``` value is used in the integration test to run the test AND captured and injected into the requestBody section.
##### rswag response examples ##### ##### rswag response examples #####
@ -837,7 +841,7 @@ You can specify custom headers for serving your generated Swagger JSON. For exam
```ruby ```ruby
Rswag::Api.configure do |c| Rswag::Api.configure do |c|
... ...
c.swagger_headers = { 'Content-Type' => 'application/json; charset=UTF-8' } c.swagger_headers = { 'Content-Type' => 'application/json; charset=UTF-8' }
end end
``` ```
@ -909,5 +913,5 @@ docker pull swaggerapi/swagger-editor
``` ```
docker run -d -p 80:8080 swaggerapi/swagger-editor docker run -d -p 80:8080 swaggerapi/swagger-editor
``` ```
This will run the swagger editor in the docker daemon and can be accessed This will run the swagger editor in the docker daemon and can be accessed
at ```http://localhost```. From here, you can use the UI to load the generated swagger.json to validate the output. at ```http://localhost```. From here, you can use the UI to load the generated swagger.json to validate the output.

View File

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

View File

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

View File

@ -118,7 +118,8 @@ module Rswag
def build_query_string_part(param, value) def build_query_string_part(param, value)
name = param[:name] name = param[:name]
return "#{name}=#{value}" unless param[:type]&.to_sym == :array type = param[:type] || param.dig(:schema, :type)
return "#{name}=#{value}" unless type&.to_sym == :array
case param[:collectionFormat] case param[:collectionFormat]
when :ssv when :ssv

View File

@ -59,6 +59,7 @@ module Rswag
mime_list = value.dig(:consumes) || doc[:consumes] mime_list = value.dig(:consumes) || doc[:consumes]
if value && schema_param && mime_list if value && schema_param && mime_list
value[:requestBody] = { content: {} } unless value.dig(:requestBody, :content) value[:requestBody] = { content: {} } unless value.dig(:requestBody, :content)
value[:requestBody][:required] = true if schema_param[:required]
mime_list.each do |mime| mime_list.each do |mime|
value[:requestBody][:content][mime] = { schema: schema_param[:schema] } value[:requestBody][:content][mime] = { schema: schema_param[:schema] }
end end
@ -129,14 +130,14 @@ module Rswag
end end
def upgrade_content!(mime_list, target_node) def upgrade_content!(mime_list, target_node)
target_node.merge!(content: {}) if target_node[:content].nil?
schema = target_node[:schema] schema = target_node[:schema]
return if mime_list.empty? || schema.nil? return if mime_list.empty? || schema.nil?
target_node[:content] ||= {}
target_node.merge!(content: {})
mime_list.each do |mime_type| mime_list.each do |mime_type|
# TODO upgrade to have content-type specific schema # TODO upgrade to have content-type specific schema
target_node[:content][mime_type] = {} if target_node[:content][mime_type].nil? (target_node[:content][mime_type] ||= {}).merge!(schema: schema)
target_node[:content][mime_type][:schema] = schema
end end
end end

View File

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

View File

@ -129,6 +129,56 @@ module Rswag
) )
end end
context 'with empty content' do
let(:swagger_doc) do
{
openapi: '3.0.1',
basePath: '/foo',
schemes: ['http', 'https'],
host: 'api.example.com',
components: {
securitySchemes: {
myClientCredentials: {
type: :oauth2,
flow: :application,
token_url: :somewhere
},
myAuthorizationCode: {
type: :oauth2,
flow: :accessCode,
token_url: :somewhere
},
myImplicit: {
type: :oauth2,
flow: :implicit,
token_url: :somewhere
}
}
}
}
end
it 'converts query and path params, type: to schema: { type: }' do
expect(swagger_doc.slice(:paths)).to match(
paths: {
'/blogs' => {
parameters: [{ schema: { type: :string } }],
post: {
parameters: [{ schema: { type: :string } }],
summary: 'Creates a blog',
responses: {
'201' => {
description: 'blog created',
headers: { schema: { type: :string } }
}
}
}
}
}
)
end
end
it 'converts basePath, schemas and host to urls' do it 'converts basePath, schemas and host to urls' do
expect(swagger_doc.slice(:servers)).to match( expect(swagger_doc.slice(:servers)).to match(
servers: { servers: {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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