committee 3.3.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/lib/committee/drivers/open_api_2/driver.rb +1 -2
 - data/lib/committee/drivers/open_api_2/parameter_schema_builder.rb +1 -1
 - data/lib/committee/drivers.rb +22 -10
 - data/lib/committee/errors.rb +12 -0
 - data/lib/committee/middleware/base.rb +5 -4
 - data/lib/committee/middleware/request_validation.rb +4 -18
 - data/lib/committee/middleware/response_validation.rb +15 -16
 - data/lib/committee/request_unpacker.rb +46 -60
 - data/lib/committee/schema_validator/hyper_schema/response_validator.rb +8 -2
 - data/lib/committee/schema_validator/hyper_schema.rb +41 -27
 - data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +44 -37
 - data/lib/committee/schema_validator/open_api_3/request_validator.rb +11 -2
 - data/lib/committee/schema_validator/open_api_3/router.rb +3 -1
 - data/lib/committee/schema_validator/open_api_3.rb +52 -26
 - data/lib/committee/schema_validator/option.rb +14 -3
 - data/lib/committee/schema_validator.rb +1 -1
 - data/lib/committee/test/methods.rb +27 -16
 - data/lib/committee/test/schema_coverage.rb +101 -0
 - data/lib/committee/utils.rb +28 -0
 - data/lib/committee/validation_error.rb +3 -2
 - data/lib/committee/version.rb +5 -0
 - data/lib/committee.rb +11 -4
 - data/test/bin/committee_stub_test.rb +5 -1
 - data/test/committee_test.rb +29 -3
 - data/test/drivers/open_api_3/driver_test.rb +1 -1
 - data/test/drivers_test.rb +20 -7
 - data/test/middleware/base_test.rb +9 -10
 - data/test/middleware/request_validation_open_api_3_test.rb +175 -18
 - data/test/middleware/request_validation_test.rb +20 -28
 - data/test/middleware/response_validation_open_api_3_test.rb +96 -7
 - data/test/middleware/response_validation_test.rb +21 -26
 - data/test/middleware/stub_test.rb +4 -0
 - data/test/request_unpacker_test.rb +51 -110
 - data/test/schema_validator/hyper_schema/response_validator_test.rb +10 -0
 - data/test/schema_validator/hyper_schema/router_test.rb +4 -0
 - data/test/schema_validator/hyper_schema/string_params_coercer_test.rb +1 -1
 - data/test/schema_validator/open_api_3/operation_wrapper_test.rb +72 -20
 - data/test/schema_validator/open_api_3/request_validator_test.rb +27 -0
 - data/test/schema_validator/open_api_3/response_validator_test.rb +26 -5
 - data/test/test/methods_new_version_test.rb +17 -5
 - data/test/test/methods_test.rb +155 -31
 - data/test/test/schema_coverage_test.rb +216 -0
 - data/test/test_helper.rb +34 -4
 - metadata +47 -15
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 284c8c1198255435e959dcee2b92cb25c3d80dd5fe6d4622b8077a69525712ed
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: b306ec0ad5e628d9cc7b6382781c4b710c99bedb127a7000edadf9d0e0b8fa84
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 687d9a47d2a17786313bde939339d464a8d775795458b09ad7d7784bfa337f848a9ffabe240e9f39173c4466589c026f64a8eede768e7abfff4b01b2672339f5
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 4ee7781341de9ebafae28d01d98c0f72d54067c425f3ee10b221c4eb88da85e79b36853bbda4818b20348177f8d975103cc5be694ff7e8252a6634a856853b7c
         
     | 
| 
         @@ -59,7 +59,7 @@ module Committee 
     | 
|
| 
       59 
59 
     | 
    
         
             
                      schema.base_path = data['basePath'] || ''
         
     | 
| 
       60 
60 
     | 
    
         | 
| 
       61 
61 
     | 
    
         
             
                      # Arbitrarily choose the first media type found in these arrays. This
         
     | 
| 
       62 
     | 
    
         
            -
                      #  
     | 
| 
      
 62 
     | 
    
         
            +
                      # approach could probably stand to be improved, but at least users will
         
     | 
| 
       63 
63 
     | 
    
         
             
                      # for now have the option of turning media type validation off if they so
         
     | 
| 
       64 
64 
     | 
    
         
             
                      # choose.
         
     | 
| 
       65 
65 
     | 
    
         
             
                      schema.consumes = data['consumes'].first
         
     | 
| 
         @@ -156,7 +156,6 @@ module Committee 
     | 
|
| 
       156 
156 
     | 
    
         | 
| 
       157 
157 
     | 
    
         
             
                        methods.each do |method, link_data|
         
     | 
| 
       158 
158 
     | 
    
         
             
                          method = method.upcase
         
     | 
| 
       159 
     | 
    
         
            -
             
     | 
| 
       160 
159 
     | 
    
         
             
                          link = Link.new
         
     | 
| 
       161 
160 
     | 
    
         
             
                          link.enc_type = schema.consumes
         
     | 
| 
       162 
161 
     | 
    
         
             
                          link.href = href
         
     | 
| 
         @@ -62,7 +62,7 @@ module Committee 
     | 
|
| 
       62 
62 
     | 
    
         
             
                            # And same idea: despite parameters not being schemas, the items
         
     | 
| 
       63 
63 
     | 
    
         
             
                            # key (if preset) is actually a schema that defines each item of an
         
     | 
| 
       64 
64 
     | 
    
         
             
                            # array type, so we can just reflect that directly onto our
         
     | 
| 
       65 
     | 
    
         
            -
                            #  
     | 
| 
      
 65 
     | 
    
         
            +
                            # artificial schema.
         
     | 
| 
       66 
66 
     | 
    
         
             
                            if param_data["type"] == "array" && param_data["items"]
         
     | 
| 
       67 
67 
     | 
    
         
             
                              param_schema.items = param_data["items"]
         
     | 
| 
       68 
68 
     | 
    
         
             
                            end
         
     | 
    
        data/lib/committee/drivers.rb
    CHANGED
    
    | 
         @@ -20,26 +20,27 @@ module Committee 
     | 
|
| 
       20 
20 
     | 
    
         
             
                # load and build drive from JSON file
         
     | 
| 
       21 
21 
     | 
    
         
             
                # @param [String] schema_path
         
     | 
| 
       22 
22 
     | 
    
         
             
                # @return [Committee::Driver]
         
     | 
| 
       23 
     | 
    
         
            -
                def self.load_from_json(schema_path)
         
     | 
| 
       24 
     | 
    
         
            -
                  load_from_data(JSON.parse(File.read(schema_path)))
         
     | 
| 
      
 23 
     | 
    
         
            +
                def self.load_from_json(schema_path, parser_options: {})
         
     | 
| 
      
 24 
     | 
    
         
            +
                  load_from_data(JSON.parse(File.read(schema_path)), schema_path, parser_options: parser_options)
         
     | 
| 
       25 
