committee 4.3.0 → 4.4.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.rb +1 -0
- data/lib/committee/drivers/open_api_2/driver.rb +0 -1
- data/lib/committee/middleware/request_validation.rb +0 -3
- data/lib/committee/middleware/response_validation.rb +4 -0
- data/lib/committee/request_unpacker.rb +46 -60
- data/lib/committee/schema_validator/hyper_schema.rb +33 -26
- data/lib/committee/schema_validator/open_api_3.rb +26 -20
- data/lib/committee/schema_validator/option.rb +14 -2
- data/lib/committee/test/methods.rb +10 -3
- data/lib/committee/utils.rb +28 -0
- data/test/middleware/request_validation_open_api_3_test.rb +69 -6
- data/test/middleware/request_validation_test.rb +13 -0
- data/test/middleware/response_validation_open_api_3_test.rb +2 -2
- data/test/middleware/response_validation_test.rb +8 -0
- data/test/request_unpacker_test.rb +35 -123
- data/test/schema_validator/open_api_3/operation_wrapper_test.rb +2 -2
- data/test/test/methods_new_version_test.rb +13 -4
- data/test/test/methods_test.rb +6 -4
- data/test/test_helper.rb +12 -0
- metadata +43 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 108842305271b038db8a6a49bd4ec9da83ea573a5061144484f4704a4af278df
|
|
4
|
+
data.tar.gz: 216360635fabf23f082f768594c4799ef4c71f3de4cf7ff7ffc4ac2b7fc5a72c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 417368733fb254f0211dd9ff6b273bf722db984325326816e3568ba7352da4079fb27c1405029d7191ae17e232d357041474f89ce6b7209c063911e8f4db26b3
|
|
7
|
+
data.tar.gz: '05682d9aaf2688ef511484bdaca307c1ff63dcefbdf1e4b6dcece592db7a548c5fa7ee0bb3498f4f1b903e20f9944a58790c335fc7c69251bdf0c65e33e3b19c'
|
data/lib/committee.rb
CHANGED
|
@@ -7,6 +7,10 @@ module Committee
|
|
|
7
7
|
|
|
8
8
|
def initialize(app, options = {})
|
|
9
9
|
super
|
|
10
|
+
|
|
11
|
+
unless options[:strict].nil?
|
|
12
|
+
Committee.warn_deprecated("Committee: Committee::Middleware::ResponseValidation doesn't support strict option now but we'll support this option. This change break backward compatibility so please remove strict option from ResponseValidation")
|
|
13
|
+
end
|
|
10
14
|
@validate_success_only = @schema.validator_option.validate_success_only
|
|
11
15
|
end
|
|
12
16
|
|
|
@@ -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
|
+
# reutrn 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
|
|
@@ -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?
|
|
@@ -50,16 +40,10 @@ module Committee
|
|
|
50
40
|
!link.nil?
|
|
51
41
|
end
|
|
52
42
|
|
|
53
|
-
def coerce_form_params(parameter)
|
|
54
|
-
return unless link_exist?
|
|
55
|
-
return unless link.schema
|
|
56
|
-
Committee::SchemaValidator::HyperSchema::StringParamsCoercer.new(parameter, link.schema).call!
|
|
57
|
-
end
|
|
58
|
-
|
|
59
43
|
private
|
|
60
44
|
|
|
61
45
|
def coerce_path_params
|
|
62
|
-
return unless link_exist?
|
|
46
|
+
return {} unless link_exist?
|
|
63
47
|
|
|
64
48
|
Committee::SchemaValidator::HyperSchema::StringParamsCoercer.new(param_matches, link.schema, coerce_recursive: validator_option.coerce_recursive).call!
|
|
65
49
|
param_matches
|
|
@@ -73,15 +57,38 @@ module Committee
|
|
|
73
57
|
end
|
|
74
58
|
|
|
75
59
|
def request_unpack(request)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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!
|
|
85
92
|
end
|
|
86
93
|
|
|
87
94
|
def request_schema_validation(request)
|
|
@@ -14,12 +14,7 @@ module Committee
|
|
|
14
14
|
def request_validate(request)
|
|
15
15
|
return unless link_exist?
|
|
16
16
|
|
|
17
|
-
path_params = validator_option.coerce_path_params ? coerce_path_params : {}
|
|
18
|
-
|
|
19
17
|
request_unpack(request)
|
|
20
|
-
|
|
21
|
-
request.env[validator_option.params_key]&.merge!(path_params) unless path_params.empty?
|
|
22
|
-
|
|
23
18
|
request_schema_validation(request)
|
|
24
19
|
|
|
25
20
|
copy_coerced_data_to_query_hash(request)
|
|
@@ -49,16 +44,13 @@ module Committee
|
|
|
49
44
|
!@operation_object.nil?
|
|
50
45
|
end
|
|
51
46
|
|
|
52
|
-
def coerce_form_params(_parameter)
|
|
53
|
-
# Empty because request_schema_validation checks and coerces
|
|
54
|
-
end
|
|
55
|
-
|
|
56
47
|
private
|
|
57
48
|
|
|
58
49
|
attr_reader :validator_option
|
|
59
50
|
|
|
60
51
|
def coerce_path_params
|
|
61
|
-
|
|
52
|
+
return Committee::Utils.indifferent_hash unless validator_option.coerce_path_params
|
|
53
|
+
Committee::RequestUnpacker.indifferent_params(@operation_object.coerce_path_parameter(@validator_option))
|
|
62
54
|
end
|
|
63
55
|
|
|
64
56
|
def request_schema_validation(request)
|
|
@@ -73,22 +65,36 @@ module Committee
|
|
|
73
65
|
end
|
|
74
66
|
|
|
75
67
|
def request_unpack(request)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
+
optimistic_json: validator_option.optimistic_json,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
request.env[validator_option.headers_key] = unpacker.unpack_headers(request)
|
|
76
|
+
|
|
77
|
+
request_param, is_form_params = unpacker.unpack_request_params(request)
|
|
78
|
+
request.env[validator_option.request_body_hash_key] = request_param
|
|
79
|
+
request.env[validator_option.path_hash_key] = coerce_path_params
|
|
80
|
+
|
|
81
|
+
query_param = unpacker.unpack_query_params(request)
|
|
82
|
+
|
|
83
|
+
request.env[validator_option.params_key] = Committee::Utils.indifferent_hash
|
|
84
|
+
request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(query_param))
|
|
85
|
+
request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(request.env[validator_option.request_body_hash_key]))
|
|
86
|
+
request.env[validator_option.params_key].merge!(Committee::Utils.deep_copy(request.env[validator_option.path_hash_key]))
|
|
85
87
|
end
|
|
86
88
|
|
|
87
89
|
def copy_coerced_data_to_query_hash(request)
|
|
88
90
|
return if request.env["rack.request.query_hash"].nil? || request.env["rack.request.query_hash"].empty?
|
|
89
91
|
|
|
92
|
+
query_hash_key = @validator_option.query_hash_key
|
|
93
|
+
return unless query_hash_key
|
|
94
|
+
|
|
95
|
+
request.env[query_hash_key] = {} unless request.env[query_hash_key]
|
|
90
96
|
request.env["rack.request.query_hash"].keys.each do |k|
|
|
91
|
-
request.env[
|
|
97
|
+
request.env[query_hash_key][k] = request.env[validator_option.params_key][k]
|
|
92
98
|
end
|
|
93
99
|
end
|
|
94
100
|
end
|
|
@@ -21,12 +21,24 @@ module Committee
|
|
|
21
21
|
# Non-boolean options:
|
|
22
22
|
attr_reader :headers_key,
|
|
23
23
|
:params_key,
|
|
24
|
+
:query_hash_key,
|
|
25
|
+
:request_body_hash_key,
|
|
26
|
+
:path_hash_key,
|
|
24
27
|
:prefix
|
|
25
28
|
|
|
26
29
|
def initialize(options, schema, schema_type)
|
|
27
30
|
# Non-boolean options
|
|
28
|
-
@headers_key = options[:headers_key]
|
|
29
|
-
@params_key = options[:params_key]
|
|
31
|
+
@headers_key = options[:headers_key] || "committee.headers"
|
|
32
|
+
@params_key = options[:params_key] || "committee.params"
|
|
33
|
+
@query_hash_key = if options[:query_hash_key].nil?
|
|
34
|
+
Committee.warn_deprecated('Committee: please set query_hash_key = rack.request.query_hash because we\'ll change default value in next major version.')
|
|
35
|
+
'rack.request.query_hash'
|
|
36
|
+
else
|
|
37
|
+
options.fetch(:query_hash_key)
|
|
38
|
+
end
|
|
39
|
+
@path_hash_key = options[:path_hash_key] || "committee.path_hash"
|
|
40
|
+
@request_body_hash_key = options[:request_body_hash_key] || "committee.request_body_hash"
|
|
41
|
+
|
|
30
42
|
@prefix = options[:prefix]
|
|
31
43
|
|
|
32
44
|
# Boolean options and have a common value by default
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
module Committee
|
|
4
4
|
module Test
|
|
5
5
|
module Methods
|
|
6
|
-
def assert_schema_conform
|
|
6
|
+
def assert_schema_conform(expected_status = nil)
|
|
7
7
|
assert_request_schema_confirm unless old_behavior
|
|
8
|
-
assert_response_schema_confirm
|
|
8
|
+
assert_response_schema_confirm(expected_status)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def assert_request_schema_confirm
|
|
@@ -17,7 +17,7 @@ module Committee
|
|
|
17
17
|
schema_validator.request_validate(request_object)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def assert_response_schema_confirm
|
|
20
|
+
def assert_response_schema_confirm(expected_status = nil)
|
|
21
21
|
unless schema_validator.link_exist?
|
|
22
22
|
response = "`#{request_object.request_method} #{request_object.path_info}` undefined in schema (prefix: #{committee_options[:prefix].inspect})."
|
|
23
23
|
raise Committee::InvalidResponse.new(response)
|
|
@@ -25,6 +25,13 @@ module Committee
|
|
|
25
25
|
|
|
26
26
|
status, headers, body = response_data
|
|
27
27
|
|
|
28
|
+
if expected_status.nil?
|
|
29
|
+
Committee.warn_deprecated('Pass expected response status code to check it against the corresponding schema explicitly.')
|
|
30
|
+
elsif expected_status != status
|
|
31
|
+
response = "Expected `#{expected_status}` status code, but it was `#{status}`."
|
|
32
|
+
raise Committee::InvalidResponse.new(response)
|
|
33
|
+
end
|
|
34
|
+
|
|
28
35
|
if schema_coverage
|
|
29
36
|
operation_object = router.operation_object(request_object)
|
|
30
37
|
schema_coverage&.update_response_coverage!(operation_object.original_path, operation_object.http_method, status)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Committee
|
|
4
|
+
module Utils
|
|
5
|
+
# Creates a Hash with indifferent access.
|
|
6
|
+
#
|
|
7
|
+
# (Copied from Sinatra)
|
|
8
|
+
def self.indifferent_hash
|
|
9
|
+
Hash.new { |hash,key| hash[key.to_s] if Symbol === key }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.deep_copy(from)
|
|
13
|
+
if from.is_a?(Hash)
|
|
14
|
+
h = Committee::Utils.indifferent_hash
|
|
15
|
+
from.each_pair do |k, v|
|
|
16
|
+
h[k] = deep_copy(v)
|
|
17
|
+
end
|
|
18
|
+
return h
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if from.is_a?(Array)
|
|
22
|
+
return from.map{ |v| deep_copy(v) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
return from
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -34,11 +34,27 @@ describe Committee::Middleware::RequestValidation do
|
|
|
34
34
|
params = { "datetime_string" => "2016-04-01T16:00:00.000+09:00" }
|
|
35
35
|
|
|
36
36
|
check_parameter = lambda { |env|
|
|
37
|
+
assert_equal DateTime, env['committee.query_hash']["datetime_string"].class
|
|
38
|
+
assert_equal String, env['rack.request.query_hash']["datetime_string"].class
|
|
39
|
+
[200, {}, []]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_3_schema, coerce_date_times: true, query_hash_key: "committee.query_hash")
|
|
43
|
+
|
|
44
|
+
get "/string_params_coercer", params
|
|
45
|
+
assert_equal 200, last_response.status
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "passes given a datetime and with coerce_date_times enabled on GET endpoint overwrite query_hash" do
|
|
49
|
+
params = { "datetime_string" => "2016-04-01T16:00:00.000+09:00" }
|
|
50
|
+
|
|
51
|
+
check_parameter = lambda { |env|
|
|
52
|
+
assert_equal nil, env['committee.query_hash']
|
|
37
53
|
assert_equal DateTime, env['rack.request.query_hash']["datetime_string"].class
|
|
38
54
|
[200, {}, []]
|
|
39
55
|
}
|
|
40
56
|
|
|
41
|
-
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_3_schema, coerce_date_times: true)
|
|
57
|
+
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_3_schema, coerce_date_times: true, query_hash_key: "rack.request.query_hash")
|
|
42
58
|
|
|
43
59
|
get "/string_params_coercer", params
|
|
44
60
|
assert_equal 200, last_response.status
|
|
@@ -154,7 +170,8 @@ describe Committee::Middleware::RequestValidation do
|
|
|
154
170
|
}
|
|
155
171
|
|
|
156
172
|
check_parameter = lambda { |env|
|
|
157
|
-
hash = env[
|
|
173
|
+
# hash = env["committee.query_hash"] # 5.0.x-
|
|
174
|
+
hash = env["rack.request.query_hash"]
|
|
158
175
|
assert_equal DateTime, hash['nested_array'].first['update_time'].class
|
|
159
176
|
assert_equal 1, hash['nested_array'].first['per_page']
|
|
160
177
|
|
|
@@ -331,7 +348,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
331
348
|
get "/coerce_path_params/#{not_an_integer}", nil
|
|
332
349
|
end
|
|
333
350
|
|
|
334
|
-
assert_match(/expected integer, but received String: abc/i, e.message)
|
|
351
|
+
assert_match(/expected integer, but received String: \"abc\"/i, e.message)
|
|
335
352
|
end
|
|
336
353
|
|
|
337
354
|
it "optionally raises an error" do
|
|
@@ -360,7 +377,8 @@ describe Committee::Middleware::RequestValidation do
|
|
|
360
377
|
|
|
361
378
|
it "passes through a valid request for OpenAPI3" do
|
|
362
379
|
check_parameter = lambda { |env|
|
|
363
|
-
assert_equal 3, env['
|
|
380
|
+
# assert_equal 3, env['committee.query_hash']['limit'] #5.0.x-
|
|
381
|
+
assert_equal 3, env['rack.request.query_hash']['limit'] #5.0.x-
|
|
364
382
|
[200, {}, []]
|
|
365
383
|
}
|
|
366
384
|
|
|
@@ -374,7 +392,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
374
392
|
get "/characters?limit=foo"
|
|
375
393
|
|
|
376
394
|
assert_equal 400, last_response.status
|
|
377
|
-
assert_match(/expected integer, but received String: foo/i, last_response.body)
|
|
395
|
+
assert_match(/expected integer, but received String: \\"foo\\"/i, last_response.body)
|
|
378
396
|
end
|
|
379
397
|
|
|
380
398
|
it "ignores errors when ignore_error: true" do
|
|
@@ -394,6 +412,51 @@ describe Committee::Middleware::RequestValidation do
|
|
|
394
412
|
get "/coerce_path_params/1"
|
|
395
413
|
end
|
|
396
414
|
|
|
415
|
+
it "corce string and save path hash" do
|
|
416
|
+
@app = new_rack_app_with_lambda(lambda do |env|
|
|
417
|
+
assert_equal env['committee.params']['integer'], 21
|
|
418
|
+
assert_equal env['committee.params'][:integer], 21
|
|
419
|
+
assert_equal env['committee.path_hash']['integer'], 21
|
|
420
|
+
assert_equal env['committee.path_hash'][:integer], 21
|
|
421
|
+
[204, {}, []]
|
|
422
|
+
end, schema: open_api_3_schema)
|
|
423
|
+
|
|
424
|
+
header "Content-Type", "application/json"
|
|
425
|
+
post '/parameter_option_test/21'
|
|
426
|
+
assert_equal 204, last_response.status
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
it "corce string and save request body hash" do
|
|
430
|
+
@app = new_rack_app_with_lambda(lambda do |env|
|
|
431
|
+
assert_equal env['committee.params']['integer'], 21 # use path parameter
|
|
432
|
+
assert_equal env['committee.params'][:integer], 21
|
|
433
|
+
assert_equal env['committee.request_body_hash']['integer'], 42
|
|
434
|
+
assert_equal env['committee.request_body_hash'][:integer], 42
|
|
435
|
+
[204, {}, []]
|
|
436
|
+
end, schema: open_api_3_schema)
|
|
437
|
+
|
|
438
|
+
params = {integer: 42}
|
|
439
|
+
|
|
440
|
+
header "Content-Type", "application/json"
|
|
441
|
+
post '/parameter_option_test/21', JSON.generate(params)
|
|
442
|
+
assert_equal 204, last_response.status
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
it "unpacker test" do
|
|
446
|
+
@app = new_rack_app_with_lambda(lambda do |env|
|
|
447
|
+
assert_equal env['committee.params']['integer'], 42
|
|
448
|
+
assert_equal env['committee.params'][:integer], 42
|
|
449
|
+
# overwrite by request body...
|
|
450
|
+
assert_equal env['rack.request.query_hash']['integer'], 42
|
|
451
|
+
# assert_equal env['rack.request.query_hash'][:integer], 42
|
|
452
|
+
[204, {}, []]
|
|
453
|
+
end, schema: open_api_3_schema, raise: true)
|
|
454
|
+
|
|
455
|
+
header "Content-Type", "application/x-www-form-urlencoded"
|
|
456
|
+
post '/validate?integer=21', "integer=42"
|
|
457
|
+
assert_equal 204, last_response.status
|
|
458
|
+
end
|
|
459
|
+
|
|
397
460
|
it "OpenAPI3 raise not support method" do
|
|
398
461
|
@app = new_rack_app(schema: open_api_3_schema)
|
|
399
462
|
|
|
@@ -408,7 +471,7 @@ describe Committee::Middleware::RequestValidation do
|
|
|
408
471
|
[
|
|
409
472
|
{ check_header: true, description: 'valid value', value: 1, expected: { status: 200 } },
|
|
410
473
|
{ check_header: true, description: 'missing value', value: nil, expected: { status: 400, error: 'missing required parameters: integer' } },
|
|
411
|
-
{ check_header: true, description: 'invalid value', value: 'x', expected: { status: 400, error: 'expected integer, but received String: x' } },
|
|
474
|
+
{ check_header: true, description: 'invalid value', value: 'x', expected: { status: 400, error: 'expected integer, but received String: \\"x\\"' } },
|
|
412
475
|
|
|
413
476
|
{ check_header: false, description: 'valid value', value: 1, expected: { status: 200 } },
|
|
414
477
|
{ check_header: false, description: 'missing value', value: nil, expected: { status: 200 } },
|
|
@@ -435,6 +435,19 @@ describe Committee::Middleware::RequestValidation do
|
|
|
435
435
|
assert_equal 200, last_response.status
|
|
436
436
|
end
|
|
437
437
|
|
|
438
|
+
it "corce form params" do
|
|
439
|
+
check_parameter = lambda { |env|
|
|
440
|
+
assert_equal 3, env['committee.params']['age']
|
|
441
|
+
assert_equal 3, env['committee.request_body_hash']['age']
|
|
442
|
+
[200, {}, []]
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
@app = new_rack_app_with_lambda(check_parameter, schema: open_api_2_form_schema, raise: true, allow_form_params: true, coerce_form_params: true)
|
|
446
|
+
header "Content-Type", "application/x-www-form-urlencoded"
|
|
447
|
+
post "/api/pets", "age=3&name=ab"
|
|
448
|
+
assert_equal 200, last_response.status
|
|
449
|
+
end
|
|
450
|
+
|
|
438
451
|
it "detects an invalid request for OpenAPI" do
|
|
439
452
|
@app = new_rack_app(schema: open_api_2_schema)
|
|
440
453
|
get "/api/pets?limit=foo", nil, { "HTTP_AUTH_TOKEN" => "xxx" }
|
|
@@ -125,7 +125,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
125
125
|
[
|
|
126
126
|
{ check_header: true, description: 'valid value', header: { 'integer' => 1 }, expected: { status: 200 } },
|
|
127
127
|
{ check_header: true, description: 'missing value', header: { 'integer' => nil }, expected: { error: 'headers/integer/schema does not allow null values' } },
|
|
128
|
-
{ check_header: true, description: 'invalid value', header: { 'integer' => 'x' }, expected: { error: 'headers/integer/schema expected integer, but received String: x' } },
|
|
128
|
+
{ check_header: true, description: 'invalid value', header: { 'integer' => 'x' }, expected: { error: 'headers/integer/schema expected integer, but received String: "x"' } },
|
|
129
129
|
|
|
130
130
|
{ check_header: false, description: 'valid value', header: { 'integer' => 1 }, expected: { status: 200 } },
|
|
131
131
|
{ check_header: false, description: 'missing value', header: { 'integer' => nil }, expected: { status: 200 } },
|
|
@@ -177,7 +177,7 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
177
177
|
get "/characters"
|
|
178
178
|
end
|
|
179
179
|
|
|
180
|
-
assert_match(/but received String: 1/i, e.message)
|
|
180
|
+
assert_match(/but received String: \"1\"/i, e.message)
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
it "detects an invalid response status code with validate_success_only=true" do
|
|
@@ -15,6 +15,14 @@ describe Committee::Middleware::ResponseValidation do
|
|
|
15
15
|
assert_equal 200, last_response.status
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# TODO: remove 5.0.0
|
|
19
|
+
it "passes through a valid response" do
|
|
20
|
+
# will show deprecated message
|
|
21
|
+
@app = new_rack_app(JSON.generate([ValidApp]), {}, schema: hyper_schema, strict: true)
|
|
22
|
+
get "/apps"
|
|
23
|
+
assert_equal 200, last_response.status
|
|
24
|
+
end
|
|
25
|
+
|
|
18
26
|
it "doesn't call error_handler (has a arg) when response is valid" do
|
|
19
27
|
called = false
|
|
20
28
|
pr = ->(_e) { called = true }
|
|
@@ -9,8 +9,8 @@ describe Committee::RequestUnpacker do
|
|
|
9
9
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
|
10
10
|
}
|
|
11
11
|
request = Rack::Request.new(env)
|
|
12
|
-
|
|
13
|
-
assert_equal({ "x" => "y" },
|
|
12
|
+
unpacker = Committee::RequestUnpacker.new
|
|
13
|
+
assert_equal([{ "x" => "y" }, false], unpacker.unpack_request_params(request))
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
it "unpacks JSON on Content-Type: application/vnd.api+json" do
|
|
@@ -19,8 +19,8 @@ describe Committee::RequestUnpacker do
|
|
|
19
19
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
|
20
20
|
}
|
|
21
21
|
request = Rack::Request.new(env)
|
|
22
|
-
|
|
23
|
-
assert_equal({ "x" => "y" },
|
|
22
|
+
unpacker = Committee::RequestUnpacker.new
|
|
23
|
+
assert_equal([{ "x" => "y" }, false], unpacker.unpack_request_params(request))
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
it "unpacks JSON on no Content-Type" do
|
|
@@ -28,8 +28,8 @@ describe Committee::RequestUnpacker do
|
|
|
28
28
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
|
29
29
|
}
|
|
30
30
|
request = Rack::Request.new(env)
|
|
31
|
-
|
|
32
|
-
assert_equal({ "x" => "y" },
|
|
31
|
+
unpacker = Committee::RequestUnpacker.new
|
|
32
|
+
assert_equal([{ "x" => "y" }, false], unpacker.unpack_request_params(request))
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
it "doesn't unpack JSON on application/x-ndjson" do
|
|
@@ -38,8 +38,8 @@ describe Committee::RequestUnpacker do
|
|
|
38
38
|
"rack.input" => StringIO.new('{"x":"y"}\n{"a":"b"}'),
|
|
39
39
|
}
|
|
40
40
|
request = Rack::Request.new(env)
|
|
41
|
-
|
|
42
|
-
assert_equal({},
|
|
41
|
+
unpacker = Committee::RequestUnpacker.new
|
|
42
|
+
assert_equal([{}, false], unpacker.unpack_request_params(request))
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
it "doesn't unpack JSON under other Content-Types" do
|
|
@@ -49,8 +49,8 @@ describe Committee::RequestUnpacker do
|
|
|
49
49
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
|
50
50
|
}
|
|
51
51
|
request = Rack::Request.new(env)
|
|
52
|
-
|
|
53
|
-
assert_equal({},
|
|
52
|
+
unpacker = Committee::RequestUnpacker.new
|
|
53
|
+
assert_equal([{}, false], unpacker.unpack_request_params(request))
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
|
|
@@ -61,8 +61,8 @@ describe Committee::RequestUnpacker do
|
|
|
61
61
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
|
62
62
|
}
|
|
63
63
|
request = Rack::Request.new(env)
|
|
64
|
-
|
|
65
|
-
assert_equal({ "x" => "y" },
|
|
64
|
+
unpacker = Committee::RequestUnpacker.new(optimistic_json: true)
|
|
65
|
+
assert_equal([{ "x" => "y" }, false], unpacker.unpack_request_params(request))
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
@@ -73,8 +73,8 @@ describe Committee::RequestUnpacker do
|
|
|
73
73
|
"rack.input" => StringIO.new('x=y&foo=42'),
|
|
74
74
|
}
|
|
75
75
|
request = Rack::Request.new(env)
|
|
76
|
-
|
|
77
|
-
assert_equal({},
|
|
76
|
+
unpacker = Committee::RequestUnpacker.new(optimistic_json: true)
|
|
77
|
+
assert_equal([{}, false], unpacker.unpack_request_params(request))
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
|
|
@@ -84,8 +84,8 @@ describe Committee::RequestUnpacker do
|
|
|
84
84
|
"rack.input" => StringIO.new(""),
|
|
85
85
|
}
|
|
86
86
|
request = Rack::Request.new(env)
|
|
87
|
-
|
|
88
|
-
assert_equal({},
|
|
87
|
+
unpacker = Committee::RequestUnpacker.new
|
|
88
|
+
assert_equal([{}, false], unpacker.unpack_request_params(request))
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
it "doesn't unpack form params" do
|
|
@@ -95,8 +95,8 @@ describe Committee::RequestUnpacker do
|
|
|
95
95
|
"rack.input" => StringIO.new("x=y"),
|
|
96
96
|
}
|
|
97
97
|
request = Rack::Request.new(env)
|
|
98
|
-
|
|
99
|
-
assert_equal({},
|
|
98
|
+
unpacker = Committee::RequestUnpacker.new
|
|
99
|
+
assert_equal([{}, false], unpacker.unpack_request_params(request))
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
|
|
@@ -107,96 +107,8 @@ describe Committee::RequestUnpacker do
|
|
|
107
107
|
"rack.input" => StringIO.new("x=y"),
|
|
108
108
|
}
|
|
109
109
|
request = Rack::Request.new(env)
|
|
110
|
-
|
|
111
|
-
assert_equal({ "x" => "y" },
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
it "coerces form params with coerce_form_params and a schema" do
|
|
116
|
-
%w[application/x-www-form-urlencoded multipart/form-data].each do |content_type|
|
|
117
|
-
env = {
|
|
118
|
-
"CONTENT_TYPE" => content_type,
|
|
119
|
-
"rack.input" => StringIO.new("x=1"),
|
|
120
|
-
}
|
|
121
|
-
request = Rack::Request.new(env)
|
|
122
|
-
|
|
123
|
-
options = {}
|
|
124
|
-
# TODO: delete when 5.0.0 released because default value changed
|
|
125
|
-
options[:parse_response_by_content_type] = false
|
|
126
|
-
router = hyper_schema.build_router(options)
|
|
127
|
-
validator = router.build_schema_validator(request)
|
|
128
|
-
|
|
129
|
-
schema = JsonSchema::Schema.new
|
|
130
|
-
schema.properties = { "x" => JsonSchema::Schema.new }
|
|
131
|
-
schema.properties["x"].type = ["integer"]
|
|
132
|
-
|
|
133
|
-
link_class = Struct.new(:schema)
|
|
134
|
-
link_object = link_class.new(schema)
|
|
135
|
-
|
|
136
|
-
validator.instance_variable_set(:@link, link_object)
|
|
137
|
-
|
|
138
|
-
params, _ = Committee::RequestUnpacker.new(
|
|
139
|
-
request,
|
|
140
|
-
allow_form_params: true,
|
|
141
|
-
coerce_form_params: true,
|
|
142
|
-
schema_validator: validator,
|
|
143
|
-
).call
|
|
144
|
-
assert_equal({ "x" => 1 }, params)
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
it "coerces form params with coerce_form_params and an OpenAPI3 schema" do
|
|
149
|
-
%w[application/x-www-form-urlencoded multipart/form-data].each do |content_type|
|
|
150
|
-
env = {
|
|
151
|
-
"CONTENT_TYPE" => content_type,
|
|
152
|
-
"rack.input" => StringIO.new("limit=20"),
|
|
153
|
-
"PATH_INFO" => "/characters",
|
|
154
|
-
"SCRIPT_NAME" => "",
|
|
155
|
-
"REQUEST_METHOD" => "GET",
|
|
156
|
-
}
|
|
157
|
-
request = Rack::Request.new(env)
|
|
158
|
-
|
|
159
|
-
options = {}
|
|
160
|
-
# TODO: delete when 5.0.0 released because default value changed
|
|
161
|
-
options[:parse_response_by_content_type] = false
|
|
162
|
-
router = open_api_3_schema.build_router(options)
|
|
163
|
-
validator = router.build_schema_validator(request)
|
|
164
|
-
|
|
165
|
-
params, _ = Committee::RequestUnpacker.new(
|
|
166
|
-
request,
|
|
167
|
-
allow_form_params: true,
|
|
168
|
-
coerce_form_params: true,
|
|
169
|
-
schema_validator: validator,
|
|
170
|
-
).call
|
|
171
|
-
# openapi3 not support coerce in request unpacker
|
|
172
|
-
assert_equal({ "limit" => '20' }, params)
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
it "coerces error params with coerce_form_params and a OpenAPI3 schema" do
|
|
177
|
-
%w[application/x-www-form-urlencoded multipart/form-data].each do |content_type|
|
|
178
|
-
env = {
|
|
179
|
-
"CONTENT_TYPE" => content_type,
|
|
180
|
-
"rack.input" => StringIO.new("limit=twenty"),
|
|
181
|
-
"PATH_INFO" => "/characters",
|
|
182
|
-
"SCRIPT_NAME" => "",
|
|
183
|
-
"REQUEST_METHOD" => "GET",
|
|
184
|
-
}
|
|
185
|
-
request = Rack::Request.new(env)
|
|
186
|
-
|
|
187
|
-
options = {}
|
|
188
|
-
# TODO: delete when 5.0.0 released because default value changed
|
|
189
|
-
options[:parse_response_by_content_type] = false
|
|
190
|
-
router = open_api_3_schema.build_router(options)
|
|
191
|
-
validator = router.build_schema_validator(request)
|
|
192
|
-
|
|
193
|
-
params, _ = Committee::RequestUnpacker.new(
|
|
194
|
-
request,
|
|
195
|
-
allow_form_params: true,
|
|
196
|
-
coerce_form_params: true,
|
|
197
|
-
schema_validator: validator,
|
|
198
|
-
).call
|
|
199
|
-
assert_equal({ "limit" => "twenty" }, params)
|
|
110
|
+
unpacker = Committee::RequestUnpacker.new(allow_form_params: true)
|
|
111
|
+
assert_equal([{ "x" => "y" }, true], unpacker.unpack_request_params(request))
|
|
200
112
|
end
|
|
201
113
|
end
|
|
202
114
|
|
|
@@ -208,8 +120,8 @@ describe Committee::RequestUnpacker do
|
|
|
208
120
|
"QUERY_STRING" => "a=b"
|
|
209
121
|
}
|
|
210
122
|
request = Rack::Request.new(env)
|
|
211
|
-
|
|
212
|
-
assert_equal({ "x" => "y",
|
|
123
|
+
unpacker = Committee::RequestUnpacker.new(allow_form_params: true, allow_query_params: true)
|
|
124
|
+
assert_equal([ { "x" => "y"}, true], unpacker.unpack_request_params(request))
|
|
213
125
|
end
|
|
214
126
|
end
|
|
215
127
|
|
|
@@ -219,8 +131,8 @@ describe Committee::RequestUnpacker do
|
|
|
219
131
|
"QUERY_STRING" => "a=b"
|
|
220
132
|
}
|
|
221
133
|
request = Rack::Request.new(env)
|
|
222
|
-
|
|
223
|
-
assert_equal({ "a" => "b" },
|
|
134
|
+
unpacker = Committee::RequestUnpacker.new(allow_query_params: true)
|
|
135
|
+
assert_equal({ "a" => "b" }, unpacker.unpack_query_params(request))
|
|
224
136
|
end
|
|
225
137
|
|
|
226
138
|
it "errors if JSON is not an object" do
|
|
@@ -230,7 +142,7 @@ describe Committee::RequestUnpacker do
|
|
|
230
142
|
}
|
|
231
143
|
request = Rack::Request.new(env)
|
|
232
144
|
assert_raises(Committee::BadRequest) do
|
|
233
|
-
Committee::RequestUnpacker.new(request)
|
|
145
|
+
Committee::RequestUnpacker.new.unpack_request_params(request)
|
|
234
146
|
end
|
|
235
147
|
end
|
|
236
148
|
|
|
@@ -240,8 +152,8 @@ describe Committee::RequestUnpacker do
|
|
|
240
152
|
"rack.input" => StringIO.new('{"x":"y"}'),
|
|
241
153
|
}
|
|
242
154
|
request = Rack::Request.new(env)
|
|
243
|
-
|
|
244
|
-
assert_equal({},
|
|
155
|
+
unpacker = Committee::RequestUnpacker.new
|
|
156
|
+
assert_equal([{}, false], unpacker.unpack_request_params(request))
|
|
245
157
|
end
|
|
246
158
|
|
|
247
159
|
# this is mostly here for line coverage
|
|
@@ -250,8 +162,8 @@ describe Committee::RequestUnpacker do
|
|
|
250
162
|
"rack.input" => StringIO.new('{"x":[]}'),
|
|
251
163
|
}
|
|
252
164
|
request = Rack::Request.new(env)
|
|
253
|
-
|
|
254
|
-
assert_equal({ "x" => [] },
|
|
165
|
+
unpacker = Committee::RequestUnpacker.new
|
|
166
|
+
assert_equal([{ "x" => [] }, false], unpacker.unpack_request_params(request))
|
|
255
167
|
end
|
|
256
168
|
|
|
257
169
|
it "unpacks http header" do
|
|
@@ -260,8 +172,8 @@ describe Committee::RequestUnpacker do
|
|
|
260
172
|
"rack.input" => StringIO.new(""),
|
|
261
173
|
}
|
|
262
174
|
request = Rack::Request.new(env)
|
|
263
|
-
|
|
264
|
-
assert_equal({ "FOO-BAR" => "some header value" },
|
|
175
|
+
unpacker = Committee::RequestUnpacker.new({ allow_header_params: true })
|
|
176
|
+
assert_equal({ "FOO-BAR" => "some header value" }, unpacker.unpack_headers(request))
|
|
265
177
|
end
|
|
266
178
|
|
|
267
179
|
it "includes request body when`use_get_body` is true" do
|
|
@@ -271,8 +183,8 @@ describe Committee::RequestUnpacker do
|
|
|
271
183
|
"QUERY_STRING"=>"data=value&x=aaa",
|
|
272
184
|
}
|
|
273
185
|
request = Rack::Request.new(env)
|
|
274
|
-
|
|
275
|
-
assert_equal({ '
|
|
186
|
+
unpacker = Committee::RequestUnpacker.new({ allow_query_params: true, allow_get_body: true })
|
|
187
|
+
assert_equal([{ 'x' => 1, 'y' => 2 }, false], unpacker.unpack_request_params(request))
|
|
276
188
|
end
|
|
277
189
|
|
|
278
190
|
it "doesn't include request body when `use_get_body` is false" do
|
|
@@ -282,7 +194,7 @@ describe Committee::RequestUnpacker do
|
|
|
282
194
|
"QUERY_STRING"=>"data=value&x=aaa",
|
|
283
195
|
}
|
|
284
196
|
request = Rack::Request.new(env)
|
|
285
|
-
|
|
286
|
-
assert_equal({ 'data' => 'value', 'x' => 'aaa' },
|
|
197
|
+
unpacker = Committee::RequestUnpacker.new({ allow_query_params: true, use_get_body: false })
|
|
198
|
+
assert_equal({ 'data' => 'value', 'x' => 'aaa' }, unpacker.unpack_query_params(request))
|
|
287
199
|
end
|
|
288
200
|
end
|
|
@@ -81,7 +81,7 @@ describe Committee::SchemaValidator::OpenAPI3::OperationWrapper do
|
|
|
81
81
|
operation_object.validate_request_params({"integer" => "str"}, HEADER, @validator_option)
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
assert_match(/expected integer, but received String: str/i, e.message)
|
|
84
|
+
assert_match(/expected integer, but received String: "str"/i, e.message)
|
|
85
85
|
assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
|
|
86
86
|
end
|
|
87
87
|
|
|
@@ -150,7 +150,7 @@ describe Committee::SchemaValidator::OpenAPI3::OperationWrapper do
|
|
|
150
150
|
operation_object.validate_request_params({"limit" => "a"}, HEADER, @validator_option)
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
assert_match(/expected integer, but received String: a/i, e.message)
|
|
153
|
+
assert_match(/expected integer, but received String: "a"/i, e.message)
|
|
154
154
|
assert_kind_of(OpenAPIParser::OpenAPIError, e.original_error)
|
|
155
155
|
end
|
|
156
156
|
end
|
|
@@ -39,7 +39,7 @@ describe Committee::Test::Methods do
|
|
|
39
39
|
it "passes through a valid response" do
|
|
40
40
|
@app = new_rack_app(JSON.generate([ValidApp]))
|
|
41
41
|
get "/apps"
|
|
42
|
-
assert_schema_conform
|
|
42
|
+
assert_schema_conform(200)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
it "passes with prefix" do
|
|
@@ -47,18 +47,27 @@ describe Committee::Test::Methods do
|
|
|
47
47
|
|
|
48
48
|
@app = new_rack_app(JSON.generate([ValidApp]))
|
|
49
49
|
get "/v1/apps"
|
|
50
|
-
assert_schema_conform
|
|
50
|
+
assert_schema_conform(200)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
it "detects an invalid response Content-Type" do
|
|
54
54
|
@app = new_rack_app(JSON.generate([ValidApp]), 200, {})
|
|
55
55
|
get "/apps"
|
|
56
56
|
e = assert_raises(Committee::InvalidResponse) do
|
|
57
|
-
assert_schema_conform
|
|
57
|
+
assert_schema_conform(200)
|
|
58
58
|
end
|
|
59
59
|
assert_match(/response header must be set to/i, e.message)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
it "it detects unexpected response code" do
|
|
63
|
+
@app = new_rack_app(JSON.generate([ValidApp]), 400)
|
|
64
|
+
get "/apps"
|
|
65
|
+
e = assert_raises(Committee::InvalidResponse) do
|
|
66
|
+
assert_schema_conform(200)
|
|
67
|
+
end
|
|
68
|
+
assert_match(/Expected `200` status code, but it was `400`/i, e.message)
|
|
69
|
+
end
|
|
70
|
+
|
|
62
71
|
it "detects an invalid response Content-Type but ignore because it's not success status code" do
|
|
63
72
|
@committee_options.merge!(validate_success_only: true)
|
|
64
73
|
@app = new_rack_app(JSON.generate([ValidApp]), 400, {})
|
|
@@ -70,7 +79,7 @@ describe Committee::Test::Methods do
|
|
|
70
79
|
@app = new_rack_app(JSON.generate([ValidApp]), 400, {})
|
|
71
80
|
get "/apps"
|
|
72
81
|
e = assert_raises(Committee::InvalidResponse) do
|
|
73
|
-
assert_schema_conform
|
|
82
|
+
assert_schema_conform(400)
|
|
74
83
|
end
|
|
75
84
|
assert_match(/response header must be set to/i, e.message)
|
|
76
85
|
end
|
data/test/test/methods_test.rb
CHANGED
|
@@ -46,14 +46,14 @@ describe Committee::Test::Methods do
|
|
|
46
46
|
it "passes through a valid response" do
|
|
47
47
|
@app = new_rack_app(JSON.generate([ValidApp]))
|
|
48
48
|
get "/apps"
|
|
49
|
-
assert_schema_conform
|
|
49
|
+
assert_schema_conform(200)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
it "detects an invalid response Content-Type" do
|
|
53
53
|
@app = new_rack_app(JSON.generate([ValidApp]), {})
|
|
54
54
|
get "/apps"
|
|
55
55
|
e = assert_raises(Committee::InvalidResponse) do
|
|
56
|
-
assert_schema_conform
|
|
56
|
+
assert_schema_conform(200)
|
|
57
57
|
end
|
|
58
58
|
assert_match(/response header must be set to/i, e.message)
|
|
59
59
|
end
|
|
@@ -64,7 +64,8 @@ describe Committee::Test::Methods do
|
|
|
64
64
|
_, err = capture_io do
|
|
65
65
|
assert_schema_conform
|
|
66
66
|
end
|
|
67
|
-
assert_match(/\[DEPRECATION\]/i, err)
|
|
67
|
+
assert_match(/\[DEPRECATION\] Now assert_schema_conform check response schema only/i, err)
|
|
68
|
+
assert_match(/\[DEPRECATION\] Pass expected response status code/i, err)
|
|
68
69
|
end
|
|
69
70
|
end
|
|
70
71
|
|
|
@@ -161,7 +162,8 @@ describe Committee::Test::Methods do
|
|
|
161
162
|
_, err = capture_io do
|
|
162
163
|
assert_schema_conform
|
|
163
164
|
end
|
|
164
|
-
assert_match(/\[DEPRECATION\]/i, err)
|
|
165
|
+
assert_match(/\[DEPRECATION\] Now assert_schema_conform check response schema only/i, err)
|
|
166
|
+
assert_match(/\[DEPRECATION\] Pass expected response status code/i, err)
|
|
165
167
|
end
|
|
166
168
|
end
|
|
167
169
|
|
data/test/test_helper.rb
CHANGED
|
@@ -55,6 +55,10 @@ def open_api_2_schema
|
|
|
55
55
|
@open_api_2_schema ||= Committee::Drivers.load_from_file(open_api_2_schema_path)
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
def open_api_2_form_schema
|
|
59
|
+
@open_api_2_form_schema ||= Committee::Drivers.load_from_file(open_api_2_form_schema_path)
|
|
60
|
+
end
|
|
61
|
+
|
|
58
62
|
def open_api_3_schema
|
|
59
63
|
@open_api_3_schema ||= Committee::Drivers.load_from_file(open_api_3_schema_path)
|
|
60
64
|
end
|
|
@@ -73,6 +77,10 @@ def open_api_2_data
|
|
|
73
77
|
JSON.parse(File.read(open_api_2_schema_path))
|
|
74
78
|
end
|
|
75
79
|
|
|
80
|
+
def open_api_2_form_data
|
|
81
|
+
JSON.parse(File.read(open_api_2_form_schema_path))
|
|
82
|
+
end
|
|
83
|
+
|
|
76
84
|
def open_api_3_data
|
|
77
85
|
YAML.load_file(open_api_3_schema_path)
|
|
78
86
|
end
|
|
@@ -85,6 +93,10 @@ def open_api_2_schema_path
|
|
|
85
93
|
"./test/data/openapi2/petstore-expanded.json"
|
|
86
94
|
end
|
|
87
95
|
|
|
96
|
+
def open_api_2_form_schema_path
|
|
97
|
+
"./test/data/openapi2/petstore-expanded-form.json"
|
|
98
|
+
end
|
|
99
|
+
|
|
88
100
|
def open_api_3_schema_path
|
|
89
101
|
"./test/data/openapi3/normal.yaml"
|
|
90
102
|
end
|
metadata
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: committee
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brandur
|
|
8
8
|
- geemus (Wesley Beary)
|
|
9
9
|
- ota42y
|
|
10
|
-
autorequire:
|
|
10
|
+
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date:
|
|
13
|
+
date: 2021-06-12 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: json_schema
|
|
@@ -53,6 +53,9 @@ dependencies:
|
|
|
53
53
|
- - ">="
|
|
54
54
|
- !ruby/object:Gem::Version
|
|
55
55
|
version: 0.11.1
|
|
56
|
+
- - "<"
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '1.0'
|
|
56
59
|
type: :runtime
|
|
57
60
|
prerelease: false
|
|
58
61
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -60,6 +63,9 @@ dependencies:
|
|
|
60
63
|
- - ">="
|
|
61
64
|
- !ruby/object:Gem::Version
|
|
62
65
|
version: 0.11.1
|
|
66
|
+
- - "<"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.0'
|
|
63
69
|
- !ruby/object:Gem::Dependency
|
|
64
70
|
name: minitest
|
|
65
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -146,6 +152,20 @@ dependencies:
|
|
|
146
152
|
version: '0'
|
|
147
153
|
- !ruby/object:Gem::Dependency
|
|
148
154
|
name: rubocop
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - "<"
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: 1.13.0
|
|
160
|
+
type: :development
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - "<"
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: 1.13.0
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: rubocop-performance
|
|
149
169
|
requirement: !ruby/object:Gem::Requirement
|
|
150
170
|
requirements:
|
|
151
171
|
- - ">="
|
|
@@ -159,7 +179,21 @@ dependencies:
|
|
|
159
179
|
- !ruby/object:Gem::Version
|
|
160
180
|
version: '0'
|
|
161
181
|
- !ruby/object:Gem::Dependency
|
|
162
|
-
name: rubocop-
|
|
182
|
+
name: rubocop-minitest
|
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
|
184
|
+
requirements:
|
|
185
|
+
- - ">="
|
|
186
|
+
- !ruby/object:Gem::Version
|
|
187
|
+
version: '0'
|
|
188
|
+
type: :development
|
|
189
|
+
prerelease: false
|
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
191
|
+
requirements:
|
|
192
|
+
- - ">="
|
|
193
|
+
- !ruby/object:Gem::Version
|
|
194
|
+
version: '0'
|
|
195
|
+
- !ruby/object:Gem::Dependency
|
|
196
|
+
name: rubocop-rake
|
|
163
197
|
requirement: !ruby/object:Gem::Requirement
|
|
164
198
|
requirements:
|
|
165
199
|
- - ">="
|
|
@@ -186,7 +220,7 @@ dependencies:
|
|
|
186
220
|
- - ">="
|
|
187
221
|
- !ruby/object:Gem::Version
|
|
188
222
|
version: '0'
|
|
189
|
-
description:
|
|
223
|
+
description:
|
|
190
224
|
email:
|
|
191
225
|
- brandur@mutelight.org
|
|
192
226
|
- geemus+github@gmail.com
|
|
@@ -239,6 +273,7 @@ files:
|
|
|
239
273
|
- lib/committee/schema_validator/option.rb
|
|
240
274
|
- lib/committee/test/methods.rb
|
|
241
275
|
- lib/committee/test/schema_coverage.rb
|
|
276
|
+
- lib/committee/utils.rb
|
|
242
277
|
- lib/committee/validation_error.rb
|
|
243
278
|
- test/bin/committee_stub_test.rb
|
|
244
279
|
- test/bin_test.rb
|
|
@@ -276,7 +311,7 @@ homepage: https://github.com/interagent/committee
|
|
|
276
311
|
licenses:
|
|
277
312
|
- MIT
|
|
278
313
|
metadata: {}
|
|
279
|
-
post_install_message:
|
|
314
|
+
post_install_message:
|
|
280
315
|
rdoc_options: []
|
|
281
316
|
require_paths:
|
|
282
317
|
- lib
|
|
@@ -291,8 +326,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
291
326
|
- !ruby/object:Gem::Version
|
|
292
327
|
version: '0'
|
|
293
328
|
requirements: []
|
|
294
|
-
rubygems_version: 3.
|
|
295
|
-
signing_key:
|
|
329
|
+
rubygems_version: 3.2.3
|
|
330
|
+
signing_key:
|
|
296
331
|
specification_version: 4
|
|
297
332
|
summary: A collection of Rack middleware to support JSON Schema.
|
|
298
333
|
test_files: []
|