mirror of
https://github.com/ditkrg/rswag.git
synced 2026-01-25 07:16:40 +00:00
Fixes response_validator to handle 3.0 responses and validate against the schema.
JSON::Validator already handles anyOf oneOf schema definitions, so those can be passed in and validation errors are returned properly
This commit is contained in:
@@ -55,7 +55,15 @@ module Rswag
|
|||||||
def request_body(attributes)
|
def request_body(attributes)
|
||||||
# can make this generic, and accept any incoming hash (like parameter method)
|
# can make this generic, and accept any incoming hash (like parameter method)
|
||||||
attributes.compact!
|
attributes.compact!
|
||||||
metadata[:operation][:requestBody] = attributes
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
def request_body_json(schema:, required: true, description: nil, examples: nil)
|
def request_body_json(schema:, required: true, description: nil, examples: nil)
|
||||||
@@ -77,6 +85,18 @@ module Rswag
|
|||||||
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)
|
def request_body_multipart(schema:, description: nil)
|
||||||
content_hash = { 'multipart/form-data' => { schema: schema }}
|
content_hash = { 'multipart/form-data' => { schema: schema }}
|
||||||
request_body(description: description, content: content_hash)
|
request_body(description: description, content: content_hash)
|
||||||
|
|||||||
@@ -39,19 +39,25 @@ module Rswag
|
|||||||
end
|
end
|
||||||
|
|
||||||
def validate_body!(metadata, swagger_doc, body)
|
def validate_body!(metadata, swagger_doc, body)
|
||||||
response_schema = metadata[:response][:schema]
|
test_schemas = extract_schemas(metadata)
|
||||||
return if response_schema.nil?
|
return if test_schemas.nil?
|
||||||
|
|
||||||
components = swagger_doc[:components] || {}
|
components = swagger_doc[:components] || {}
|
||||||
components_schemas = { components: { schemas: components[:schemas] } }
|
components_schemas = { components: { schemas: components[:schemas] } }
|
||||||
|
|
||||||
validation_schema = response_schema
|
validation_schema = test_schemas[:schema] # response_schema
|
||||||
.merge('$schema' => 'http://tempuri.org/rswag/specs/extended_schema')
|
.merge('$schema' => 'http://tempuri.org/rswag/specs/extended_schema')
|
||||||
.merge(components_schemas)
|
.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
|
||||||
|
|
||||||
|
def extract_schemas(metadata)
|
||||||
|
produces = Array(metadata[:operation][:produces])
|
||||||
|
response_content = metadata[:response][:content] || {}
|
||||||
|
producer_content = produces.first || 'application/json'
|
||||||
|
response_content[producer_content]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class UnexpectedResponse < StandardError; end
|
class UnexpectedResponse < StandardError; end
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ class BlogsController < ApplicationController
|
|||||||
# request body definition for 3.0
|
# request body definition for 3.0
|
||||||
blog_params = params.require(:blog).permit(:title, :content, :headline, :text)
|
blog_params = params.require(:blog).permit(:title, :content, :headline, :text)
|
||||||
|
|
||||||
|
|
||||||
@blog = Blog.create(blog_params)
|
@blog = Blog.create(blog_params)
|
||||||
respond_with @blog
|
respond_with @blog
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
|
|||||||
let(:flexible_blog) { { blog: { headline: 'my headline', text: 'my text' } } }
|
let(:flexible_blog) { { blog: { headline: 'my headline', text: 'my text' } } }
|
||||||
|
|
||||||
response '201', 'flexible blog created' do
|
response '201', 'flexible blog created' do
|
||||||
schema '$ref' => '#/components/schemas/blog'
|
schema :oneOf => [{'$ref' => '#/components/schemas/blog'},{'$ref' => '#/components/schemas/flexible_blog'}]
|
||||||
run_test!
|
run_test!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ RSpec.configure do |config|
|
|||||||
id: { type: 'integer' },
|
id: { type: 'integer' },
|
||||||
title: { type: 'string' },
|
title: { type: 'string' },
|
||||||
content: { type: 'string', nullable: true },
|
content: { type: 'string', nullable: true },
|
||||||
thumbnail: { type: 'string' }
|
thumbnail: { type: 'string', nullable: true }
|
||||||
},
|
},
|
||||||
required: %w[id title content thumbnail]
|
required: %w[id title]
|
||||||
},
|
},
|
||||||
flexible_blog: {
|
flexible_blog: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@@ -64,9 +64,9 @@ RSpec.configure do |config|
|
|||||||
id: { type: 'integer' },
|
id: { type: 'integer' },
|
||||||
headline: { type: 'string' },
|
headline: { type: 'string' },
|
||||||
text: { type: 'string', nullable: true },
|
text: { type: 'string', nullable: true },
|
||||||
thumbnail: { type: 'string' }
|
thumbnail: { type: 'string', nullable:true }
|
||||||
},
|
},
|
||||||
required: %w[id headline thumbnail]
|
required: %w[id headline]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
securitySchemes: {
|
securitySchemes: {
|
||||||
|
|||||||
@@ -243,7 +243,14 @@
|
|||||||
"thumbnail": null
|
"thumbnail": null
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/components/schemas/blog"
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/blog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/flexible_blog"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,14 +402,13 @@
|
|||||||
"nullable": true
|
"nullable": true
|
||||||
},
|
},
|
||||||
"thumbnail": {
|
"thumbnail": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"id",
|
"id",
|
||||||
"title",
|
"title"
|
||||||
"content",
|
|
||||||
"thumbnail"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"flexible_blog": {
|
"flexible_blog": {
|
||||||
@@ -419,13 +425,13 @@
|
|||||||
"nullable": true
|
"nullable": true
|
||||||
},
|
},
|
||||||
"thumbnail": {
|
"thumbnail": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"nullable": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"id",
|
"id",
|
||||||
"headline",
|
"headline"
|
||||||
"thumbnail"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user