25 
     | 
    
         
             
                end
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
                # load and build drive from YAML file
         
     | 
| 
       28 
28 
     | 
    
         
             
                # @param [String] schema_path
         
     | 
| 
       29 
29 
     | 
    
         
             
                # @return [Committee::Driver]
         
     | 
| 
       30 
     | 
    
         
            -
                def self.load_from_yaml(schema_path)
         
     | 
| 
       31 
     | 
    
         
            -
                   
     | 
| 
      
 30 
     | 
    
         
            +
                def self.load_from_yaml(schema_path, parser_options: {})
         
     | 
| 
      
 31 
     | 
    
         
            +
                  data = YAML.respond_to?(:unsafe_load_file) ? YAML.unsafe_load_file(schema_path) : YAML.load_file(schema_path)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  load_from_data(data, schema_path, parser_options: parser_options)
         
     | 
| 
       32 
33 
     | 
    
         
             
                end
         
     | 
| 
       33 
34 
     | 
    
         | 
| 
       34 
35 
     | 
    
         
             
                # load and build drive from file
         
     | 
| 
       35 
36 
     | 
    
         
             
                # @param [String] schema_path
         
     | 
| 
       36 
37 
     | 
    
         
             
                # @return [Committee::Driver]
         
     | 
| 
       37 
     | 
    
         
            -
                def self.load_from_file(schema_path)
         
     | 
| 
      
 38 
     | 
    
         
            +
                def self.load_from_file(schema_path, parser_options: {})
         
     | 
| 
       38 
39 
     | 
    
         
             
                  case File.extname(schema_path)
         
     | 
| 
       39 
40 
     | 
    
         
             
                  when '.json'
         
     | 
| 
       40 
     | 
    
         
            -
                    load_from_json(schema_path)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    load_from_json(schema_path, parser_options: parser_options)
         
     | 
| 
       41 
42 
     | 
    
         
             
                  when '.yaml', '.yml'
         
     | 
| 
       42 
     | 
    
         
            -
                    load_from_yaml(schema_path)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    load_from_yaml(schema_path, parser_options: parser_options)
         
     | 
| 
       43 
44 
     | 
    
         
             
                  else
         
     | 
| 
       44 
45 
     | 
    
         
             
                    raise "Committee only supports the following file extensions: '.json', '.yaml', '.yml'"
         
     | 
| 
       45 
46 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -48,10 +49,20 @@ module Committee 
     | 
|
| 
       48 
49 
     | 
    
         
             
                # load and build drive from Hash object
         
     | 
| 
       49 
50 
     | 
    
         
             
                # @param [Hash] hash
         
     | 
| 
       50 
51 
     | 
    
         
             
                # @return [Committee::Driver]
         
     | 
| 
       51 
     | 
    
         
            -
                def self.load_from_data(hash)
         
     | 
| 
      
 52 
     | 
    
         
            +
                def self.load_from_data(hash, schema_path = nil, parser_options: {})
         
     | 
| 
       52 
53 
     | 
    
         
             
                  if hash['openapi']&.start_with?('3.0.')
         
     | 
| 
       53 
     | 
    
         
            -
                     
     | 
| 
       54 
     | 
    
         
            -
                     
     | 
| 
      
 54 
     | 
    
         
            +
                    # From the next major version, we want to ensure `{ strict_reference_validation: true }`
         
     | 
| 
      
 55 
     | 
    
         
            +
                    # as a parser option here, but since it may break existing implementations, just warn
         
     | 
| 
      
 56 
     | 
    
         
            +
                    # if it is not explicitly set. See: https://github.com/interagent/committee/issues/343#issuecomment-997400329
         
     | 
| 
      
 57 
     | 
    
         
            +
                    opts = parser_options.dup
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    Committee.warn_deprecated_until_6(!opts.key?(:strict_reference_validation), 'openapi_parser will default to strict reference validation ' +
         
     | 
| 
      
 60 
     | 
    
         
            +
                    'from next version. Pass config `strict_reference_validation: true` (or false, if you must) ' +
         
     | 
| 
      
 61 
     | 
    
         
            +
                    'to quiet this warning.')
         
     | 
| 
      
 62 
     | 
    
         
            +
                    opts[:strict_reference_validation] ||= false
         
     | 
| 
      
 63 
     | 
    
         
            +
                    
         
     | 
| 
      
 64 
     | 
    
         
            +
                    openapi = OpenAPIParser.parse_with_filepath(hash, schema_path, opts)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    return Committee::Drivers::OpenAPI3::Driver.new.parse(openapi)
         
     | 
| 
       55 
66 
     | 
    
         
             
                  end
         
     | 
| 
       56 
67 
     | 
    
         | 
| 
       57 
68 
     | 
    
         
             
                  driver = if hash['swagger'] == '2.0'
         
     | 
| 
         @@ -60,6 +71,7 @@ module Committee 
     | 
|
| 
       60 
71 
     | 
    
         
             
                             Committee::Drivers::HyperSchema::Driver.new
         
     | 
| 
       61 
72 
     | 
    
         
             
                           end
         
     | 
| 
       62 
73 
     | 
    
         | 
| 
      
 74 
     | 
    
         
            +
                  # TODO: in the future, pass `opts` here and allow optionality in other drivers?
         
     | 
| 
       63 
75 
     | 
    
         
             
                  driver.parse(hash)
         
     | 
| 
       64 
76 
     | 
    
         
             
                end
         
     | 
| 
       65 
77 
     | 
    
         
             
              end
         
     | 
    
        data/lib/committee/errors.rb
    CHANGED
    
    | 
         @@ -8,9 +8,21 @@ module Committee 
     | 
|
| 
       8 
8 
     | 
    
         
             
              end
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
              class InvalidRequest < Error
         
     | 
| 
      
 11 
     | 
    
         
            +
                attr_reader :original_error
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def initialize(error_message=nil, original_error: nil)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @original_error = original_error
         
     | 
| 
      
 15 
     | 
    
         
            +
                  super(error_message)
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
       11 
17 
     | 
    
         
             
              end
         
     | 
| 
       12 
18 
     | 
    
         | 
| 
       13 
19 
     | 
    
         
             
              class InvalidResponse < Error
         
     | 
| 
      
 20 
     | 
    
         
            +
                attr_reader :original_error
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def initialize(error_message=nil, original_error: nil)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  @original_error = original_error
         
     | 
| 
      
 24 
     | 
    
         
            +
                  super(error_message)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
       14 
26 
     | 
    
         
             
              end
         
     | 
| 
       15 
27 
     | 
    
         | 
| 
       16 
28 
     | 
    
         
             
              class NotFound < Error
         
     | 
| 
         @@ -30,11 +30,12 @@ module Committee 
     | 
|
| 
       30 
30 
     | 
    
         
             
                  class << self
         
     | 
| 
       31 
31 
     | 
    
         
             
                    def get_schema(options)
         
     | 
| 
       32 
