From 4c2097e017e81c78f56a611b46709fdfd3e4bc4a Mon Sep 17 00:00:00 2001 From: Jay Danielian Date: Sat, 20 Jul 2019 14:33:51 -0400 Subject: [PATCH] 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 --- .../lib/rswag/specs/example_group_helpers.rb | 22 ++++++++++++++++++- .../lib/rswag/specs/response_validator.rb | 16 +++++++++----- test-app/app/controllers/blogs_controller.rb | 1 - test-app/spec/integration/blogs_spec.rb | 4 ++-- test-app/spec/swagger_helper.rb | 8 +++---- test-app/swagger/v1/swagger.json | 22 ++++++++++++------- 6 files changed, 52 insertions(+), 21 deletions(-) diff --git a/rswag-specs/lib/rswag/specs/example_group_helpers.rb b/rswag-specs/lib/rswag/specs/example_group_helpers.rb index 3942018..9eaa7ba 100644 --- a/rswag-specs/lib/rswag/specs/example_group_helpers.rb +++ b/rswag-specs/lib/rswag/specs/example_group_helpers.rb @@ -55,7 +55,15 @@ module Rswag def request_body(attributes) # can make this generic, and accept any incoming hash (like parameter method) 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 def request_body_json(schema:, required: true, description: nil, examples: nil) @@ -77,6 +85,18 @@ module Rswag 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) diff --git a/rswag-specs/lib/rswag/specs/response_validator.rb b/rswag-specs/lib/rswag/specs/response_validator.rb index 4f14829..38d4dbf 100644 --- a/rswag-specs/lib/rswag/specs/response_validator.rb +++ b/rswag-specs/lib/rswag/specs/response_validator.rb @@ -37,21 +37,27 @@ module Rswag raise UnexpectedResponse, "Expected response header #{name} to be present" if headers[name.to_s].nil? end end - + def validate_body!(metadata, swagger_doc, body) - response_schema = metadata[:response][:schema] - return if response_schema.nil? + test_schemas = extract_schemas(metadata) + return if test_schemas.nil? components = swagger_doc[:components] || {} 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(components_schemas) - errors = JSON::Validator.fully_validate(validation_schema, body) raise UnexpectedResponse, "Expected response body to match schema: #{errors[0]}" if errors.any? 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 class UnexpectedResponse < StandardError; end diff --git a/test-app/app/controllers/blogs_controller.rb b/test-app/app/controllers/blogs_controller.rb index bf922d7..2719750 100644 --- a/test-app/app/controllers/blogs_controller.rb +++ b/test-app/app/controllers/blogs_controller.rb @@ -15,7 +15,6 @@ class BlogsController < ApplicationController # 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 diff --git a/test-app/spec/integration/blogs_spec.rb b/test-app/spec/integration/blogs_spec.rb index ded2353..a3ec90c 100644 --- a/test-app/spec/integration/blogs_spec.rb +++ b/test-app/spec/integration/blogs_spec.rb @@ -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' } } } response '201', 'flexible blog created' do - schema '$ref' => '#/components/schemas/blog' + schema :oneOf => [{'$ref' => '#/components/schemas/blog'},{'$ref' => '#/components/schemas/flexible_blog'}] run_test! end end @@ -120,7 +120,7 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do end end - + path '/blogs/{id}/upload' do let(:id) { blog.id } let(:blog) { Blog.create(title: 'foo', content: 'bar') } diff --git a/test-app/spec/swagger_helper.rb b/test-app/spec/swagger_helper.rb index 9a864ef..4c1e7b8 100644 --- a/test-app/spec/swagger_helper.rb +++ b/test-app/spec/swagger_helper.rb @@ -54,9 +54,9 @@ RSpec.configure do |config| id: { type: 'integer' }, title: { type: 'string' }, 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: { type: 'object', @@ -64,9 +64,9 @@ RSpec.configure do |config| id: { type: 'integer' }, headline: { type: 'string' }, text: { type: 'string', nullable: true }, - thumbnail: { type: 'string' } + thumbnail: { type: 'string', nullable:true } }, - required: %w[id headline thumbnail] + required: %w[id headline] } }, securitySchemes: { diff --git a/test-app/swagger/v1/swagger.json b/test-app/swagger/v1/swagger.json index a2af55e..be26582 100644 --- a/test-app/swagger/v1/swagger.json +++ b/test-app/swagger/v1/swagger.json @@ -243,7 +243,14 @@ "thumbnail": null }, "schema": { - "$ref": "#/components/schemas/blog" + "oneOf": [ + { + "$ref": "#/components/schemas/blog" + }, + { + "$ref": "#/components/schemas/flexible_blog" + } + ] } } } @@ -395,14 +402,13 @@ "nullable": true }, "thumbnail": { - "type": "string" + "type": "string", + "nullable": true } }, "required": [ "id", - "title", - "content", - "thumbnail" + "title" ] }, "flexible_blog": { @@ -419,13 +425,13 @@ "nullable": true }, "thumbnail": { - "type": "string" + "type": "string", + "nullable": true } }, "required": [ "id", - "headline", - "thumbnail" + "headline" ] } },