Initial commit for trying to produce and consume v3 swagger

This commit is contained in:
Jay Danielian 2019-06-29 18:12:21 -04:00
parent 10bb732148
commit 768a1a1d43
10 changed files with 130 additions and 91 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@
**/*/node_modules **/*/node_modules
*.swp *.swp
Gemfile.lock Gemfile.lock
/.idea/

View File

@ -1 +1 @@
2.3.1 2.5.1

View File

@ -13,7 +13,7 @@ when '4', '5'
gem 'responders' gem 'responders'
end end
gem 'sqlite3' gem 'sqlite3', '~> 1.3.6'
gem 'rswag-api', path: './rswag-api' gem 'rswag-api', path: './rswag-api'
gem 'rswag-ui', path: './rswag-ui' gem 'rswag-ui', path: './rswag-ui'

View File

@ -35,6 +35,14 @@ module Rswag
end end
end end
#TODO: look at adding request_body method to handle diffs in Open API 2.0 to 3.0
# https://swagger.io/docs/specification/describing-request-body/
# need to make sure we output requestBody in the swagger generator .json
# also need to make sure that it can handle content: , required: true/false, schema: ref
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

View File

@ -15,7 +15,7 @@ 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={})
return if data.nil? && current_schema.schema['x-nullable'] == true return if data.nil? && (current_schema.schema['nullable'] == true || current_schema.schema['x-nullable'] == true)
super super
end end
end end

View File

@ -39,7 +39,7 @@ 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 }
schemes = (swagger_doc[:securityDefinitions] || {}).slice(*scheme_names).values schemes = (swagger_doc[:components][:securitySchemes] || {}).slice(*scheme_names).values
schemes.map do |scheme| schemes.map do |scheme|
param = (scheme[:type] == :apiKey) ? scheme.slice(:name, :in) : { name: 'Authorization', in: :header } param = (scheme[:type] == :apiKey) ? scheme.slice(:name, :in) : { name: 'Authorization', in: :header }

View File

@ -41,9 +41,12 @@ module Rswag
response_schema = metadata[:response][:schema] response_schema = metadata[:response][:schema]
return if response_schema.nil? return if response_schema.nil?
components_schemas = {components: {schemas: swagger_doc[:components][:schemas]}}
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(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

View File

@ -10,7 +10,7 @@ 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' } parameter name: :blog, in: :body, schema: { '$ref' => '#/components/schemas/blog' }
let(:blog) { { title: 'foo', content: 'bar' } } let(:blog) { { title: 'foo', content: 'bar' } }
@ -19,7 +19,7 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
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) { { title: 'foo' } } let(:blog) { { title: 'foo' } }
run_test! do |response| run_test! do |response|
@ -38,7 +38,7 @@ 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' }
end end
response '406', 'unsupported accept header' do response '406', 'unsupported accept header' do
@ -65,7 +65,7 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
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,

View File

@ -14,17 +14,29 @@ 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: [
{
url: "https://{defaultHost}",
variables: {
defaultHost: {
default: "www.example.com"
}
}
}
],
components: {
schemas: {
errors_object: { errors_object: {
type: 'object', type: 'object',
properties: { properties: {
errors: { '$ref' => '#/definitions/errors_map' } errors: { '$ref' => '#/components/schemas/errors_map' }
} }
}, },
errors_map: { errors_map: {
@ -39,15 +51,16 @@ RSpec.configure do |config|
properties: { properties: {
id: { type: 'integer' }, id: { type: 'integer' },
title: { type: 'string' }, title: { type: 'string' },
content: { type: 'string', 'x-nullable': true }, content: { type: 'string', nullable: true },
thumbnail: { type: 'string'} thumbnail: { type: 'string'}
}, },
required: [ 'id', 'title', 'content', 'thumbnail' ] required: [ 'id', 'title', 'content', 'thumbnail' ]
} }
}, },
securityDefinitions: { securitySchemes: {
basic_auth: { basic_auth: {
type: :basic type: :http,
scheme: :basic
}, },
api_key: { api_key: {
type: :apiKey, type: :apiKey,
@ -55,6 +68,7 @@ RSpec.configure do |config|
in: :query in: :query
} }
} }
},
} }
} }
end end

View File

@ -1,5 +1,5 @@
{ {
"swagger": "2.0", "openapi": "3.0.0",
"info": { "info": {
"title": "API V1", "title": "API V1",
"version": "v1" "version": "v1"
@ -99,7 +99,7 @@
"name": "blog", "name": "blog",
"in": "body", "in": "body",
"schema": { "schema": {
"$ref": "#/definitions/blog" "$ref": "#/components/schemas/blog"
} }
} }
], ],
@ -110,7 +110,7 @@
"422": { "422": {
"description": "invalid request", "description": "invalid request",
"schema": { "schema": {
"$ref": "#/definitions/errors_object" "$ref": "#/components/schemas/errors_object"
} }
} }
} }
@ -173,7 +173,7 @@
} }
}, },
"schema": { "schema": {
"$ref": "#/definitions/blog" "$ref": "#/components/schemas/blog"
}, },
"examples": { "examples": {
"application/json": { "application/json": {
@ -225,12 +225,23 @@
} }
} }
}, },
"definitions": { "servers": [
{
"url": "https://{defaultHost}",
"variables": {
"defaultHost": {
"default": "www.example.com"
}
}
}
],
"components": {
"schemas": {
"errors_object": { "errors_object": {
"type": "object", "type": "object",
"properties": { "properties": {
"errors": { "errors": {
"$ref": "#/definitions/errors_map" "$ref": "#/components/schemas/errors_map"
} }
} }
}, },
@ -254,7 +265,7 @@
}, },
"content": { "content": {
"type": "string", "type": "string",
"x-nullable": true "nullable": true
}, },
"thumbnail": { "thumbnail": {
"type": "string" "type": "string"
@ -268,9 +279,10 @@
] ]
} }
}, },
"securityDefinitions": { "securitySchemes": {
"basic_auth": { "basic_auth": {
"type": "basic" "type": "http",
"scheme": "basic"
}, },
"api_key": { "api_key": {
"type": "apiKey", "type": "apiKey",
@ -278,4 +290,5 @@
"in": "query" "in": "query"
} }
} }
}
} }