32 
     | 
    
         
             
                      schema = options[:schema]
         
     | 
| 
       33 
     | 
    
         
            -
                       
     | 
| 
       34 
     | 
    
         
            -
                         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
                         
     | 
| 
      
 33 
     | 
    
         
            +
                      if !schema && options[:schema_path]
         
     | 
| 
      
 34 
     | 
    
         
            +
                        # In the future, we could have `parser_options` as an exposed config?
         
     | 
| 
      
 35 
     | 
    
         
            +
                        parser_options = options.key?(:strict_reference_validation) ? { strict_reference_validation: options[:strict_reference_validation] } : {}
         
     | 
| 
      
 36 
     | 
    
         
            +
                        schema = Committee::Drivers::load_from_file(options[:schema_path], parser_options: parser_options)
         
     | 
| 
       37 
37 
     | 
    
         
             
                      end
         
     | 
| 
      
 38 
     | 
    
         
            +
                      raise(ArgumentError, "Committee: need option `schema` or `schema_path`") unless schema
         
     | 
| 
       38 
39 
     | 
    
         | 
| 
       39 
40 
     | 
    
         
             
                      # Expect the type we want by now. If we don't have it, the user passed
         
     | 
| 
       40 
41 
     | 
    
         
             
                      # something else non-standard in.
         
     | 
| 
         @@ -7,9 +7,6 @@ module Committee 
     | 
|
| 
       7 
7 
     | 
    
         
             
                    super
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
                    @strict  = options[:strict]
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
                    # deprecated
         
     | 
| 
       12 
     | 
    
         
            -
                    @allow_extra = options[:allow_extra]
         
     | 
| 
       13 
10 
     | 
    
         
             
                  end
         
     | 
| 
       14 
11 
     | 
    
         | 
| 
       15 
12 
     | 
    
         
             
                  def handle(request)
         
     | 
| 
         @@ -21,14 +18,14 @@ module Committee 
     | 
|
| 
       21 
18 
     | 
    
         
             
                    rescue Committee::BadRequest, Committee::InvalidRequest
         
     | 
| 
       22 
19 
     | 
    
         
             
                      handle_exception($!, request.env)
         
     | 
| 
       23 
20 
     | 
    
         
             
                      raise if @raise
         
     | 
| 
       24 
     | 
    
         
            -
                      return @error_class.new(400, :bad_request, $!.message).render unless @ignore_error
         
     | 
| 
      
 21 
     | 
    
         
            +
                      return @error_class.new(400, :bad_request, $!.message, request).render unless @ignore_error
         
     | 
| 
       25 
22 
     | 
    
         
             
                    rescue Committee::NotFound => e
         
     | 
| 
       26 
23 
     | 
    
         
             
                      raise if @raise
         
     | 
| 
       27 
     | 
    
         
            -
                      return @error_class.new(404, :not_found, e.message).render unless @ignore_error
         
     | 
| 
      
 24 
     | 
    
         
            +
                      return @error_class.new(404, :not_found, e.message, request).render unless @ignore_error
         
     | 
| 
       28 
25 
     | 
    
         
             
                    rescue JSON::ParserError
         
     | 
| 
       29 
26 
     | 
    
         
             
                      handle_exception($!, request.env)
         
     | 
| 
       30 
27 
     | 
    
         
             
                      raise Committee::InvalidRequest if @raise
         
     | 
| 
       31 
     | 
    
         
            -
                      return @error_class.new(400, :bad_request, "Request body wasn't valid JSON.").render unless @ignore_error
         
     | 
| 
      
 28 
     | 
    
         
            +
                      return @error_class.new(400, :bad_request, "Request body wasn't valid JSON.", request).render unless @ignore_error
         
     | 
| 
       32 
29 
     | 
    
         
             
                    end
         
     | 
| 
       33 
30 
     | 
    
         | 
| 
       34 
31 
     | 
    
         
             
                    @app.call(request.env)
         
     | 
| 
         @@ -37,18 +34,7 @@ module Committee 
     | 
|
| 
       37 
34 
     | 
    
         
             
                  private
         
     | 
| 
       38 
35 
     | 
    
         | 
| 
       39 
36 
     | 
    
         
             
                  def handle_exception(e, env)
         
     | 
| 
       40 
     | 
    
         
            -
                     
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                    if @error_handler.arity > 1
         
     | 
| 
       43 
     | 
    
         
            -
                      @error_handler.call(e, env)
         
     | 
| 
       44 
     | 
    
         
            -
                    else
         
     | 
| 
       45 
     | 
    
         
            -
                      warn <<-MESSAGE
         
     | 
| 
       46 
     | 
    
         
            -
                      [DEPRECATION] Using `error_handler.call(exception)` is deprecated and will be change to
         
     | 
| 
       47 
     | 
    
         
            -
                        `error_handler.call(exception, request.env)` in next major version.
         
     | 
| 
       48 
     | 
    
         
            -
                      MESSAGE
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                      @error_handler.call(e)
         
     | 
| 
       51 
     | 
    
         
            -
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
                    @error_handler.call(e, env) if @error_handler
         
     | 
| 
       52 
38 
     | 
    
         
             
                  end
         
     | 
| 
       53 
39 
     | 
    
         
             
                end
         
     | 
| 
       54 
40 
     | 
    
         
             
              end
         
     | 
| 
         @@ -7,15 +7,16 @@ module Committee 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                  def initialize(app, options = {})
         
     | 
| 
       9 
9 
     | 
    
         
             
                    super
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @strict = options[:strict]
         
     | 
| 
       10 
11 
     | 
    
         
             
                    @validate_success_only = @schema.validator_option.validate_success_only
         
     | 
| 
       11 
12 
     | 
    
         
             
                  end
         
     | 
| 
       12 
13 
     | 
    
         | 
| 
       13 
14 
     | 
    
         
             
                  def handle(request)
         
     | 
| 
       14 
     | 
    
         
            -
                     
     | 
| 
       15 
     | 
    
         
            -
                      status, headers, response = @app.call(request.env)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    status, headers, response = @app.call(request.env)
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
      
 17 
     | 
    
         
            +
                    begin
         
     | 
| 
       17 
18 
     | 
    
         
             
                      v = build_schema_validator(request)
         
     | 
| 
       18 
     | 
    
         
            -
                      v.response_validate(status, headers, response) if v.link_exist? && self.class.validate?(status, validate_success_only)
         
     | 
| 
      
 19 
     | 
    
         
            +
                      v.response_validate(status, headers, response, @strict) if v.link_exist? && self.class.validate?(status, validate_success_only)
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
       20 
21 
     | 
    
         
             
                    rescue Committee::InvalidResponse
         
     | 
| 
       21 
22 
     | 
    
         
             
                      handle_exception($!, request.env)
         
     | 
| 
         @@ -34,25 +35,23 @@ module Committee 
     | 
|
| 
       34 
35 
     | 
    
         | 
| 
       35 
