Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ This piece of middleware validates the contents of the response received from up
|raise| false | false | Raise an exception on error instead of responding with a generic error body. |
|validate_success_only| true | false | Also validate non-2xx responses only. |
|ignore_error| false | false | Validate and ignore result even if validation is error. So always return original data. |
|parse_response_by_content_type| true | true | Parse response body to JSON only if Content-Type header is 'application/json'. When false, this always optimistically parses as JSON without checking for Content-Type header. |
|parse_response_by_content_type| true | true | Parse response body to JSON only if Content-Type header is `application/json` or an `application/*+json` media type such as `application/problem+json`. When false, this always optimistically parses as JSON without checking for Content-Type header. |
|strict| false | false | Puts the middleware into strict mode, meaning that response code and content type does not defined in the schema will be responded to with a 500 instead of application's status code. |
|strict_reference_validation| always false | false | Raises an exception (`OpenAPIParser::MissingReferenceError`) on middleware load if the provided schema file contains unresolvable references (`$ref:"#/something/not/here"`). Not supported on Hyper-schema parser. Defaults to `false` on OpenAPI3 but will default to `true` in next major version. |

Expand Down
7 changes: 7 additions & 0 deletions lib/committee/schema_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@

module Committee
module SchemaValidator
JSON_MEDIA_TYPE_PATTERN = %r{\Aapplication/(?:.+\+)?json\z}.freeze

class << self
def request_media_type(request)
Rack::MediaType.type(request.env['CONTENT_TYPE'])
end

def json_media_type?(content_type)
normalized_content_type = Rack::MediaType.type(content_type)
normalized_content_type&.match?(JSON_MEDIA_TYPE_PATTERN) || false
end

# @param [String] prefix
# @return [Regexp]
def build_prefix_regexp(prefix)
Expand Down
2 changes: 1 addition & 1 deletion lib/committee/schema_validator/hyper_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def response_validate(status, headers, response, _test_method = false, custom_bo
elsif !full_body.empty?
parse_to_json = if validator_option.parse_response_by_content_type
content_type_key = headers.keys.detect { |k| k.casecmp?('Content-Type') }
headers.fetch(content_type_key, nil)&.start_with?('application/json')
Committee::SchemaValidator.json_media_type?(headers.fetch(content_type_key, nil))
else
true
end
Expand Down
2 changes: 1 addition & 1 deletion lib/committee/schema_validator/open_api_3.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def response_validate(status, headers, response, test_method = false, custom_bod

parse_to_json = if validator_option.parse_response_by_content_type
content_type_key = headers.keys.detect { |k| k.casecmp?('Content-Type') }
headers.fetch(content_type_key, nil)&.start_with?('application/json')
Committee::SchemaValidator.json_media_type?(headers.fetch(content_type_key, nil))
else
true
end
Expand Down
7 changes: 7 additions & 0 deletions test/middleware/response_validation_open_api_3_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ def app
assert_equal 200, last_response.status
end

it "passes through a valid response with a +json content-type" do
@app = new_response_rack(JSON.generate(CHARACTERS_RESPONSE), { "Content-Type" => "application/problem+json; charset=utf-8" }, schema: open_api_3_schema, parse_response_by_content_type: true,)

get "/characters"
assert_equal 200, last_response.status
end

it "passes through a invalid json" do
@app = new_response_rack("not_json", {}, schema: open_api_3_schema)

Expand Down
33 changes: 33 additions & 0 deletions test/schema_validator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,39 @@
assert_equal 'multipart/form-data', media_type
end

it "detects application/json as a JSON media type" do
assert_equal true, Committee::SchemaValidator.json_media_type?("application/json")
end

it "detects application/problem+json with parameters as a JSON media type" do
assert_equal true, Committee::SchemaValidator.json_media_type?("application/problem+json; charset=utf-8")
end

it "detects application/vnd.api+json as a JSON media type" do
assert_equal true, Committee::SchemaValidator.json_media_type?("application/vnd.api+json")
end

it "does not detect application/x-ndjson as a JSON media type" do
assert_equal false, Committee::SchemaValidator.json_media_type?("application/x-ndjson")
end

it "does not detect non-JSON content types as JSON media types" do
assert_equal false, Committee::SchemaValidator.json_media_type?("test/csv")
assert_equal false, Committee::SchemaValidator.json_media_type?(nil)
end

it "parses +json responses in the HyperSchema validator" do
schema = Committee::Drivers::OpenAPI2::Driver.new.parse(open_api_2_data)
validator_option = Committee::SchemaValidator::Option.new({ parse_response_by_content_type: true }, schema, :hyper_schema)
router = Committee::SchemaValidator::HyperSchema::Router.new(schema, validator_option)
request = Rack::Request.new({ "REQUEST_METHOD" => "GET", "PATH_INFO" => "/api/pets", "rack.input" => StringIO.new("") })
validator = Committee::SchemaValidator::HyperSchema.new(router, request, validator_option)

validator.link.media_type = "application/vnd.api+json"

validator.response_validate(200, { "Content-Type" => "application/vnd.api+json" }, [JSON.generate([ValidPet])])
end

it "builds prefix regexp with a path segment boundary" do
regexp = Committee::SchemaValidator.build_prefix_regexp("/v1")

Expand Down