From c6fbebc9714744bf10cbf8414c1896441d2f72d9 Mon Sep 17 00:00:00 2001 From: Ryu Sato Date: Sat, 23 Jul 2022 16:31:50 +0900 Subject: [PATCH 1/7] enable to parse request which is not hash --- lib/committee/request_unpacker.rb | 6 ------ test/request_unpacker_test.rb | 7 +++---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/committee/request_unpacker.rb b/lib/committee/request_unpacker.rb index abddd264..c4bff764 100644 --- a/lib/committee/request_unpacker.rb +++ b/lib/committee/request_unpacker.rb @@ -79,12 +79,6 @@ def parse_json(request) request.body.rewind hash = JSON.parse(body) - # We want a hash specifically. '42', 42, and [42] will all be - # decoded properly, but we can't use them here. - if !hash.is_a?(Hash) - raise BadRequest, - "Invalid JSON input. Require object with parameters as keys." - end self.class.indifferent_params(hash) end end diff --git a/test/request_unpacker_test.rb b/test/request_unpacker_test.rb index e51d08a3..44baa805 100644 --- a/test/request_unpacker_test.rb +++ b/test/request_unpacker_test.rb @@ -135,15 +135,14 @@ assert_equal({ "a" => "b" }, unpacker.unpack_query_params(request)) end - it "errors if JSON is not an object" do + it "unpacks if JSON is not an object" do env = { "CONTENT_TYPE" => "application/json", "rack.input" => StringIO.new('[2]'), } request = Rack::Request.new(env) - assert_raises(Committee::BadRequest) do - Committee::RequestUnpacker.new.unpack_request_params(request) - end + unpacker = Committee::RequestUnpacker.new + assert_equal([[2], false], unpacker.unpack_request_params(request)) end it "errors on an unknown Content-Type" do From 74b6f44f144b6d331d78f38e713679d6504ad4e4 Mon Sep 17 00:00:00 2001 From: Ryu Sato Date: Sun, 24 Jul 2022 19:14:49 +0900 Subject: [PATCH 2/7] support cases where the request parameter is not a hash --- lib/committee/schema_validator/hyper_schema.rb | 9 +++++---- .../schema_validator/hyper_schema/parameter_coercer.rb | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/committee/schema_validator/hyper_schema.rb b/lib/committee/schema_validator/hyper_schema.rb index af8bd0b1..c53e01e5 100644 --- a/lib/committee/schema_validator/hyper_schema.rb +++ b/lib/committee/schema_validator/hyper_schema.rb @@ -79,10 +79,11 @@ def request_unpack(request) coerce_form_params(request_param) if validator_option.coerce_form_params && is_form_params request.env[validator_option.request_body_hash_key] = request_param - request.env[validator_option.params_key] = Committee::Utils.indifferent_hash - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(query_param)) - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(request_param)) - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(param_matches_hash)) + raise BadRequest, "Invalid JSON input. Require object with parameters as keys when path parameter exists." if !request_param.is_a?(Hash) && (query_param != {} || param_matches_hash != {}) + + request.env[validator_option.params_key] = Committee::Utils.deep_copy(request_param) || Committee::Utils.indifferent_hash + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(query_param)) if query_param != {} + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(param_matches_hash)) if param_matches_hash != {} end def coerce_form_params(parameter) diff --git a/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb b/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb index 0f552117..60b0014a 100644 --- a/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb +++ b/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb @@ -13,7 +13,7 @@ def initialize(params, schema, options = {}) end def call! - coerce_object!(@params, @schema) + coerce_value!(@params, @schema) end private From 08f78d142f95a92c868b545a9217e95eea63f6aa Mon Sep 17 00:00:00 2001 From: Ryu Sato Date: Sun, 24 Jul 2022 21:46:53 +0900 Subject: [PATCH 3/7] support cases where the request parameter is not a hash --- lib/committee/schema_validator/open_api_3.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/committee/schema_validator/open_api_3.rb b/lib/committee/schema_validator/open_api_3.rb index 494ba7a1..f60932a9 100644 --- a/lib/committee/schema_validator/open_api_3.rb +++ b/lib/committee/schema_validator/open_api_3.rb @@ -92,6 +92,14 @@ def request_unpack(request) request.env[validator_option.path_hash_key] = coerce_path_params query_param = unpacker.unpack_query_params(request) + param_matches_hash = request.env[validator_option.path_hash_key] + + raise BadRequest, "Invalid JSON input. Require object with parameters as keys when path parameter exists." if !request_param.is_a?(Hash) && (query_param != {} || param_matches_hash != {}) + + request.env[validator_option.params_key] = Committee::Utils.deep_copy(request_param) || Committee::Utils.indifferent_hash + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(query_param)) if query_param != {} + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(param_matches_hash)) if param_matches_hash != {} + query_param.merge!(request_param) if request.get? && validator_option.allow_get_body request.env[validator_option.query_hash_key] = query_param end From 311dc8b243f0165700d8849fe0ec9344d19b6a25 Mon Sep 17 00:00:00 2001 From: Ryu Sato Date: Sun, 22 Jan 2023 17:54:39 +0900 Subject: [PATCH 4/7] add test for request with array body --- .../open_api_3/operation_wrapper.rb | 3 +-- test/data/openapi3/normal.yaml | 16 ++++++++++++++++ .../open_api_3/request_validator_test.rb | 8 ++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/committee/schema_validator/open_api_3/operation_wrapper.rb b/lib/committee/schema_validator/open_api_3/operation_wrapper.rb index 376c207a..2daa8bcc 100644 --- a/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +++ b/lib/committee/schema_validator/open_api_3/operation_wrapper.rb @@ -40,7 +40,7 @@ def validate_response_params(status_code, headers, response_data, strict, check_ end def validate_request_params(path_params, query_params, body_params, headers, validator_option) - ret, err = case request_operation.http_method + ret = case request_operation.http_method when 'get', 'delete', 'head' validate_get_request_params(path_params, query_params, headers, validator_option) when 'post', 'put', 'patch', 'options' @@ -48,7 +48,6 @@ def validate_request_params(path_params, query_params, body_params, headers, val else raise "Committee OpenAPI3 not support #{request_operation.http_method} method" end - raise err if err ret end diff --git a/test/data/openapi3/normal.yaml b/test/data/openapi3/normal.yaml index fd535293..b9c7d076 100644 --- a/test/data/openapi3/normal.yaml +++ b/test/data/openapi3/normal.yaml @@ -467,6 +467,22 @@ paths: type: string format: binary + /validate_request_array: + post: + description: validate request with array + responses: + '200': + description: no content + requestBody: + content: + application/json: + schema: + type: array + items: + anyOf: + - type: string + - type: boolean + /validate_no_parameter: patch: description: validate no body diff --git a/test/schema_validator/open_api_3/request_validator_test.rb b/test/schema_validator/open_api_3/request_validator_test.rb index 1df37e40..8db63097 100644 --- a/test/schema_validator/open_api_3/request_validator_test.rb +++ b/test/schema_validator/open_api_3/request_validator_test.rb @@ -95,6 +95,14 @@ def app assert_equal 400, last_response.status end + it "validates array request" do + @app = new_rack_app(check_content_type: true, schema: open_api_3_schema) + params = ["1", true] + header "Content-Type", "application/json" + post "/validate_request_array", JSON.generate(params) + assert_equal 200, last_response.status + end + def new_rack_app(options = {}) # TODO: delete when 5.0.0 released because default value changed options[:parse_response_by_content_type] = true if options[:parse_response_by_content_type] == nil From 77dc3a1798552a21919a9577487dbcba7a24f28d Mon Sep 17 00:00:00 2001 From: Ryu Sato Date: Sat, 28 Jan 2023 18:07:51 +0900 Subject: [PATCH 5/7] enable to set an array to the request --- lib/committee/schema_validator/hyper_schema.rb | 15 ++++++++++----- .../hyper_schema/parameter_coercer.rb | 2 +- lib/committee/schema_validator/open_api_3.rb | 17 +++++++++++------ test/data/hyperschema/paas.json | 13 +++++++++++++ test/middleware/request_validation_test.rb | 12 +++++++++++- 5 files changed, 46 insertions(+), 13 deletions(-) diff --git a/lib/committee/schema_validator/hyper_schema.rb b/lib/committee/schema_validator/hyper_schema.rb index c53e01e5..2295e618 100644 --- a/lib/committee/schema_validator/hyper_schema.rb +++ b/lib/committee/schema_validator/hyper_schema.rb @@ -79,11 +79,16 @@ def request_unpack(request) coerce_form_params(request_param) if validator_option.coerce_form_params && is_form_params request.env[validator_option.request_body_hash_key] = request_param - raise BadRequest, "Invalid JSON input. Require object with parameters as keys when path parameter exists." if !request_param.is_a?(Hash) && (query_param != {} || param_matches_hash != {}) - - request.env[validator_option.params_key] = Committee::Utils.deep_copy(request_param) || Committee::Utils.indifferent_hash - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(query_param)) if query_param != {} - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(param_matches_hash)) if param_matches_hash != {} + if request_param.is_a?(Array) + raise BadRequest, "Invalid JSON input. Require object with parameters as keys when path parameter exists." if !query_param.empty? || !param_matches_hash.empty? + + request.env[validator_option.params_key] = Committee::Utils.deep_copy(request_param) + else + request.env[validator_option.params_key] = Committee::Utils.indifferent_hash + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(query_param)) + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(request_param)) + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(param_matches_hash)) + end end def coerce_form_params(parameter) diff --git a/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb b/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb index 60b0014a..2c2da599 100644 --- a/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb +++ b/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb @@ -13,7 +13,7 @@ def initialize(params, schema, options = {}) end def call! - coerce_value!(@params, @schema) + @params.is_a?(Array) ? coerce_array_data!(@params, @schema) : coerce_object!(@params, @schema) end private diff --git a/lib/committee/schema_validator/open_api_3.rb b/lib/committee/schema_validator/open_api_3.rb index f60932a9..2bbef79e 100644 --- a/lib/committee/schema_validator/open_api_3.rb +++ b/lib/committee/schema_validator/open_api_3.rb @@ -94,14 +94,19 @@ def request_unpack(request) query_param = unpacker.unpack_query_params(request) param_matches_hash = request.env[validator_option.path_hash_key] - raise BadRequest, "Invalid JSON input. Require object with parameters as keys when path parameter exists." if !request_param.is_a?(Hash) && (query_param != {} || param_matches_hash != {}) + if request_param.is_a?(Array) + raise BadRequest, "Invalid JSON input. Require object with parameters as keys when path parameter exists." if !query_param.empty? || !param_matches_hash.empty? - request.env[validator_option.params_key] = Committee::Utils.deep_copy(request_param) || Committee::Utils.indifferent_hash - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(query_param)) if query_param != {} - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(param_matches_hash)) if param_matches_hash != {} + request.env[validator_option.params_key] = Committee::Utils.deep_copy(request_param) + else + request.env[validator_option.params_key] = Committee::Utils.indifferent_hash + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(query_param)) + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(request_param)) + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(param_matches_hash)) - query_param.merge!(request_param) if request.get? && validator_option.allow_get_body - request.env[validator_option.query_hash_key] = query_param + query_param.merge!(request_param) if request.get? && validator_option.allow_get_body + request.env[validator_option.query_hash_key] = query_param + end end def copy_coerced_data_to_params(request) diff --git a/test/data/hyperschema/paas.json b/test/data/hyperschema/paas.json index 6c65d065..d8ec60e4 100644 --- a/test/data/hyperschema/paas.json +++ b/test/data/hyperschema/paas.json @@ -262,6 +262,19 @@ "$ref": "#/definitions/app" }, "title": "abc" + }, + { + "description": "Create new apps.", + "href": "/apps", + "method": "PUT", + "rel": "collection", + "targetSchema": { + "items": { + "$ref": "#/definitions/app" + }, + "type": "array" + }, + "title": "Create Apps" } ], "properties": { diff --git a/test/middleware/request_validation_test.rb b/test/middleware/request_validation_test.rb index 930647be..a19c7b25 100644 --- a/test/middleware/request_validation_test.rb +++ b/test/middleware/request_validation_test.rb @@ -53,7 +53,7 @@ def app } ] - it "passes through a valid request" do + it "passes through a valid request typed by object" do @app = new_rack_app(schema: hyper_schema) params = { "name" => "cloudnasium" @@ -63,6 +63,16 @@ def app assert_equal 200, last_response.status end + it "passes through a valid request typed by array" do + @app = new_rack_app(schema: hyper_schema) + params = [{ + "name" => "cloudnasium" + }] + header "Content-Type", "application/json" + put "/apps", JSON.generate(params) + assert_equal 200, last_response.status + end + it "doesn't call error_handler (has a arg) when request is valid" do called_error = false pr = ->(_e) { called_error = true } From d1b7891935df4f4d3e9a6f097af3ac63cb8f5116 Mon Sep 17 00:00:00 2001 From: Ryu Sato Date: Sat, 28 Jan 2023 19:25:33 +0900 Subject: [PATCH 6/7] enable to set an array to the request --- lib/committee/schema_validator/open_api_3.rb | 31 ++++++++------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/lib/committee/schema_validator/open_api_3.rb b/lib/committee/schema_validator/open_api_3.rb index 2bbef79e..6af4fd89 100644 --- a/lib/committee/schema_validator/open_api_3.rb +++ b/lib/committee/schema_validator/open_api_3.rb @@ -92,21 +92,8 @@ def request_unpack(request) request.env[validator_option.path_hash_key] = coerce_path_params query_param = unpacker.unpack_query_params(request) - param_matches_hash = request.env[validator_option.path_hash_key] - - if request_param.is_a?(Array) - raise BadRequest, "Invalid JSON input. Require object with parameters as keys when path parameter exists." if !query_param.empty? || !param_matches_hash.empty? - - request.env[validator_option.params_key] = Committee::Utils.deep_copy(request_param) - else - request.env[validator_option.params_key] = Committee::Utils.indifferent_hash - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(query_param)) - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(request_param)) - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(param_matches_hash)) - - query_param.merge!(request_param) if request.get? && validator_option.allow_get_body - request.env[validator_option.query_hash_key] = query_param - end + query_param.merge!(request_param) if request.get? && validator_option.allow_get_body + request.env[validator_option.query_hash_key] = query_param end def copy_coerced_data_to_params(request) @@ -118,9 +105,17 @@ def copy_coerced_data_to_params(request) [validator_option.query_hash_key, validator_option.request_body_hash_key, validator_option.path_hash_key] end - request.env[validator_option.params_key] = Committee::Utils.indifferent_hash - order.each do |key| - request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(request.env[key])) + if request.env[validator_option.request_body_hash_key].is_a?(Array) + if (!request.env[validator_option.query_hash_key].empty? || !request.env[validator_option.path_hash_key].empty?) + raise BadRequest, "Invalid JSON input. Require object with parameters as keys when path parameter exists." + end + + request.env[validator_option.params_key] = Committee::Utils.deep_copy(request.env[validator_option.request_body_hash_key]) + else + request.env[validator_option.params_key] = Committee::Utils.indifferent_hash + order.each do |key| + request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(request.env[key])) + end end end end From eb536eabd98700adb2cb838eea1343ddb2ea341b Mon Sep 17 00:00:00 2001 From: Ryu Sato Date: Sat, 28 Jan 2023 19:40:46 +0900 Subject: [PATCH 7/7] fix error messages --- lib/committee/schema_validator/hyper_schema.rb | 2 +- lib/committee/schema_validator/open_api_3.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/committee/schema_validator/hyper_schema.rb b/lib/committee/schema_validator/hyper_schema.rb index 2295e618..0eb6ccec 100644 --- a/lib/committee/schema_validator/hyper_schema.rb +++ b/lib/committee/schema_validator/hyper_schema.rb @@ -80,7 +80,7 @@ def request_unpack(request) request.env[validator_option.request_body_hash_key] = request_param if request_param.is_a?(Array) - raise BadRequest, "Invalid JSON input. Require object with parameters as keys when path parameter exists." if !query_param.empty? || !param_matches_hash.empty? + raise BadRequest, "Invalid JSON input. Require request body typed by object when path/query parameter exists." if !query_param.empty? || !param_matches_hash.empty? request.env[validator_option.params_key] = Committee::Utils.deep_copy(request_param) else diff --git a/lib/committee/schema_validator/open_api_3.rb b/lib/committee/schema_validator/open_api_3.rb index 6af4fd89..a87f37d0 100644 --- a/lib/committee/schema_validator/open_api_3.rb +++ b/lib/committee/schema_validator/open_api_3.rb @@ -107,7 +107,7 @@ def copy_coerced_data_to_params(request) if request.env[validator_option.request_body_hash_key].is_a?(Array) if (!request.env[validator_option.query_hash_key].empty? || !request.env[validator_option.path_hash_key].empty?) - raise BadRequest, "Invalid JSON input. Require object with parameters as keys when path parameter exists." + raise BadRequest, "Invalid JSON input. Require request body typed by object when path/query parameter exists." end request.env[validator_option.params_key] = Committee::Utils.deep_copy(request.env[validator_option.request_body_hash_key])