36 
     | 
    
         
             
                  class << self
         
     | 
| 
       36 
37 
     | 
    
         
             
                    def validate?(status, validate_success_only)
         
     | 
| 
       37 
     | 
    
         
            -
                       
     | 
| 
      
 38 
     | 
    
         
            +
                      case status
         
     | 
| 
      
 39 
     | 
    
         
            +
                      when 204
         
     | 
| 
      
 40 
     | 
    
         
            +
                        false
         
     | 
| 
      
 41 
     | 
    
         
            +
                      when 200..299
         
     | 
| 
      
 42 
     | 
    
         
            +
                        true
         
     | 
| 
      
 43 
     | 
    
         
            +
                      when 304
         
     | 
| 
      
 44 
     | 
    
         
            +
                        false
         
     | 
| 
      
 45 
     | 
    
         
            +
                      else
         
     | 
| 
      
 46 
     | 
    
         
            +
                        !validate_success_only
         
     | 
| 
      
 47 
     | 
    
         
            +
                      end
         
     | 
| 
       38 
48 
     | 
    
         
             
                    end
         
     | 
| 
       39 
49 
     | 
    
         
             
                  end
         
     | 
| 
       40 
50 
     | 
    
         | 
| 
       41 
51 
     | 
    
         
             
                  private
         
     | 
| 
       42 
52 
     | 
    
         | 
| 
       43 
53 
     | 
    
         
             
                  def handle_exception(e, env)
         
     | 
| 
       44 
     | 
    
         
            -
                     
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
                    if @error_handler.arity > 1
         
     | 
| 
       47 
     | 
    
         
            -
                      @error_handler.call(e, env)
         
     | 
| 
       48 
     | 
    
         
            -
                    else
         
     | 
| 
       49 
     | 
    
         
            -
                      warn <<-MESSAGE
         
     | 
| 
       50 
     | 
    
         
            -
                      [DEPRECATION] Using `error_handler.call(exception)` is deprecated and will be change to
         
     | 
| 
       51 
     | 
    
         
            -
                        `error_handler.call(exception, request.env)` in next major version.
         
     | 
| 
       52 
     | 
    
         
            -
                      MESSAGE
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
                      @error_handler.call(e)
         
     | 
| 
       55 
     | 
    
         
            -
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @error_handler.call(e, env) if @error_handler
         
     | 
| 
       56 
55 
     | 
    
         
             
                  end
         
     | 
| 
       57 
56 
     | 
    
         
             
                end
         
     | 
| 
       58 
57 
     | 
    
         
             
              end
         
     | 
| 
         @@ -2,84 +2,82 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Committee
         
     | 
| 
       4 
4 
     | 
    
         
             
              class RequestUnpacker
         
     | 
| 
       5 
     | 
    
         
            -
                 
     | 
| 
       6 
     | 
    
         
            -
                   
     | 
| 
      
 5 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # Enable string or symbol key access to the nested params hash.
         
     | 
| 
      
 7 
     | 
    
         
            +
                  #
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # (Copied from Sinatra)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def indifferent_params(object)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    case object
         
     | 
| 
      
 11 
     | 
    
         
            +
                    when Hash
         
     | 
| 
      
 12 
     | 
    
         
            +
                      new_hash = Committee::Utils.indifferent_hash
         
     | 
| 
      
 13 
     | 
    
         
            +
                      object.each { |key, value| new_hash[key] = indifferent_params(value) }
         
     | 
| 
      
 14 
     | 
    
         
            +
                      new_hash
         
     | 
| 
      
 15 
     | 
    
         
            +
                    when Array
         
     | 
| 
      
 16 
     | 
    
         
            +
                      object.map { |item| indifferent_params(item) }
         
     | 
| 
      
 17 
     | 
    
         
            +
                    else
         
     | 
| 
      
 18 
     | 
    
         
            +
                      object
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
       7 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
                def initialize(options={})
         
     | 
| 
       8 
24 
     | 
    
         
             
                  @allow_form_params  = options[:allow_form_params]
         
     | 
| 
       9 
25 
     | 
    
         
             
                  @allow_get_body     = options[:allow_get_body]
         
     | 
| 
       10 
26 
     | 
    
         
             
                  @allow_query_params = options[:allow_query_params]
         
     | 
| 
       11 
     | 
    
         
            -
                  @coerce_form_params = options[:coerce_form_params]
         
     | 
| 
       12 
27 
     | 
    
         
             
                  @optimistic_json    = options[:optimistic_json]
         
     | 
| 
       13 
     | 
    
         
            -
                  @schema_validator   = options[:schema_validator]
         
     | 
| 
       14 
28 
     | 
    
         
             
                end
         
     | 
| 
       15 
29 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
                 
     | 
| 
      
 30 
     | 
    
         
            +
                # return params and is_form_params
         
     | 
| 
      
 31 
     | 
    
         
            +
                def unpack_request_params(request)
         
     | 
| 
       17 
32 
     | 
    
         
             
                  # if Content-Type is empty or JSON, and there was a request body, try to
         
     | 
| 
       18 
33 
     | 
    
         
             
                  # interpret it as JSON
         
     | 
| 
       19 
     | 
    
         
            -
                  params = if  
     | 
| 
       20 
     | 
    
         
            -
                    parse_json
         
     | 
| 
      
 34 
     | 
    
         
            +
                  params = if !request.media_type || request.media_type =~ %r{application/(?:.*\+)?json}
         
     | 
| 
      
 35 
     | 
    
         
            +
                    parse_json(request)
         
     | 
| 
       21 
36 
     | 
    
         
             
                  elsif @optimistic_json
         
     | 
| 
       22 
37 
     | 
    
         
             
                    begin
         
     | 
| 
       23 
     | 
    
         
            -
                      parse_json
         
     | 
| 
      
 38 
     | 
    
         
            +
                      parse_json(request)
         
     | 
| 
       24 
39 
     | 
    
         
             
                    rescue JSON::ParserError
         
     | 
| 
       25 
40 
     | 
    
         
             
                      nil
         
     | 
| 
       26 
41 
     | 
    
         
             
                    end
         
     | 
| 
       27 
42 
     | 
    
         
             
                  end
         
     | 
| 
       28 
43 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                  params  
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                   
     | 
| 
      
 44 
     | 
    
         
            +
                  return [params, false] if params
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  if @allow_form_params && %w[application/x-www-form-urlencoded multipart/form-data].include?(request.media_type)
         
     | 
| 
       32 
47 
     | 
    
         
             
                    # Actually, POST means anything in the request body, could be from
         
     | 
| 
       33 
48 
     | 
    
         
             
                    # PUT or PATCH too. Silly Rack.
         
     | 
| 
       34 
     | 
    
         
            -
                     
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
                    @schema_validator.coerce_form_params(p) if @coerce_form_params
         
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                    p
         
     | 
| 
       39 
     | 
    
         
            -
                  else
         
     | 
