committee 5.6.1 → 5.6.2
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_3/driver.rb +5 -0
- data/lib/committee/drivers.rb +2 -3
- data/lib/committee/errors.rb +11 -0
- data/lib/committee/middleware/base.rb +11 -5
- data/lib/committee/middleware/options/base.rb +107 -0
- data/lib/committee/middleware/options/request_validation.rb +80 -0
- data/lib/committee/middleware/options/response_validation.rb +46 -0
- data/lib/committee/middleware/options.rb +12 -0
- data/lib/committee/middleware/request_validation.rb +7 -1
- data/lib/committee/middleware/response_validation.rb +6 -2
- data/lib/committee/middleware.rb +1 -0
- data/lib/committee/schema_validator/hyper_schema/response_generator.rb +1 -3
- data/lib/committee/schema_validator/hyper_schema/response_validator.rb +14 -1
- data/lib/committee/schema_validator/hyper_schema/router.rb +1 -1
- data/lib/committee/schema_validator/hyper_schema.rb +2 -13
- data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +43 -13
- data/lib/committee/schema_validator/open_api_3/parameter_deserializer.rb +495 -0
- data/lib/committee/schema_validator/open_api_3/response_validator.rb +16 -2
- data/lib/committee/schema_validator/open_api_3.rb +18 -12
- data/lib/committee/schema_validator/option.rb +6 -17
- data/lib/committee/schema_validator.rb +5 -1
- data/lib/committee/test/except_parameter.rb +416 -0
- data/lib/committee/test/methods.rb +27 -2
- data/lib/committee/version.rb +1 -1
- data/lib/committee.rb +1 -1
- data/test/drivers/open_api_2/driver_test.rb +4 -16
- data/test/drivers/open_api_2/parameter_schema_builder_test.rb +4 -50
- data/test/drivers_test.rb +35 -21
- data/test/middleware/options/base_test.rb +120 -0
- data/test/middleware/options/request_validation_test.rb +177 -0
- data/test/middleware/options/response_validation_test.rb +121 -0
- data/test/middleware/request_validation_open_api_3_test.rb +113 -80
- data/test/middleware/request_validation_test.rb +13 -70
- data/test/middleware/response_validation_open_api_3_test.rb +33 -17
- data/test/middleware/response_validation_test.rb +3 -14
- data/test/request_unpacker_test.rb +2 -10
- data/test/schema_validator/hyper_schema/parameter_coercer_test.rb +1 -37
- data/test/schema_validator/hyper_schema/request_validator_test.rb +6 -30
- data/test/schema_validator/hyper_schema/router_test.rb +5 -0
- data/test/schema_validator/hyper_schema/string_params_coercer_test.rb +1 -37
- data/test/schema_validator/open_api_3/operation_wrapper_test.rb +40 -43
- data/test/schema_validator/open_api_3/parameter_deserializer_test.rb +457 -0
- data/test/schema_validator/open_api_3/request_validator_test.rb +1 -2
- data/test/schema_validator/open_api_3/response_validator_test.rb +3 -11
- data/test/schema_validator_test.rb +8 -0
- data/test/test/methods_test.rb +222 -105
- data/test/test/schema_coverage_test.rb +8 -155
- metadata +12 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f5e2c05d7caa0327f1f9aab8eb4e70fde475df991c89d2969cf1e64fbaf2ab4
|
|
4
|
+
data.tar.gz: b8e55ce938ae8276b2469cef2a5f451b326fcbd94ab10a0957a0c8908307707d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 48819e9df56d4ad78afc3e4ccd6992eba3570a3e3ec39570354b8ab3777e73d4ef75f5554cfb2e3bf173b91d645c0015173c465e64712f87d4ef22ad02738b7e
|
|
7
|
+
data.tar.gz: f3e72af3ca96804db1b07f0b28002367bee84d6557f1725c0244fee839757a940c7aa89e6373952559109c273f5ec721759be89d8f98517234bd3d00bdba17ce
|
data/lib/committee/drivers.rb
CHANGED
|
@@ -61,9 +61,7 @@ module Committee
|
|
|
61
61
|
# if it is not explicitly set. See: https://github.com/interagent/committee/issues/343#issuecomment-997400329
|
|
62
62
|
opts = parser_options.dup
|
|
63
63
|
|
|
64
|
-
Committee.warn_deprecated_until_6(!opts.key?(:strict_reference_validation), 'openapi_parser will default to strict reference validation ' +
|
|
65
|
-
'from next version. Pass config `strict_reference_validation: true` (or false, if you must) ' +
|
|
66
|
-
'to quiet this warning.')
|
|
64
|
+
Committee.warn_deprecated_until_6(!opts.key?(:strict_reference_validation), 'openapi_parser will default to strict reference validation ' + 'from next version. Pass config `strict_reference_validation: true` (or false, if you must) ' + 'to quiet this warning.')
|
|
67
65
|
opts[:strict_reference_validation] ||= false
|
|
68
66
|
|
|
69
67
|
openapi = OpenAPIParser.parse_with_filepath(hash, schema_path, opts)
|
|
@@ -91,6 +89,7 @@ module Committee
|
|
|
91
89
|
|
|
92
90
|
def cache_key(schema_path, parser_options)
|
|
93
91
|
[
|
|
92
|
+
File.expand_path(schema_path),
|
|
94
93
|
File.exist?(schema_path) ? Digest::MD5.hexdigest(File.read(schema_path)) : nil,
|
|
95
94
|
parser_options.hash,
|
|
96
95
|
].join('_')
|
data/lib/committee/errors.rb
CHANGED
|
@@ -33,4 +33,15 @@ module Committee
|
|
|
33
33
|
|
|
34
34
|
class OpenAPI3Unsupported < Error
|
|
35
35
|
end
|
|
36
|
+
|
|
37
|
+
class ParameterDeserializationError < InvalidRequest
|
|
38
|
+
attr_reader :parameter_name, :style, :raw_value
|
|
39
|
+
|
|
40
|
+
def initialize(param_name, style, raw_value, message)
|
|
41
|
+
@parameter_name = param_name
|
|
42
|
+
@style = style
|
|
43
|
+
@raw_value = raw_value
|
|
44
|
+
super("Parameter '#{param_name}' (style: #{style}): #{message}")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
36
47
|
end
|
|
@@ -5,16 +5,17 @@ module Committee
|
|
|
5
5
|
class Base
|
|
6
6
|
def initialize(app, options = {})
|
|
7
7
|
@app = app
|
|
8
|
+
@options = build_options(options)
|
|
8
9
|
|
|
9
|
-
@error_class = options.
|
|
10
|
-
@error_handler = options
|
|
11
|
-
@ignore_error = options.
|
|
10
|
+
@error_class = @options.error_class
|
|
11
|
+
@error_handler = @options.error_handler
|
|
12
|
+
@ignore_error = @options.ignore_error
|
|
12
13
|
|
|
13
|
-
@raise = options
|
|
14
|
+
@raise = @options.raise_error
|
|
14
15
|
@schema = self.class.get_schema(options)
|
|
15
16
|
|
|
16
17
|
@router = @schema.build_router(options)
|
|
17
|
-
@accept_request_filter = options
|
|
18
|
+
@accept_request_filter = @options.accept_request_filter
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def call(env)
|
|
@@ -49,6 +50,11 @@ module Committee
|
|
|
49
50
|
|
|
50
51
|
private
|
|
51
52
|
|
|
53
|
+
# Subclasses should override this method to use their specific Options class
|
|
54
|
+
def build_options(options)
|
|
55
|
+
Options::Base.from(options)
|
|
56
|
+
end
|
|
57
|
+
|
|
52
58
|
def build_schema_validator(request)
|
|
53
59
|
@router.build_schema_validator(request)
|
|
54
60
|
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Committee
|
|
4
|
+
module Middleware
|
|
5
|
+
module Options
|
|
6
|
+
class Base
|
|
7
|
+
# Common options
|
|
8
|
+
attr_reader :schema
|
|
9
|
+
attr_reader :schema_path
|
|
10
|
+
attr_reader :error_class
|
|
11
|
+
attr_reader :error_handler
|
|
12
|
+
attr_reader :raise_error
|
|
13
|
+
attr_reader :ignore_error
|
|
14
|
+
attr_reader :prefix
|
|
15
|
+
attr_reader :accept_request_filter
|
|
16
|
+
|
|
17
|
+
# Schema loading options
|
|
18
|
+
attr_reader :strict_reference_validation
|
|
19
|
+
|
|
20
|
+
def initialize(options = {})
|
|
21
|
+
@original_options = options.dup.freeze
|
|
22
|
+
|
|
23
|
+
# Schema related
|
|
24
|
+
@schema = options[:schema]
|
|
25
|
+
@schema_path = options[:schema_path]
|
|
26
|
+
@strict_reference_validation = options.fetch(:strict_reference_validation, false)
|
|
27
|
+
|
|
28
|
+
# Error handling
|
|
29
|
+
@error_class = options.fetch(:error_class, Committee::ValidationError)
|
|
30
|
+
@error_handler = options[:error_handler]
|
|
31
|
+
@raise_error = options[:raise] || false
|
|
32
|
+
@ignore_error = options.fetch(:ignore_error, false)
|
|
33
|
+
|
|
34
|
+
# Routing
|
|
35
|
+
@prefix = options[:prefix]
|
|
36
|
+
@accept_request_filter = options[:accept_request_filter] || ->(_) { true }
|
|
37
|
+
|
|
38
|
+
validate!
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Allow Hash-like access for backward compatibility
|
|
42
|
+
def [](key)
|
|
43
|
+
to_h[key]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def fetch(key, default = nil)
|
|
47
|
+
to_h.fetch(key, default)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_h
|
|
51
|
+
@to_h ||= build_hash
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Create an Option object from Hash options
|
|
55
|
+
# Returns the object as-is if already an Option object
|
|
56
|
+
def self.from(options)
|
|
57
|
+
case options
|
|
58
|
+
when self
|
|
59
|
+
options
|
|
60
|
+
when Hash
|
|
61
|
+
new(options)
|
|
62
|
+
else
|
|
63
|
+
raise ArgumentError, "options must be a Hash or #{name}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def validate!
|
|
70
|
+
validate_error_class!
|
|
71
|
+
validate_error_handler!
|
|
72
|
+
validate_schema_source!
|
|
73
|
+
validate_accept_request_filter!
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def validate_error_handler!
|
|
77
|
+
return if @error_handler.nil? || @error_handler.respond_to?(:call)
|
|
78
|
+
|
|
79
|
+
raise ArgumentError, "error_handler must respond to #call"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def validate_error_class!
|
|
83
|
+
return if @error_class.is_a?(Class)
|
|
84
|
+
|
|
85
|
+
raise ArgumentError, "error_class must be a Class"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def validate_schema_source!
|
|
89
|
+
return if @schema || @schema_path
|
|
90
|
+
|
|
91
|
+
raise ArgumentError, "Committee: need option `schema` or `schema_path`"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def validate_accept_request_filter!
|
|
95
|
+
return if @accept_request_filter.respond_to?(:call)
|
|
96
|
+
|
|
97
|
+
raise ArgumentError, "accept_request_filter must respond to #call"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def build_hash
|
|
101
|
+
# Start with original options to preserve any options not explicitly handled
|
|
102
|
+
@original_options.dup.merge(schema: @schema, schema_path: @schema_path, strict_reference_validation: @strict_reference_validation, error_class: @error_class, error_handler: @error_handler, raise: @raise_error, ignore_error: @ignore_error, prefix: @prefix, accept_request_filter: @accept_request_filter)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Committee
|
|
4
|
+
module Middleware
|
|
5
|
+
module Options
|
|
6
|
+
class RequestValidation < Base
|
|
7
|
+
# RequestValidation specific options
|
|
8
|
+
attr_reader :strict
|
|
9
|
+
attr_reader :coerce_date_times
|
|
10
|
+
attr_reader :coerce_recursive
|
|
11
|
+
attr_reader :coerce_form_params
|
|
12
|
+
attr_reader :coerce_path_params
|
|
13
|
+
attr_reader :coerce_query_params
|
|
14
|
+
attr_reader :allow_form_params
|
|
15
|
+
attr_reader :allow_query_params
|
|
16
|
+
attr_reader :allow_get_body
|
|
17
|
+
attr_reader :allow_non_get_query_params
|
|
18
|
+
attr_reader :check_content_type
|
|
19
|
+
attr_reader :check_header
|
|
20
|
+
attr_reader :optimistic_json
|
|
21
|
+
attr_reader :request_body_hash_key
|
|
22
|
+
attr_reader :query_hash_key
|
|
23
|
+
attr_reader :path_hash_key
|
|
24
|
+
attr_reader :headers_key
|
|
25
|
+
attr_reader :params_key
|
|
26
|
+
attr_reader :allow_blank_structures
|
|
27
|
+
attr_reader :allow_empty_date_and_datetime
|
|
28
|
+
attr_reader :parameter_overwrite_by_rails_rule
|
|
29
|
+
attr_reader :deserialize_parameters
|
|
30
|
+
|
|
31
|
+
# Default values
|
|
32
|
+
DEFAULTS = { strict: false, coerce_recursive: true, allow_get_body: false, check_content_type: true, check_header: true, optimistic_json: false, allow_form_params: true, allow_query_params: true, allow_non_get_query_params: false, allow_blank_structures: false, allow_empty_date_and_datetime: false, parameter_overwrite_by_rails_rule: true, request_body_hash_key: "committee.request_body_hash", query_hash_key: "committee.query_hash", path_hash_key: "committee.path_hash", headers_key: "committee.headers", params_key: "committee.params" }.freeze
|
|
33
|
+
|
|
34
|
+
def initialize(options = {})
|
|
35
|
+
super(options)
|
|
36
|
+
|
|
37
|
+
# Validation related
|
|
38
|
+
@strict = options.fetch(:strict, DEFAULTS[:strict])
|
|
39
|
+
@check_content_type = options.fetch(:check_content_type, DEFAULTS[:check_content_type])
|
|
40
|
+
@check_header = options.fetch(:check_header, DEFAULTS[:check_header])
|
|
41
|
+
@optimistic_json = options.fetch(:optimistic_json, DEFAULTS[:optimistic_json])
|
|
42
|
+
|
|
43
|
+
# Type coercion related
|
|
44
|
+
@coerce_date_times = options[:coerce_date_times]
|
|
45
|
+
@coerce_recursive = options.fetch(:coerce_recursive, DEFAULTS[:coerce_recursive])
|
|
46
|
+
@coerce_form_params = options[:coerce_form_params]
|
|
47
|
+
@coerce_path_params = options[:coerce_path_params]
|
|
48
|
+
@coerce_query_params = options[:coerce_query_params]
|
|
49
|
+
|
|
50
|
+
# Parameter permission related
|
|
51
|
+
@allow_form_params = options.fetch(:allow_form_params, DEFAULTS[:allow_form_params])
|
|
52
|
+
@allow_query_params = options.fetch(:allow_query_params, DEFAULTS[:allow_query_params])
|
|
53
|
+
@allow_get_body = options.fetch(:allow_get_body, DEFAULTS[:allow_get_body])
|
|
54
|
+
@allow_non_get_query_params = options.fetch(:allow_non_get_query_params, DEFAULTS[:allow_non_get_query_params])
|
|
55
|
+
@allow_blank_structures = options.fetch(:allow_blank_structures, DEFAULTS[:allow_blank_structures])
|
|
56
|
+
@allow_empty_date_and_datetime = options.fetch(:allow_empty_date_and_datetime, DEFAULTS[:allow_empty_date_and_datetime])
|
|
57
|
+
|
|
58
|
+
# Rails compatibility
|
|
59
|
+
@parameter_overwrite_by_rails_rule = options.fetch(:parameter_overwrite_by_rails_rule, DEFAULTS[:parameter_overwrite_by_rails_rule])
|
|
60
|
+
|
|
61
|
+
# Deserialization
|
|
62
|
+
@deserialize_parameters = options[:deserialize_parameters]
|
|
63
|
+
|
|
64
|
+
# Hash keys
|
|
65
|
+
@request_body_hash_key = options.fetch(:request_body_hash_key, DEFAULTS[:request_body_hash_key])
|
|
66
|
+
@query_hash_key = options.fetch(:query_hash_key, DEFAULTS[:query_hash_key])
|
|
67
|
+
@path_hash_key = options.fetch(:path_hash_key, DEFAULTS[:path_hash_key])
|
|
68
|
+
@headers_key = options.fetch(:headers_key, DEFAULTS[:headers_key])
|
|
69
|
+
@params_key = options.fetch(:params_key, DEFAULTS[:params_key])
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def build_hash
|
|
75
|
+
super.merge(strict: @strict, coerce_date_times: @coerce_date_times, coerce_recursive: @coerce_recursive, coerce_form_params: @coerce_form_params, coerce_path_params: @coerce_path_params, coerce_query_params: @coerce_query_params, allow_form_params: @allow_form_params, allow_query_params: @allow_query_params, allow_get_body: @allow_get_body, allow_non_get_query_params: @allow_non_get_query_params, allow_blank_structures: @allow_blank_structures, allow_empty_date_and_datetime: @allow_empty_date_and_datetime, parameter_overwrite_by_rails_rule: @parameter_overwrite_by_rails_rule, deserialize_parameters: @deserialize_parameters, check_content_type: @check_content_type, check_header: @check_header, optimistic_json: @optimistic_json, request_body_hash_key: @request_body_hash_key, query_hash_key: @query_hash_key, path_hash_key: @path_hash_key, headers_key: @headers_key, params_key: @params_key)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Committee
|
|
4
|
+
module Middleware
|
|
5
|
+
module Options
|
|
6
|
+
class ResponseValidation < Base
|
|
7
|
+
# ResponseValidation specific options
|
|
8
|
+
attr_reader :strict
|
|
9
|
+
attr_reader :validate_success_only
|
|
10
|
+
attr_reader :parse_response_by_content_type
|
|
11
|
+
attr_reader :coerce_response_values
|
|
12
|
+
attr_reader :streaming_content_parsers
|
|
13
|
+
|
|
14
|
+
# Default values
|
|
15
|
+
DEFAULTS = { strict: false, validate_success_only: true, parse_response_by_content_type: true, coerce_response_values: false }.freeze
|
|
16
|
+
|
|
17
|
+
def initialize(options = {})
|
|
18
|
+
super(options)
|
|
19
|
+
|
|
20
|
+
# Validation related
|
|
21
|
+
@strict = options.fetch(:strict, DEFAULTS[:strict])
|
|
22
|
+
@validate_success_only = options.fetch(:validate_success_only, DEFAULTS[:validate_success_only])
|
|
23
|
+
@parse_response_by_content_type = options.fetch(:parse_response_by_content_type, DEFAULTS[:parse_response_by_content_type])
|
|
24
|
+
@coerce_response_values = options.fetch(:coerce_response_values, DEFAULTS[:coerce_response_values])
|
|
25
|
+
|
|
26
|
+
# Streaming
|
|
27
|
+
@streaming_content_parsers = options[:streaming_content_parsers] || {}
|
|
28
|
+
|
|
29
|
+
validate_response_validation_options!
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def validate_response_validation_options!
|
|
35
|
+
return if @streaming_content_parsers.is_a?(Hash)
|
|
36
|
+
|
|
37
|
+
raise ArgumentError, "streaming_content_parsers must be a Hash"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def build_hash
|
|
41
|
+
super.merge(strict: @strict, validate_success_only: @validate_success_only, parse_response_by_content_type: @parse_response_by_content_type, coerce_response_values: @coerce_response_values, streaming_content_parsers: @streaming_content_parsers)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -7,9 +7,9 @@ module Committee
|
|
|
7
7
|
|
|
8
8
|
def initialize(app, options = {})
|
|
9
9
|
super
|
|
10
|
-
@strict = options
|
|
10
|
+
@strict = @options.strict
|
|
11
11
|
@validate_success_only = @schema.validator_option.validate_success_only
|
|
12
|
-
@streaming_content_parsers = options
|
|
12
|
+
@streaming_content_parsers = @options.streaming_content_parsers
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def handle(request)
|
|
@@ -63,6 +63,10 @@ module Committee
|
|
|
63
63
|
|
|
64
64
|
private
|
|
65
65
|
|
|
66
|
+
def build_options(options)
|
|
67
|
+
Options::ResponseValidation.from(options)
|
|
68
|
+
end
|
|
69
|
+
|
|
66
70
|
def validate(request, status, headers, response, streaming_content_parser = nil)
|
|
67
71
|
v = build_schema_validator(request)
|
|
68
72
|
if v.link_exist? && self.class.validate?(status, validate_success_only)
|
data/lib/committee/middleware.rb
CHANGED
|
@@ -73,9 +73,7 @@ module Committee
|
|
|
73
73
|
{}
|
|
74
74
|
|
|
75
75
|
else
|
|
76
|
-
raise(%{At "#{link.method} #{link.href}" "#{schema.pointer}": no } +
|
|
77
|
-
%{"example" attribute and "null" } +
|
|
78
|
-
%{is not allowed; don't know how to generate property.})
|
|
76
|
+
raise(%{At "#{link.method} #{link.href}" "#{schema.pointer}": no } + %{"example" attribute and "null" } + %{is not allowed; don't know how to generate property.})
|
|
79
77
|
end
|
|
80
78
|
end
|
|
81
79
|
|
|
@@ -53,7 +53,7 @@ module Committee
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
begin
|
|
56
|
-
if
|
|
56
|
+
if validate?(status)
|
|
57
57
|
if @link.is_a?(Drivers::OpenAPI2::Link)
|
|
58
58
|
raise InvalidResponse, "Invalid response.#{@link.href} status code #{status} definition does not exist" if @validators[status].nil?
|
|
59
59
|
if !@validators[status].validate(data)
|
|
@@ -73,6 +73,19 @@ module Committee
|
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
def validate?(status)
|
|
77
|
+
case status
|
|
78
|
+
when 204
|
|
79
|
+
false
|
|
80
|
+
when 200..299
|
|
81
|
+
true
|
|
82
|
+
when 304
|
|
83
|
+
false
|
|
84
|
+
else
|
|
85
|
+
!validate_success_only
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
76
89
|
private
|
|
77
90
|
|
|
78
91
|
def response_media_type(response)
|
|
@@ -6,7 +6,7 @@ module Committee
|
|
|
6
6
|
class Router
|
|
7
7
|
def initialize(schema, validator_option)
|
|
8
8
|
@prefix = validator_option.prefix
|
|
9
|
-
@prefix_regexp =
|
|
9
|
+
@prefix_regexp = ::Committee::SchemaValidator.build_prefix_regexp(@prefix)
|
|
10
10
|
@schema = schema
|
|
11
11
|
|
|
12
12
|
@validator_option = validator_option
|
|
@@ -65,13 +65,7 @@ module Committee
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def request_unpack(request)
|
|
68
|
-
unpacker = Committee::RequestUnpacker.new(
|
|
69
|
-
allow_form_params: validator_option.allow_form_params,
|
|
70
|
-
allow_get_body: validator_option.allow_get_body,
|
|
71
|
-
allow_query_params: validator_option.allow_query_params,
|
|
72
|
-
allow_non_get_query_params: validator_option.allow_non_get_query_params,
|
|
73
|
-
optimistic_json: validator_option.optimistic_json,
|
|
74
|
-
)
|
|
68
|
+
unpacker = Committee::RequestUnpacker.new(allow_form_params: validator_option.allow_form_params, allow_get_body: validator_option.allow_get_body, allow_query_params: validator_option.allow_query_params, allow_non_get_query_params: validator_option.allow_non_get_query_params, optimistic_json: validator_option.optimistic_json,)
|
|
75
69
|
|
|
76
70
|
request.env[validator_option.headers_key] = unpacker.unpack_headers(request)
|
|
77
71
|
|
|
@@ -109,12 +103,7 @@ module Committee
|
|
|
109
103
|
def parameter_coerce!(request, link, coerce_key)
|
|
110
104
|
return unless link_exist?
|
|
111
105
|
|
|
112
|
-
Committee::SchemaValidator::HyperSchema::ParameterCoercer.
|
|
113
|
-
new(request.env[coerce_key],
|
|
114
|
-
link.schema,
|
|
115
|
-
coerce_date_times: validator_option.coerce_date_times,
|
|
116
|
-
coerce_recursive: validator_option.coerce_recursive).
|
|
117
|
-
call!
|
|
106
|
+
Committee::SchemaValidator::HyperSchema::ParameterCoercer.new(request.env[coerce_key], link.schema, coerce_date_times: validator_option.coerce_date_times, coerce_recursive: validator_option.coerce_recursive).call!
|
|
118
107
|
end
|
|
119
108
|
end
|
|
120
109
|
end
|
|
@@ -23,9 +23,10 @@ module Committee
|
|
|
23
23
|
|
|
24
24
|
def coerce_path_parameter(validator_option)
|
|
25
25
|
options = build_openapi_parser_path_option(validator_option)
|
|
26
|
+
validated_path_params = request_operation.validate_path_params(options)
|
|
26
27
|
return {} unless options.coerce_value
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
validated_path_params
|
|
29
30
|
rescue OpenAPIParser::OpenAPIError => e
|
|
30
31
|
raise Committee::InvalidRequest.new(e.message, original_error: e)
|
|
31
32
|
end
|
|
@@ -34,9 +35,7 @@ module Committee
|
|
|
34
35
|
def validate_response_params(status_code, headers, response_data, strict, check_header, validator_options: {})
|
|
35
36
|
response_body = OpenAPIParser::RequestOperation::ValidatableResponseBody.new(status_code, response_data, headers)
|
|
36
37
|
|
|
37
|
-
return request_operation.validate_response_body(
|
|
38
|
-
response_body,
|
|
39
|
-
response_validate_options(strict, check_header, validator_options: validator_options))
|
|
38
|
+
return request_operation.validate_response_body(response_body, response_validate_options(strict, check_header, validator_options: validator_options))
|
|
40
39
|
rescue OpenAPIParser::OpenAPIError => e
|
|
41
40
|
raise Committee::InvalidResponse.new(e.message, original_error: e)
|
|
42
41
|
end
|
|
@@ -72,12 +71,18 @@ module Committee
|
|
|
72
71
|
request_operation.operation_object&.request_body&.content&.keys || []
|
|
73
72
|
end
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
# Expose request_operation for parameter deserialization
|
|
75
|
+
# @return [OpenAPIParser::RequestOperation]
|
|
77
76
|
attr_reader :request_operation
|
|
78
77
|
|
|
79
|
-
#
|
|
80
|
-
|
|
78
|
+
# @return [Array<String>] names of query parameters defined in the schema
|
|
79
|
+
def query_parameter_names
|
|
80
|
+
return [] unless request_operation.operation_object&.parameters
|
|
81
|
+
|
|
82
|
+
request_operation.operation_object.parameters.select { |p| p.in == 'query' }.map(&:name)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
81
86
|
|
|
82
87
|
# @return [OpenAPIParser::SchemaValidator::Options]
|
|
83
88
|
def build_openapi_parser_body_option(validator_option)
|
|
@@ -86,6 +91,11 @@ module Committee
|
|
|
86
91
|
|
|
87
92
|
# @return [OpenAPIParser::SchemaValidator::Options]
|
|
88
93
|
def build_openapi_parser_path_option(validator_option)
|
|
94
|
+
build_openapi_parser_option(validator_option, validator_option.coerce_path_params)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @return [OpenAPIParser::SchemaValidator::Options]
|
|
98
|
+
def build_openapi_parser_request_parameter_option(validator_option)
|
|
89
99
|
build_openapi_parser_option(validator_option, validator_option.coerce_query_params)
|
|
90
100
|
end
|
|
91
101
|
|
|
@@ -121,6 +131,9 @@ module Committee
|
|
|
121
131
|
end
|
|
122
132
|
|
|
123
133
|
def validate_path_and_query_params(path_params, query_params, headers, validator_option)
|
|
134
|
+
path_params ||= {}
|
|
135
|
+
query_params ||= {}
|
|
136
|
+
|
|
124
137
|
# it's currently impossible to validate path params and query params separately
|
|
125
138
|
# so we have to resort to this workaround
|
|
126
139
|
|
|
@@ -129,22 +142,39 @@ module Committee
|
|
|
129
142
|
|
|
130
143
|
merged_params = query_params.merge(path_params)
|
|
131
144
|
|
|
132
|
-
request_operation.validate_request_parameter(merged_params, headers,
|
|
145
|
+
request_operation.validate_request_parameter(merged_params, headers, build_openapi_parser_request_parameter_option(validator_option))
|
|
133
146
|
|
|
134
147
|
merged_params.each do |k, v|
|
|
135
148
|
path_params[k] = v if path_keys.include?(k)
|
|
136
149
|
query_params[k] = v if query_keys.include?(k)
|
|
137
150
|
end
|
|
151
|
+
|
|
152
|
+
validate_no_unknown_query_params(query_params) if validator_option.strict_query_params
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def validate_no_unknown_query_params(query_params)
|
|
156
|
+
return if query_params.nil? || query_params.empty?
|
|
157
|
+
|
|
158
|
+
defined_params = query_parameter_names
|
|
159
|
+
unknown_params = query_params.keys - defined_params
|
|
160
|
+
|
|
161
|
+
return if unknown_params.empty?
|
|
162
|
+
|
|
163
|
+
raise Committee::InvalidRequest.new("Unknown query parameter(s): #{unknown_params.join(', ')}")
|
|
138
164
|
end
|
|
139
165
|
|
|
140
166
|
def response_validate_options(strict, check_header, validator_options: {})
|
|
141
167
|
options = { strict: strict, validate_header: check_header }
|
|
142
168
|
|
|
143
|
-
if
|
|
144
|
-
|
|
145
|
-
else
|
|
146
|
-
::OpenAPIParser::SchemaValidator::ResponseValidateOptions.new(**options)
|
|
169
|
+
if validator_options[:coerce_value]
|
|
170
|
+
options[:coerce_value] = validator_options[:coerce_value]
|
|
147
171
|
end
|
|
172
|
+
|
|
173
|
+
if validator_options[:allow_empty_date_and_datetime]
|
|
174
|
+
options[:allow_empty_date_and_datetime] = validator_options[:allow_empty_date_and_datetime]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
::OpenAPIParser::SchemaValidator::ResponseValidateOptions.new(**options)
|
|
148
178
|
end
|
|
149
179
|
end
|
|
150
180
|
end
|