| 
       40 
     | 
    
         
            -
                    {}
         
     | 
| 
      
 49 
     | 
    
         
            +
                    return [request.POST, true] if request.POST
         
     | 
| 
       41 
50 
     | 
    
         
             
                  end
         
     | 
| 
       42 
51 
     | 
    
         | 
| 
       43 
     | 
    
         
            -
                   
     | 
| 
       44 
     | 
    
         
            -
                    [indifferent_params(@request.GET).merge(params), headers]
         
     | 
| 
       45 
     | 
    
         
            -
                  else
         
     | 
| 
       46 
     | 
    
         
            -
                    [params, headers]
         
     | 
| 
       47 
     | 
    
         
            -
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  [{}, false]
         
     | 
| 
       48 
53 
     | 
    
         
             
                end
         
     | 
| 
       49 
54 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
                 
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
                # Creates a Hash with indifferent access.
         
     | 
| 
       53 
     | 
    
         
            -
                #
         
     | 
| 
       54 
     | 
    
         
            -
                # (Copied from Sinatra)
         
     | 
| 
       55 
     | 
    
         
            -
                def indifferent_hash
         
     | 
| 
       56 
     | 
    
         
            -
                  Hash.new { |hash,key| hash[key.to_s] if Symbol === key }
         
     | 
| 
      
 55 
     | 
    
         
            +
                def unpack_query_params(request)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  @allow_query_params ? self.class.indifferent_params(request.GET) : {}
         
     | 
| 
       57 
57 
     | 
    
         
             
                end
         
     | 
| 
       58 
58 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
                 
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                    new_hash = indifferent_hash
         
     | 
| 
       66 
     | 
    
         
            -
                    object.each { |key, value| new_hash[key] = indifferent_params(value) }
         
     | 
| 
       67 
     | 
    
         
            -
                    new_hash
         
     | 
| 
       68 
     | 
    
         
            -
                  when Array
         
     | 
| 
       69 
     | 
    
         
            -
                    object.map { |item| indifferent_params(item) }
         
     | 
| 
       70 
     | 
    
         
            -
                  else
         
     | 
| 
       71 
     | 
    
         
            -
                    object
         
     | 
| 
      
 59 
     | 
    
         
            +
                def unpack_headers(request)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  env = request.env
         
     | 
| 
      
 61 
     | 
    
         
            +
                  base = env.keys.grep(/HTTP_/).inject({}) do |headers, key|
         
     | 
| 
      
 62 
     | 
    
         
            +
                    headerized_key = key.gsub(/^HTTP_/, '').gsub(/_/, '-')
         
     | 
| 
      
 63 
     | 
    
         
            +
                    headers[headerized_key] = env[key]
         
     | 
| 
      
 64 
     | 
    
         
            +
                    headers
         
     | 
| 
       72 
65 
     | 
    
         
             
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  base['Content-Type'] = env['CONTENT_TYPE'] if env['CONTENT_TYPE']
         
     | 
| 
      
 68 
     | 
    
         
            +
                  base
         
     | 
| 
       73 
69 
     | 
    
         
             
                end
         
     | 
| 
       74 
70 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
                 
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
      
 71 
     | 
    
         
            +
                private
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                def parse_json(request)
         
     | 
| 
      
 74 
     | 
    
         
            +
                  return nil if request.request_method == "GET" && !@allow_get_body
         
     | 
| 
       77 
75 
     | 
    
         | 
| 
       78 
     | 
    
         
            -
                  body =  
     | 
| 
      
 76 
     | 
    
         
            +
                  body = request.body.read
         
     | 
| 
       79 
77 
     | 
    
         
             
                  # if request body is empty, we just have empty params
         
     | 
| 
       80 
78 
     | 
    
         
             
                  return nil if body.length == 0
         
     | 
| 
       81 
79 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
                   
     | 
| 
      
 80 
     | 
    
         
            +
                  request.body.rewind
         
     | 
| 
       83 
81 
     | 
    
         
             
                  hash = JSON.parse(body)
         
     | 
| 
       84 
82 
     | 
    
         
             
                  # We want a hash specifically. '42', 42, and [42] will all be
         
     | 
| 
       85 
83 
     | 
    
         
             
                  # decoded properly, but we can't use them here.
         
     | 
| 
         @@ -87,19 +85,7 @@ module Committee 
     | 
|
| 
       87 
85 
     | 
    
         
             
                    raise BadRequest,
         
     | 
| 
       88 
86 
     | 
    
         
             
                          "Invalid JSON input. Require object with parameters as keys."
         
     | 
| 
       89 
87 
     | 
    
         
             
                  end
         
     | 
| 
       90 
     | 
    
         
            -
                  indifferent_params(hash)
         
     | 
| 
       91 
     | 
    
         
            -
                end
         
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
                def headers
         
     | 
| 
       94 
     | 
    
         
            -
                  env = @request.env
         
     | 
| 
       95 
     | 
    
         
            -
                  base = env.keys.grep(/HTTP_/).inject({}) do |headers, key|
         
     | 
| 
       96 
     | 
    
         
            -
                    headerized_key = key.gsub(/^HTTP_/, '').gsub(/_/, '-')
         
     | 
| 
       97 
     | 
    
         
            -
                    headers[headerized_key] = env[key]
         
     | 
| 
       98 
     | 
    
         
            -
                    headers
         
     | 
| 
       99 
     | 
    
         
            -
                  end
         
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
                  base['Content-Type'] = env['CONTENT_TYPE'] if env['CONTENT_TYPE']
         
     | 
| 
       102 
     | 
    
         
            -
                  base
         
     | 
| 
      
 88 
     | 
    
         
            +
                  self.class.indifferent_params(hash)
         
     | 
| 
       103 
89 
     | 
    
         
             
                end
         
     | 
| 
       104 
90 
     | 
    
         
             
              end
         
     | 
| 
       105 
91 
     | 
    
         
             
            end
         
     | 
| 
         @@ -14,7 +14,7 @@ module Committee 
     | 
|
| 
       14 
14 
     | 
    
         
             
                    end
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
                    def call(status, headers, data)
         
     | 
| 
       17 
     | 
    
         
            -
                      unless status  
     | 
| 
      
 17 
     | 
    
         
            +
                      unless [204, 304].include?(status) # 204 No Content or 304 Not Modified
         
     | 
| 
       18 
18 
     | 
    
         
             
                        response = Rack::Response.new(data, status, headers)
         
     | 
| 
       19 
19 
     | 
    
         
             
                        check_content_type!(response)
         
     | 
| 
       20 
20 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -48,9 +48,15 @@ module Committee 
     | 
|
| 
       48 
48 
     | 
    
         
             
                    private
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
50 
     | 
    
         
             
                    def response_media_type(response)
         
     | 
| 
       51 
     | 
    
         
            -
                      response. 
     | 
| 
      
 51 
     | 
    
         
            +
                      if response.respond_to?(:media_type)
         
     | 
| 
      
 52 
     | 
    
         
            +
                        response.media_type.to_s
         
     | 
| 
      
 53 
     | 
    
         
            +
                      else
         
     | 
| 
      
 54 
     | 
    
         
            +
                        # for rack compatibility. In rack v 1.5.0, Rack::Response doesn't have media_type
         
     | 
| 
      
 55 
     | 
    
         
            +
                        response.content_type.to_s.split(";").first.to_s
         
     | 
| 
      
 56 
     | 
    
         
            +
                      end
         
     | 
| 
       52 
57 
     | 
    
         
             
                    end
         
     | 
| 
       53 
58 
     | 
    
         | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
       54 
60 
     | 
    
         
             
                    def check_content_type!(response)
         
     | 
| 
       55 
61 
     | 
    
         
             
                      if @link.media_type
         
     | 
| 
       56 
62 
     | 
    
         
             
                        unless Rack::Mime.match?(response_media_type(response), @link.media_type)
         
     | 
| 
         @@ -11,18 +11,8 @@ module Committee 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  end
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
                  def request_validate(request)
         
     | 
| 
       14 
     | 
    
         
            -
                    # Attempts to coerce parameters that appear in a link's URL to Ruby
         
     | 
| 
       15 
     | 
    
         
            -
                    # types that can be validated with a schema.
         
     | 
| 
       16 
     | 
    
         
            -
                    param_matches_hash = validator_option.coerce_path_params ? coerce_path_params : {}
         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
                    # Attempts to coerce parameters that appear in a query string to Ruby
         
     | 
| 
       19 
     | 
    
         
            -
                    # types that can be validated with a schema.
         
     | 
| 
       20 
     | 
    
         
            -
                    coerce_query_params(request) if validator_option.coerce_query_params
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
14 
     | 
    
         
             
                    request_unpack(request)
         
     | 
| 
       23 
15 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                    request.env[validator_option.params_key].merge!(param_matches_hash) if param_matches_hash
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
16 
     | 
    
         
             
                    request_schema_validation(request)
         
     | 
| 
       27 
17 
     | 
    
         
             
                    parameter_coerce!(request, link, validator_option.params_key)
         
     | 
| 
       28 
18 
     | 
    
         
             
                    parameter_coerce!(request, link, "rack.request.query_hash") if link_exist? && !request.GET.nil? && !link.schema.nil?
         
     | 
| 
         @@ -35,7 +25,14 @@ module Committee 
     | 
|
| 
       35 
25 
     | 
    
         
             
                    response.each do |chunk|
         
     | 
| 
       36 
26 
     | 
    
         
             
                      full_body << chunk
         
     | 
| 
       37 
27 
     | 
    
         
             
                    end
         
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    data = {}
         
     | 
| 
      
 30 
     | 
    
         
            +
                    unless full_body.empty?
         
     | 
| 
      
 31 
     | 
    
         
            +
                      parse_to_json = !validator_option.parse_response_by_content_type ||
         
     | 
| 
      
 32 
     | 
    
         
            +
                                      headers.fetch('Content-Type', nil)&.start_with?('application/json')
         
     | 
| 
      
 33 
     | 
    
         
            +
                      data = JSON.parse(full_body) if parse_to_json
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
       39 
36 
     | 
    
         
             
                    Committee::SchemaValidator::HyperSchema::ResponseValidator.new(link, validate_success_only: validator_option.validate_success_only).call(status, headers, data)
         
     | 
| 
       40 
37 
     | 
    
         
             
                  end
         
     | 
| 
       41 
38 
     | 
    
         | 
| 
         @@ -43,16 +40,10 @@ module Committee 
     | 
|
| 
       43 
40 
     | 
    
         
             
                    !link.nil?
         
     | 
| 
       44 
41 
     | 
    
         
             
                  end
         
     | 
| 
       45 
42 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
                  def coerce_form_params(parameter)
         
     | 
| 
       47 
     | 
    
         
            -
                    return unless link_exist?
         
     | 
| 
       48 
     | 
    
         
            -
                    return unless link.schema
         
     | 
| 
       49 
     | 
    
         
            -
                    Committee::SchemaValidator::HyperSchema::StringParamsCoercer.new(parameter, link.schema).call!
         
     | 
| 
       50 
     | 
    
         
            -
                  end
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
43 
     | 
    
         
             
                  private
         
     | 
| 
       53 
44 
     | 
    
         | 
| 
       54 
45 
     | 
    
         
             
                    def coerce_path_params
         
     | 
| 
       55 
     | 
    
         
            -
                      return unless link_exist?
         
     | 
| 
      
 46 
     | 
    
         
            +
                      return {} unless link_exist?
         
     | 
| 
       56 
47 
     | 
    
         | 
| 
       57 
48 
     | 
    
         
             
                      Committee::SchemaValidator::HyperSchema::StringParamsCoercer.new(param_matches, link.schema, coerce_recursive: validator_option.coerce_recursive).call!
         
     | 
| 
       58 
49 
     | 
    
         
             
                      param_matches
         
     | 
| 
         @@ -66,15 +57,38 @@ module Committee 
     | 
|
| 
       66 
57 
     | 
    
         
             
                    end
         
     | 
| 
       67 
58 
     | 
    
         | 
| 
       68 
59 
     | 
    
         
             
                    def request_unpack(request)
         
     | 
| 
       69 
     | 
    
         
            -
                       
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
      
 60 
     | 
    
         
            +
                      unpacker = Committee::RequestUnpacker.new(
         
     | 
| 
      
 61 
     | 
    
         
            +
                        allow_form_params:  validator_option.allow_form_params,
         
     | 
| 
      
 62 
     | 
    
         
            +
                        allow_get_body:     validator_option.allow_get_body,
         
     | 
| 
      
 63 
     | 
    
         
            +
                        allow_query_params: validator_option.allow_query_params,
         
     | 
| 
      
 64 
     | 
    
         
            +
                        optimistic_json:    validator_option.optimistic_json,
         
     | 
| 
      
 65 
     | 
    
         
            +
                      )
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                      request.env[validator_option.headers_key] = unpacker.unpack_headers(request)
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                      # Attempts to coerce parameters that appear in a link's URL to Ruby
         
     | 
| 
      
 70 
     | 
    
         
            +
                      # types that can be validated with a schema.
         
     | 
| 
      
 71 
     | 
    
         
            +
                      param_matches_hash = validator_option.coerce_path_params ? coerce_path_params : {}
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                      # Attempts to coerce parameters that appear in a query string to Ruby
         
     | 
| 
      
 74 
     | 
    
         
            +
                      # types that can be validated with a schema.
         
     | 
| 
      
 75 
     | 
    
         
            +
                      coerce_query_params(request) if validator_option.coerce_query_params
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                      query_param = unpacker.unpack_query_params(request)
         
     | 
| 
      
 78 
     | 
    
         
            +
                      request_param, is_form_params = unpacker.unpack_request_params(request)
         
     | 
| 
      
 79 
     | 
    
         
            +
                      coerce_form_params(request_param) if validator_option.coerce_form_params && is_form_params
         
     | 
| 
      
 80 
     | 
    
         
            +
                      request.env[validator_option.request_body_hash_key] = request_param
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                      request.env[validator_option.params_key] = Committee::Utils.indifferent_hash
         
     | 
| 
      
 83 
     | 
    
         
            +
                      request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(query_param))
         
     | 
| 
      
 84 
     | 
    
         
            +
                      request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(request_param))
         
     | 
| 
      
 85 
     | 
    
         
            +
                      request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(param_matches_hash))
         
     | 
| 
      
 86 
     | 
    
         
            +
                    end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                    def coerce_form_params(parameter)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      return unless link_exist?
         
     | 
| 
      
 90 
     | 
    
         
            +
                      return unless link.schema
         
     | 
| 
      
 91 
     | 
    
         
            +
                      Committee::SchemaValidator::HyperSchema::StringParamsCoercer.new(parameter, link.schema).call!
         
     | 
| 
       78 
92 
     | 
    
         
             
                    end
         
     | 
| 
       79 
93 
     | 
    
         | 
| 
       80 
94 
     | 
    
         
             
                    def request_schema_validation(request)
         
     | 
| 
         @@ -17,13 +17,17 @@ module Committee 
     | 
|
| 
       17 
17 
     | 
    
         
             
                      request_operation.original_path
         
     | 
| 
       18 
18 
     | 
    
         
             
                    end
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
      
 20 
     | 
    
         
            +
                    def http_method
         
     | 
| 
      
 21 
     | 
    
         
            +
                      request_operation.http_method
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
       20 
24 
     | 
    
         
             
                    def coerce_path_parameter(validator_option)
         
     | 
| 
       21 
25 
     | 
    
         
             
                      options = build_openapi_parser_path_option(validator_option)
         
     | 
| 
       22 
26 
     | 
    
         
             
                      return {} unless options.coerce_value
         
     | 
| 
       23 
27 
     | 
    
         | 
| 
       24 
28 
     | 
    
         
             
                      request_operation.validate_path_params(options)
         
     | 
| 
       25 
29 
     | 
    
         
             
                    rescue OpenAPIParser::OpenAPIError => e
         
     | 
| 
       26 
     | 
    
         
            -
                      raise Committee::InvalidRequest.new(e.message)
         
     | 
| 
      
 30 
     | 
    
         
            +
                      raise Committee::InvalidRequest.new(e.message, original_error: e)
         
     | 
| 
       27 
31 
     | 
    
         
             
                    end
         
     | 
| 
       28 
32 
     | 
    
         | 
| 
       29 
33 
     | 
    
         
             
                    # @param [Boolean] strict when not content_type or status code definition, raise error
         
     | 
| 
         @@ -32,21 +36,15 @@ module Committee 
     | 
|
| 
       32 
36 
     | 
    
         | 
| 
       33 
37 
     | 
    
         
             
                      return request_operation.validate_response_body(response_body, response_validate_options(strict, check_header))
         
     | 
| 
       34 
38 
     | 
    
         
             
                    rescue OpenAPIParser::OpenAPIError => e
         
     | 
| 
       35 
     | 
    
         
            -
                      raise Committee::InvalidResponse.new(e.message)
         
     | 
| 
      
 39 
     | 
    
         
            +
                      raise Committee::InvalidResponse.new(e.message, original_error: e)
         
     | 
| 
       36 
40 
     | 
    
         
             
                    end
         
     | 
| 
       37 
41 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
                    def validate_request_params( 
     | 
| 
      
 42 
     | 
    
         
            +
                    def validate_request_params(path_params, query_params, body_params, headers, validator_option)
         
     | 
| 
       39 
43 
     | 
    
         
             
                      ret, err = case request_operation.http_method
         
     | 
| 
       40 
     | 
    
         
            -
                            when 'get'
         
     | 
| 
       41 
     | 
    
         
            -
                              validate_get_request_params( 
     | 
| 
       42 
     | 
    
         
            -
                            when 'post'
         
     | 
| 
       43 
     | 
    
         
            -
                              validate_post_request_params( 
     | 
| 
       44 
     | 
    
         
            -
                            when 'put'
         
     | 
| 
       45 
     | 
    
         
            -
                              validate_post_request_params(params, headers, validator_option)
         
     | 
| 
       46 
     | 
    
         
            -
                            when 'patch'
         
     | 
| 
       47 
     | 
    
         
            -
                              validate_post_request_params(params, headers, validator_option)
         
     | 
| 
       48 
     | 
    
         
            -
                            when 'delete'
         
     | 
| 
       49 
     | 
    
         
            -
                              validate_get_request_params(params, headers, validator_option)
         
     | 
| 
      
 44 
     | 
    
         
            +
                            when 'get', 'delete', 'head'
         
     | 
| 
      
 45 
     | 
    
         
            +
                              validate_get_request_params(path_params, query_params, headers, validator_option)
         
     | 
| 
      
 46 
     | 
    
         
            +
                            when 'post', 'put', 'patch', 'options'
         
     | 
| 
      
 47 
     | 
    
         
            +
                              validate_post_request_params(path_params, query_params, body_params, headers, validator_option)
         
     | 
| 
       50 
48 
     | 
    
         
             
                            else
         
     | 
| 
       51 
49 
     | 
    
         
             
                              raise "Committee OpenAPI3 not support #{request_operation.http_method} method"
         
     | 
| 
       52 
50 
     | 
    
         
             
                            end
         
     | 
| 
         @@ -54,6 +52,10 @@ module Committee 
     | 
|
| 
       54 
52 
     | 
    
         
             
                      ret
         
     | 
| 
       55 
53 
     | 
    
         
             
                    end
         
     | 
| 
       56 
54 
     | 
    
         | 
| 
      
 55 
     | 
    
         
            +
                    def optional_body?
         
     | 
| 
      
 56 
     | 
    
         
            +
                      !request_operation.operation_object&.request_body&.required
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
       57 
59 
     | 
    
         
             
                    def valid_request_content_type?(content_type)
         
     | 
| 
       58 
60 
     | 
    
         
             
                      if (request_body = request_operation.operation_object&.request_body)
         
     | 
| 
       59 
61 
     | 
    
         
             
                        !request_body.select_media_type(content_type).nil?
         
     | 
| 
         @@ -76,28 +78,17 @@ module Committee 
     | 
|
| 
       76 
78 
     | 
    
         
             
                    #   @return [OpenAPIParser::RequestOperation]
         
     | 
| 
       77 
79 
     | 
    
         | 
| 
       78 
80 
     | 
    
         
             
                    # @return [OpenAPIParser::SchemaValidator::Options]
         
     | 
| 
       79 
     | 
    
         
            -
                    def  
     | 
| 
       80 
     | 
    
         
            -
                       
     | 
| 
       81 
     | 
    
         
            -
                      datetime_coerce_class = validator_option.coerce_date_times ? DateTime : nil
         
     | 
| 
       82 
     | 
    
         
            -
                      validate_header = validator_option.check_header
         
     | 
| 
       83 
     | 
    
         
            -
                      OpenAPIParser::SchemaValidator::Options.new(coerce_value: coerce_value,
         
     | 
| 
       84 
     | 
    
         
            -
                                                                  datetime_coerce_class: datetime_coerce_class,
         
     | 
| 
       85 
     | 
    
         
            -
                                                                  validate_header: validate_header)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    def build_openapi_parser_body_option(validator_option)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      build_openapi_parser_option(validator_option, validator_option.coerce_form_params)
         
     | 
| 
       86 
83 
     | 
    
         
             
                    end
         
     | 
| 
       87 
84 
     | 
    
         | 
| 
       88 
85 
     | 
    
         
             
                    # @return [OpenAPIParser::SchemaValidator::Options]
         
     | 
| 
       89 
     | 
    
         
            -
                    def  
     | 
| 
       90 
     | 
    
         
            -
                       
     | 
| 
       91 
     | 
    
         
            -
                      datetime_coerce_class = validator_option.coerce_date_times ? DateTime : nil
         
     | 
| 
       92 
     | 
    
         
            -
                      validate_header = validator_option.check_header
         
     | 
| 
       93 
     | 
    
         
            -
                      OpenAPIParser::SchemaValidator::Options.new(coerce_value: coerce_value,
         
     | 
| 
       94 
     | 
    
         
            -
                                                                  datetime_coerce_class: datetime_coerce_class,
         
     | 
| 
       95 
     | 
    
         
            -
                                                                  validate_header: validate_header)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    def build_openapi_parser_path_option(validator_option)
         
     | 
| 
      
 87 
     | 
    
         
            +
                      build_openapi_parser_option(validator_option, validator_option.coerce_query_params)
         
     | 
| 
       96 
88 
     | 
    
         
             
                    end
         
     | 
| 
       97 
89 
     | 
    
         | 
| 
       98 
90 
     | 
    
         
             
                    # @return [OpenAPIParser::SchemaValidator::Options]
         
     | 
| 
       99 
     | 
    
         
            -
                    def  
     | 
| 
       100 
     | 
    
         
            -
                      coerce_value = validator_option.coerce_query_params
         
     | 
| 
      
 91 
     | 
    
         
            +
                    def build_openapi_parser_option(validator_option, coerce_value)
         
     | 
| 
       101 
92 
     | 
    
         
             
                      datetime_coerce_class = validator_option.coerce_date_times ? DateTime : nil
         
     | 
| 
       102 
93 
     | 
    
         
             
                      validate_header = validator_option.check_header
         
     | 
| 
       103 
94 
     | 
    
         
             
                      OpenAPIParser::SchemaValidator::Options.new(coerce_value: coerce_value,
         
     | 
| 
         @@ -105,22 +96,38 @@ module Committee 
     | 
|
| 
       105 
96 
     | 
    
         
             
                                                                  validate_header: validate_header)
         
     | 
| 
       106 
97 
     | 
    
         
             
                    end
         
     | 
| 
       107 
98 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
                    def validate_get_request_params( 
     | 
| 
      
 99 
     | 
    
         
            +
                    def validate_get_request_params(path_params, query_params, headers, validator_option)
         
     | 
| 
       109 
100 
     | 
    
         
             
                      # bad performance because when we coerce value, same check
         
     | 
| 
       110 
     | 
    
         
            -
                       
     | 
| 
      
 101 
     | 
    
         
            +
                      validate_path_and_query_params(path_params, query_params, headers, validator_option)
         
     | 
| 
       111 
102 
     | 
    
         
             
                    rescue OpenAPIParser::OpenAPIError => e
         
     | 
| 
       112 
     | 
    
         
            -
                      raise Committee::InvalidRequest.new(e.message)
         
     | 
| 
      
 103 
     | 
    
         
            +
                      raise Committee::InvalidRequest.new(e.message, original_error: e)
         
     | 
| 
       113 
104 
     | 
    
         
             
                    end
         
     | 
| 
       114 
105 
     | 
    
         | 
| 
       115 
     | 
    
         
            -
                    def validate_post_request_params( 
     | 
| 
      
 106 
     | 
    
         
            +
                    def validate_post_request_params(path_params, query_params, body_params, headers, validator_option)
         
     | 
| 
       116 
107 
     | 
    
         
             
                      content_type = headers['Content-Type'].to_s.split(";").first.to_s
         
     | 
| 
       117 
108 
     | 
    
         | 
| 
       118 
109 
     | 
    
         
             
                      # bad performance because when we coerce value, same check
         
     | 
| 
       119 
     | 
    
         
            -
                       
     | 
| 
       120 
     | 
    
         
            -
                      request_operation. 
     | 
| 
       121 
     | 
    
         
            -
                      request_operation.validate_request_body(content_type, params, schema_validator_options)
         
     | 
| 
      
 110 
     | 
    
         
            +
                      validate_path_and_query_params(path_params, query_params, headers, validator_option)
         
     | 
| 
      
 111 
     | 
    
         
            +
                      request_operation.validate_request_body(content_type, body_params, build_openapi_parser_body_option(validator_option))
         
     | 
| 
       122 
112 
     | 
    
         
             
                    rescue => e
         
     | 
| 
       123 
     | 
    
         
            -
                      raise Committee::InvalidRequest.new(e.message)
         
     | 
| 
      
 113 
     | 
    
         
            +
                      raise Committee::InvalidRequest.new(e.message, original_error: e)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                    def validate_path_and_query_params(path_params, query_params, headers, validator_option)
         
     | 
| 
      
 117 
     | 
    
         
            +
                      # it's currently impossible to validate path params and query params separately
         
     | 
| 
      
 118 
     | 
    
         
            +
                      # so we have to resort to this workaround
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                      path_keys = path_params.keys.to_set
         
     | 
| 
      
 121 
     | 
    
         
            +
                      query_keys = query_params.keys.to_set
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                      merged_params = query_params.merge(path_params)
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                      request_operation.validate_request_parameter(merged_params, headers, build_openapi_parser_path_option(validator_option))
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                      merged_params.each do |k, v|
         
     | 
| 
      
 128 
     | 
    
         
            +
                        path_params[k] = v if path_keys.include?(k)
         
     | 
| 
      
 129 
     | 
    
         
            +
                        query_params[k] = v if query_keys.include?(k)
         
     | 
| 
      
 130 
     | 
    
         
            +
                      end
         
     | 
| 
       124 
131 
     | 
    
         
             
                    end
         
     | 
| 
       125 
132 
     | 
    
         | 
| 
       126 
133 
     | 
    
         
             
                    def response_validate_options(strict, check_header)
         
     |