committee 3.0.0.alpha → 3.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/committee/drivers/hyper_schema.rb +8 -2
- data/lib/committee/drivers/open_api_2.rb +7 -2
- data/lib/committee/drivers/open_api_3.rb +9 -4
- data/lib/committee/drivers.rb +46 -0
- data/lib/committee/middleware/base.rb +9 -11
- data/lib/committee/middleware/response_validation.rb +8 -5
- data/lib/committee/request_unpacker.rb +4 -1
- data/lib/committee/{parameter_coercer.rb → schema_validator/hyper_schema/parameter_coercer.rb} +1 -2
- data/lib/committee/schema_validator/hyper_schema/request_validator.rb +1 -5
- data/lib/committee/schema_validator/hyper_schema/response_validator.rb +3 -9
- data/lib/committee/schema_validator/hyper_schema/router.rb +1 -1
- data/lib/committee/schema_validator/hyper_schema.rb +5 -4
- data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +46 -19
- data/lib/committee/schema_validator/open_api_3/request_validator.rb +14 -4
- data/lib/committee/schema_validator/open_api_3/response_validator.rb +9 -20
- data/lib/committee/schema_validator/open_api_3/router.rb +15 -4
- data/lib/committee/schema_validator/open_api_3.rb +14 -5
- data/lib/committee/schema_validator/option.rb +2 -5
- data/lib/committee/schema_validator/schema_validator.rb +15 -0
- data/lib/committee/test/methods.rb +3 -5
- data/lib/committee.rb +3 -1
- data/test/drivers/open_api_3_test.rb +2 -1
- data/test/drivers_test.rb +69 -0
- data/test/middleware/base_test.rb +20 -5
- data/test/middleware/request_validation_open_api_3_test.rb +136 -259
- data/test/middleware/request_validation_test.rb +18 -3
- data/test/middleware/response_validation_open_api_3_test.rb +83 -16
- data/test/middleware/response_validation_test.rb +19 -7
- data/test/middleware/stub_test.rb +1 -1
- data/test/{parameter_coercer_test.rb → schema_validator/hyper_schema/parameter_coercer_test.rb} +3 -3
- data/test/schema_validator/hyper_schema/router_test.rb +6 -0
- data/test/schema_validator/open_api_3/operation_wrapper_test.rb +29 -13
- data/test/schema_validator/open_api_3/request_validator_test.rb +31 -132
- data/test/schema_validator/open_api_3/response_validator_test.rb +19 -5
- data/test/test/methods_new_version_test.rb +19 -3
- data/test/test/methods_test.rb +15 -8
- data/test/test_helper.rb +15 -16
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5019abacff975df04498c8e2f04d04601dd6a99d1f3a621acafa6cf0873170fa
|
4
|
+
data.tar.gz: 0a08b6be4a680675cd985abf1fad733e55eee9a47368f852148cf732de5f68ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 179c89e72e306d1b79317a98ac7d3a1284edbecaaf13a04f9c7ab168724381988dfde7b4d45287c5f44d54f62d02296e959621f56038a55492e841e0d772021c
|
7
|
+
data.tar.gz: 9668d280fa56807e4101ac150dd04fdc280c0e0db4e04d17bbad26a031dca914388320f1dc7aaaefaeb6d4e41e11a01b51eb0f50254181a298df275eb6e50063
|
@@ -21,6 +21,10 @@ module Committee::Drivers
|
|
21
21
|
false
|
22
22
|
end
|
23
23
|
|
24
|
+
def default_validate_success_only
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
24
28
|
def name
|
25
29
|
:hyper_schema
|
26
30
|
end
|
@@ -119,9 +123,11 @@ module Committee::Drivers
|
|
119
123
|
|
120
124
|
attr_accessor :routes
|
121
125
|
|
126
|
+
attr_reader :validator_option
|
127
|
+
|
122
128
|
def build_router(options)
|
123
|
-
validator_option = Committee::SchemaValidator::Option.new(options, self, :hyper_schema)
|
124
|
-
Committee::SchemaValidator::HyperSchema::Router.new(self, validator_option)
|
129
|
+
@validator_option = Committee::SchemaValidator::Option.new(options, self, :hyper_schema)
|
130
|
+
Committee::SchemaValidator::HyperSchema::Router.new(self, @validator_option)
|
125
131
|
end
|
126
132
|
end
|
127
133
|
|
@@ -21,6 +21,10 @@ module Committee::Drivers
|
|
21
21
|
false
|
22
22
|
end
|
23
23
|
|
24
|
+
def default_validate_success_only
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
24
28
|
def name
|
25
29
|
:open_api_2
|
26
30
|
end
|
@@ -208,10 +212,11 @@ module Committee::Drivers
|
|
208
212
|
attr_accessor :definitions
|
209
213
|
attr_accessor :produces
|
210
214
|
attr_accessor :routes
|
215
|
+
attr_reader :validator_option
|
211
216
|
|
212
217
|
def build_router(options)
|
213
|
-
validator_option = Committee::SchemaValidator::Option.new(options, self, :hyper_schema)
|
214
|
-
Committee::SchemaValidator::HyperSchema::Router.new(self, validator_option)
|
218
|
+
@validator_option = Committee::SchemaValidator::Option.new(options, self, :hyper_schema)
|
219
|
+
Committee::SchemaValidator::HyperSchema::Router.new(self, @validator_option)
|
215
220
|
end
|
216
221
|
end
|
217
222
|
|
@@ -21,21 +21,26 @@ module Committee::Drivers
|
|
21
21
|
true
|
22
22
|
end
|
23
23
|
|
24
|
+
def default_validate_success_only
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
24
28
|
def name
|
25
29
|
:open_api_3
|
26
30
|
end
|
27
31
|
|
28
|
-
# TODO: check it's need?
|
29
32
|
def schema_class
|
30
33
|
Committee::Drivers::OpenAPI3::Schema
|
31
34
|
end
|
32
35
|
|
36
|
+
# @return [Committee::Drivers::OpenAPI3::Schema]
|
33
37
|
def parse(open_api)
|
34
|
-
|
38
|
+
schema_class.new(self, open_api)
|
35
39
|
end
|
36
40
|
|
37
41
|
class Schema < Committee::Drivers::Schema
|
38
42
|
attr_reader :open_api
|
43
|
+
attr_reader :validator_option
|
39
44
|
|
40
45
|
# @!attribute [r] open_api
|
41
46
|
# @return [OpenAPIParser::Schemas::OpenAPI]
|
@@ -54,8 +59,8 @@ module Committee::Drivers
|
|
54
59
|
end
|
55
60
|
|
56
61
|
def build_router(options)
|
57
|
-
validator_option = Committee::SchemaValidator::Option.new(options, self, :open_api_3)
|
58
|
-
Committee::SchemaValidator::OpenAPI3::Router.new(self, validator_option)
|
62
|
+
@validator_option = Committee::SchemaValidator::Option.new(options, self, :open_api_3)
|
63
|
+
Committee::SchemaValidator::OpenAPI3::Router.new(self, @validator_option)
|
59
64
|
end
|
60
65
|
|
61
66
|
# OpenAPI3 only
|
data/lib/committee/drivers.rb
CHANGED
@@ -15,6 +15,52 @@ module Committee
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
# load and build drive from JSON file
|
19
|
+
# @param [String] filepath
|
20
|
+
# @return [Committee::Driver]
|
21
|
+
def self.load_from_json(filepath)
|
22
|
+
load_from_data(JSON.parse(File.read(filepath)))
|
23
|
+
end
|
24
|
+
|
25
|
+
# load and build drive from YAML file
|
26
|
+
# @param [String] filepath
|
27
|
+
# @return [Committee::Driver]
|
28
|
+
def self.load_from_yaml(filepath)
|
29
|
+
load_from_data(YAML.load_file(filepath))
|
30
|
+
end
|
31
|
+
|
32
|
+
# load and build drive from file
|
33
|
+
# @param [String] filepath
|
34
|
+
# @return [Committee::Driver]
|
35
|
+
def self.load_from_file(filepath)
|
36
|
+
case File.extname(filepath)
|
37
|
+
when '.json'
|
38
|
+
load_from_json(filepath)
|
39
|
+
when '.yaml', '.yml'
|
40
|
+
load_from_yaml(filepath)
|
41
|
+
else
|
42
|
+
raise "committee filepath option support '.yaml', '.yml', '.json' files only"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# load and build drive from Hash object
|
47
|
+
# @param [Hash] hash
|
48
|
+
# @return [Committee::Driver]
|
49
|
+
def self.load_from_data(hash)
|
50
|
+
if hash['openapi'] == '3.0.0'
|
51
|
+
parser = OpenAPIParser.parse(hash)
|
52
|
+
return Committee::Drivers::OpenAPI3.new.parse(parser)
|
53
|
+
end
|
54
|
+
|
55
|
+
driver = if hash['swagger'] == '2.0'
|
56
|
+
Committee::Drivers::OpenAPI2.new
|
57
|
+
else
|
58
|
+
Committee::Drivers::HyperSchema.new
|
59
|
+
end
|
60
|
+
|
61
|
+
driver.parse(hash)
|
62
|
+
end
|
63
|
+
|
18
64
|
# Driver is a base class for driver implementations.
|
19
65
|
class Driver
|
20
66
|
# Whether parameters that were form-encoded will be coerced by default.
|
@@ -23,22 +23,20 @@ module Committee::Middleware
|
|
23
23
|
|
24
24
|
class << self
|
25
25
|
def get_schema(options)
|
26
|
-
open_api_3 = options[:open_api_3]
|
27
|
-
return open_api_3 if open_api_3
|
28
|
-
|
29
26
|
schema = options[:schema]
|
27
|
+
unless schema
|
28
|
+
schema = Committee::Drivers::load_from_file(options[:filepath]) if options[:filepath]
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
# something else non-standard in.
|
34
|
-
if !schema.is_a?(Committee::Drivers::Schema)
|
35
|
-
raise ArgumentError, "Committee: schema expected to be an instance of Committee::Drivers::Schema."
|
36
|
-
end
|
30
|
+
raise(ArgumentError, "Committee: need option `schema` or `filepath`") unless schema
|
31
|
+
end
|
37
32
|
|
38
|
-
|
33
|
+
# Expect the type we want by now. If we don't have it, the user passed
|
34
|
+
# something else non-standard in.
|
35
|
+
if !schema.is_a?(Committee::Drivers::Schema)
|
36
|
+
raise ArgumentError, "Committee: schema expected to be an instance of Committee::Drivers::Schema."
|
39
37
|
end
|
40
38
|
|
41
|
-
|
39
|
+
return schema
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module Committee::Middleware
|
2
2
|
class ResponseValidation < Base
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :validate_success_only
|
4
4
|
|
5
5
|
def initialize(app, options = {})
|
6
6
|
super
|
7
|
-
@
|
7
|
+
@validate_success_only = @schema.validator_option.validate_success_only
|
8
|
+
|
8
9
|
@error_handler = options[:error_handler]
|
9
10
|
end
|
10
11
|
|
@@ -12,7 +13,7 @@ module Committee::Middleware
|
|
12
13
|
status, headers, response = @app.call(request.env)
|
13
14
|
|
14
15
|
v = build_schema_validator(request)
|
15
|
-
v.response_validate(status, headers, response) if v.link_exist? && validate?(status)
|
16
|
+
v.response_validate(status, headers, response) if v.link_exist? && self.class.validate?(status, validate_success_only)
|
16
17
|
|
17
18
|
[status, headers, response]
|
18
19
|
rescue Committee::InvalidResponse
|
@@ -25,8 +26,10 @@ module Committee::Middleware
|
|
25
26
|
@error_class.new(500, :invalid_response, "Response wasn't valid JSON.").render
|
26
27
|
end
|
27
28
|
|
28
|
-
|
29
|
-
|
29
|
+
class << self
|
30
|
+
def validate?(status, validate_success_only)
|
31
|
+
status != 204 && (!validate_success_only || (200...300).include?(status))
|
32
|
+
end
|
30
33
|
end
|
31
34
|
end
|
32
35
|
end
|
@@ -88,11 +88,14 @@ module Committee
|
|
88
88
|
|
89
89
|
def headers
|
90
90
|
env = @request.env
|
91
|
-
env.keys.grep(/HTTP_/).inject({}) do |headers, key|
|
91
|
+
base = env.keys.grep(/HTTP_/).inject({}) do |headers, key|
|
92
92
|
headerized_key = key.gsub(/^HTTP_/, '').gsub(/_/, '-')
|
93
93
|
headers[headerized_key] = env[key]
|
94
94
|
headers
|
95
95
|
end
|
96
|
+
|
97
|
+
base.merge!('Content-Type' => env['CONTENT_TYPE']) if env['CONTENT_TYPE']
|
98
|
+
base
|
96
99
|
end
|
97
100
|
end
|
98
101
|
end
|
@@ -27,12 +27,8 @@ module Committee
|
|
27
27
|
|
28
28
|
private
|
29
29
|
|
30
|
-
def request_media_type(request)
|
31
|
-
request.content_type.to_s.split(";").first.to_s
|
32
|
-
end
|
33
|
-
|
34
30
|
def check_content_type!(request, data)
|
35
|
-
content_type = request_media_type(request)
|
31
|
+
content_type = ::Committee::SchemaValidator.request_media_type(request)
|
36
32
|
if content_type && @link.enc_type && !empty_request?(request)
|
37
33
|
unless Rack::Mime.match?(content_type, @link.enc_type)
|
38
34
|
raise Committee::InvalidRequest,
|
@@ -1,20 +1,14 @@
|
|
1
1
|
module Committee
|
2
2
|
class SchemaValidator::HyperSchema::ResponseValidator
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :validate_success_only
|
4
4
|
|
5
5
|
def initialize(link, options = {})
|
6
6
|
@link = link
|
7
|
-
@
|
7
|
+
@validate_success_only = options[:validate_success_only]
|
8
8
|
|
9
9
|
@validator = JsonSchema::Validator.new(target_schema(link))
|
10
10
|
end
|
11
11
|
|
12
|
-
def self.validate?(status, options = {})
|
13
|
-
validate_errors = options[:validate_errors]
|
14
|
-
|
15
|
-
status != 204 and validate_errors || (200...300).include?(status)
|
16
|
-
end
|
17
|
-
|
18
12
|
def call(status, headers, data)
|
19
13
|
unless status == 204 # 204 No Content
|
20
14
|
response = Rack::Response.new(data, status, headers)
|
@@ -41,7 +35,7 @@ module Committee
|
|
41
35
|
return if data == nil
|
42
36
|
end
|
43
37
|
|
44
|
-
if
|
38
|
+
if Committee::Middleware::ResponseValidation.validate?(status, validate_success_only) && !@validator.validate(data)
|
45
39
|
errors = JsonSchema::SchemaError.aggregate(@validator.errors).join("\n")
|
46
40
|
raise InvalidResponse, "Invalid response.\n\n#{errors}"
|
47
41
|
end
|
@@ -25,15 +25,15 @@ class Committee::SchemaValidator
|
|
25
25
|
parameter_coerce!(request, link, "rack.request.query_hash") if link_exist? && !request.GET.nil? && !link.schema.nil?
|
26
26
|
end
|
27
27
|
|
28
|
-
def response_validate(status, headers, response)
|
28
|
+
def response_validate(status, headers, response, _test_method = false)
|
29
29
|
return unless link_exist?
|
30
30
|
|
31
31
|
full_body = ""
|
32
32
|
response.each do |chunk|
|
33
33
|
full_body << chunk
|
34
34
|
end
|
35
|
-
data = JSON.parse(full_body)
|
36
|
-
Committee::SchemaValidator::HyperSchema::ResponseValidator.new(link,
|
35
|
+
data = full_body.empty? ? {} : JSON.parse(full_body)
|
36
|
+
Committee::SchemaValidator::HyperSchema::ResponseValidator.new(link, validate_success_only: validator_option.validate_success_only).call(status, headers, data)
|
37
37
|
end
|
38
38
|
|
39
39
|
def link_exist?
|
@@ -41,6 +41,7 @@ class Committee::SchemaValidator
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def coerce_form_params(parameter)
|
44
|
+
return unless link_exist?
|
44
45
|
return unless link.schema
|
45
46
|
Committee::SchemaValidator::HyperSchema::StringParamsCoercer.new(parameter, link.schema).call!
|
46
47
|
end
|
@@ -81,7 +82,7 @@ class Committee::SchemaValidator
|
|
81
82
|
def parameter_coerce!(request, link, coerce_key)
|
82
83
|
return unless link_exist?
|
83
84
|
|
84
|
-
Committee::ParameterCoercer.
|
85
|
+
Committee::SchemaValidator::HyperSchema::ParameterCoercer.
|
85
86
|
new(request.env[coerce_key],
|
86
87
|
link.schema,
|
87
88
|
coerce_date_times: validator_option.coerce_date_times,
|
@@ -20,31 +20,34 @@ module Committee
|
|
20
20
|
request_operation.validate_path_params(options)
|
21
21
|
end
|
22
22
|
|
23
|
-
def coerce_request_parameter(params, validator_option)
|
23
|
+
def coerce_request_parameter(params, headers, validator_option)
|
24
24
|
options = build_openapi_parser_get_option(validator_option)
|
25
25
|
return unless options.coerce_value
|
26
26
|
|
27
|
-
request_operation.validate_request_parameter(params, options)
|
27
|
+
request_operation.validate_request_parameter(params, headers, options)
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
# @param [Boolean] strict when not content_type or status code definition, raise error
|
31
|
+
def validate_response_params(status_code, headers, response_data, strict, check_header)
|
32
|
+
request_body = OpenAPIParser::RequestOperation::ValidatableResponseBody.new(status_code, response_data, headers)
|
33
|
+
|
34
|
+
return request_operation.validate_response_body(request_body, response_validate_options(strict, check_header))
|
32
35
|
rescue OpenAPIParser::OpenAPIError => e
|
33
|
-
raise Committee::
|
36
|
+
raise Committee::InvalidResponse.new(e.message)
|
34
37
|
end
|
35
38
|
|
36
|
-
def validate_request_params(params, validator_option)
|
39
|
+
def validate_request_params(params, headers, validator_option)
|
37
40
|
ret, err = case request_operation.http_method
|
38
41
|
when 'get'
|
39
|
-
validate_get_request_params(params, validator_option)
|
42
|
+
validate_get_request_params(params, headers, validator_option)
|
40
43
|
when 'post'
|
41
|
-
validate_post_request_params(params, validator_option)
|
44
|
+
validate_post_request_params(params, headers, validator_option)
|
42
45
|
when 'put'
|
43
|
-
validate_post_request_params(params, validator_option)
|
46
|
+
validate_post_request_params(params, headers, validator_option)
|
44
47
|
when 'patch'
|
45
|
-
validate_post_request_params(params, validator_option)
|
48
|
+
validate_post_request_params(params, headers, validator_option)
|
46
49
|
when 'delete'
|
47
|
-
validate_get_request_params(params, validator_option)
|
50
|
+
validate_get_request_params(params, headers, validator_option)
|
48
51
|
else
|
49
52
|
raise "Committee OpenAPI3 not support #{request_operation.http_method} method"
|
50
53
|
end
|
@@ -52,6 +55,16 @@ module Committee
|
|
52
55
|
ret
|
53
56
|
end
|
54
57
|
|
58
|
+
def valid_request_content_type?(content_type)
|
59
|
+
if (request_body = request_operation.operation_object&.request_body)
|
60
|
+
!request_body.select_media_type(content_type).nil?
|
61
|
+
else
|
62
|
+
# if not exist request body object, all content_type allow.
|
63
|
+
# because request body object required content field so when it exists there're content type definition.
|
64
|
+
true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
55
68
|
private
|
56
69
|
|
57
70
|
attr_reader :request_operation
|
@@ -63,36 +76,50 @@ module Committee
|
|
63
76
|
def build_openapi_parser_path_option(validator_option)
|
64
77
|
coerce_value = validator_option.coerce_path_params
|
65
78
|
datetime_coerce_class = validator_option.coerce_date_times ? DateTime : nil
|
66
|
-
|
79
|
+
validate_header = validator_option.check_header
|
80
|
+
OpenAPIParser::SchemaValidator::Options.new(coerce_value: coerce_value,
|
81
|
+
datetime_coerce_class: datetime_coerce_class,
|
82
|
+
validate_header: validate_header)
|
67
83
|
end
|
68
84
|
|
69
85
|
# @return [OpenAPIParser::SchemaValidator::Options]
|
70
86
|
def build_openapi_parser_post_option(validator_option)
|
71
87
|
coerce_value = validator_option.coerce_form_params
|
72
88
|
datetime_coerce_class = validator_option.coerce_date_times ? DateTime : nil
|
73
|
-
|
89
|
+
validate_header = validator_option.check_header
|
90
|
+
OpenAPIParser::SchemaValidator::Options.new(coerce_value: coerce_value,
|
91
|
+
datetime_coerce_class: datetime_coerce_class,
|
92
|
+
validate_header: validate_header)
|
74
93
|
end
|
75
94
|
|
76
95
|
# @return [OpenAPIParser::SchemaValidator::Options]
|
77
96
|
def build_openapi_parser_get_option(validator_option)
|
78
97
|
coerce_value = validator_option.coerce_query_params
|
79
98
|
datetime_coerce_class = validator_option.coerce_date_times ? DateTime : nil
|
80
|
-
|
99
|
+
validate_header = validator_option.check_header
|
100
|
+
OpenAPIParser::SchemaValidator::Options.new(coerce_value: coerce_value,
|
101
|
+
datetime_coerce_class: datetime_coerce_class,
|
102
|
+
validate_header: validate_header)
|
81
103
|
end
|
82
104
|
|
83
|
-
def validate_get_request_params(params, validator_option)
|
105
|
+
def validate_get_request_params(params, headers, validator_option)
|
84
106
|
# bad performance because when we coerce value, same check
|
85
|
-
request_operation.validate_request_parameter(params, build_openapi_parser_get_option(validator_option))
|
107
|
+
request_operation.validate_request_parameter(params, headers, build_openapi_parser_get_option(validator_option))
|
86
108
|
rescue OpenAPIParser::OpenAPIError => e
|
87
109
|
raise Committee::InvalidRequest.new(e.message)
|
88
110
|
end
|
89
111
|
|
90
|
-
def validate_post_request_params(params, validator_option)
|
112
|
+
def validate_post_request_params(params, headers, validator_option)
|
113
|
+
content_type = headers['Content-Type'].to_s.split(";").first.to_s
|
114
|
+
|
91
115
|
# bad performance because when we coerce value, same check
|
92
|
-
|
93
|
-
return request_operation.validate_request_body('application/json', params, build_openapi_parser_post_option(validator_option))
|
116
|
+
return request_operation.validate_request_body(content_type, params, build_openapi_parser_post_option(validator_option))
|
94
117
|
rescue => e
|
95
118
|
raise Committee::InvalidRequest.new(e.message)
|
96
119
|
end
|
120
|
+
|
121
|
+
def response_validate_options(strict, check_header)
|
122
|
+
::OpenAPIParser::SchemaValidator::ResponseValidateOptions.new(strict: strict, validate_header: check_header)
|
123
|
+
end
|
97
124
|
end
|
98
125
|
end
|
@@ -1,17 +1,27 @@
|
|
1
1
|
module Committee
|
2
2
|
class SchemaValidator::OpenAPI3::RequestValidator
|
3
|
+
# @param [SchemaValidator::OpenAPI3::OperationWrapper] operation_object
|
4
|
+
# @param [Committee::SchemaValidator::Option] validator_option
|
3
5
|
def initialize(operation_object, validator_option:)
|
4
6
|
@operation_object = operation_object
|
5
7
|
@validator_option = validator_option
|
6
8
|
end
|
7
9
|
|
8
10
|
def call(request, params, headers)
|
9
|
-
|
10
|
-
|
11
|
+
content_type = ::Committee::SchemaValidator.request_media_type(request)
|
12
|
+
check_content_type(request, content_type) if @validator_option.check_content_type
|
11
13
|
|
12
|
-
@operation_object.validate_request_params(params, @validator_option)
|
14
|
+
@operation_object.validate_request_params(params, headers, @validator_option)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def check_content_type(request, content_type)
|
20
|
+
# support post, put, patch only
|
21
|
+
return true unless request.post? || request.put? || request.patch?
|
22
|
+
return true if @operation_object.valid_request_content_type?(content_type)
|
13
23
|
|
14
|
-
|
24
|
+
raise Committee::InvalidRequest, %{"Content-Type" request header must be set to "#{@operation_object}".}
|
15
25
|
end
|
16
26
|
end
|
17
27
|
end
|
@@ -1,35 +1,24 @@
|
|
1
1
|
module Committee
|
2
2
|
class SchemaValidator::OpenAPI3::ResponseValidator
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :validate_success_only
|
4
4
|
|
5
|
+
# @param [Committee::SchemaValidator::Options] validator_option
|
5
6
|
# @param [Committee::SchemaValidator::OpenAPI3::OperationWrapper] operation_wrapper
|
6
7
|
def initialize(operation_wrapper, validator_option)
|
7
8
|
@operation_wrapper = operation_wrapper
|
8
|
-
@
|
9
|
+
@validate_success_only = validator_option.validate_success_only
|
10
|
+
@check_header = validator_option.check_header
|
9
11
|
end
|
10
12
|
|
11
|
-
def
|
12
|
-
|
13
|
+
def call(status, headers, response_data, strict)
|
14
|
+
return unless Committee::Middleware::ResponseValidation.validate?(status, validate_success_only)
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def call(status, headers, data)
|
18
|
-
return unless self.class.validate?(status, validate_errors: validate_errors)
|
19
|
-
|
20
|
-
content_type = headers['Content-Type'].to_s.split(";").first.to_s
|
21
|
-
operation_wrapper.validate_response_params(status, content_type, data)
|
16
|
+
#content_type = headers['Content-Type'].to_s.split(";").first.to_s
|
17
|
+
operation_wrapper.validate_response_params(status, headers, response_data, strict, check_header)
|
22
18
|
end
|
23
19
|
|
24
20
|
private
|
25
21
|
|
26
|
-
|
27
|
-
attr_reader :operation_wrapper
|
28
|
-
|
29
|
-
def check_content_type!(response)
|
30
|
-
# TODO: fix
|
31
|
-
# OpenAPI3 support multi content type definitions, so we should get OperationObject by content type and this function don't need
|
32
|
-
# We should support if content exist and not exist content-type definition, raise error (if not exist content, we don't raise error)
|
33
|
-
end
|
22
|
+
attr_reader :operation_wrapper, :check_header
|
34
23
|
end
|
35
24
|
end
|
@@ -1,15 +1,16 @@
|
|
1
1
|
module Committee
|
2
2
|
class SchemaValidator::OpenAPI3::Router
|
3
|
+
# @param [Committee::SchemaValidator::Option] validator_option
|
3
4
|
def initialize(schema, validator_option)
|
4
5
|
@schema = schema
|
5
|
-
@
|
6
|
+
@prefix_regexp = ::Committee::SchemaValidator.build_prefix_regexp(validator_option.prefix)
|
6
7
|
@validator_option = validator_option
|
7
8
|
end
|
8
9
|
|
9
10
|
def includes_request?(request)
|
10
|
-
return true unless @
|
11
|
-
|
12
|
-
|
11
|
+
return true unless @prefix_regexp
|
12
|
+
|
13
|
+
prefix_request?(request)
|
13
14
|
end
|
14
15
|
|
15
16
|
def build_schema_validator(request)
|
@@ -18,9 +19,19 @@ module Committee
|
|
18
19
|
|
19
20
|
def operation_object(request)
|
20
21
|
path = request.path
|
22
|
+
path = path.gsub(@prefix_regexp, '') if prefix_request?(request)
|
23
|
+
|
21
24
|
request_method = request.request_method.downcase
|
22
25
|
|
23
26
|
@schema.operation_object(path, request_method)
|
24
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def prefix_request?(request)
|
32
|
+
return false unless @prefix_regexp
|
33
|
+
|
34
|
+
request.path =~ @prefix_regexp
|
35
|
+
end
|
25
36
|
end
|
26
37
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
class Committee::SchemaValidator
|
2
2
|
class OpenAPI3
|
3
|
+
# @param [Committee::SchemaValidator::Option] validator_option
|
3
4
|
def initialize(router, request, validator_option)
|
4
5
|
@router = router
|
5
6
|
@request = request
|
@@ -17,16 +18,20 @@ class Committee::SchemaValidator
|
|
17
18
|
|
18
19
|
request_schema_validation(request)
|
19
20
|
|
20
|
-
@operation_object&.coerce_request_parameter(request.env["rack.request.query_hash"], validator_option) if !request.GET.nil? && !request.env["rack.request.query_hash"].empty?
|
21
|
+
@operation_object&.coerce_request_parameter(request.env["rack.request.query_hash"], header(request), validator_option) if !request.GET.nil? && !request.env["rack.request.query_hash"].empty?
|
21
22
|
end
|
22
23
|
|
23
|
-
def response_validate(status, headers, response)
|
24
|
+
def response_validate(status, headers, response, test_method = false)
|
24
25
|
full_body = ""
|
25
26
|
response.each do |chunk|
|
26
27
|
full_body << chunk
|
27
28
|
end
|
28
|
-
data = JSON.parse(full_body)
|
29
|
-
|
29
|
+
data = full_body.empty? ? {} : JSON.parse(full_body)
|
30
|
+
|
31
|
+
strict = test_method
|
32
|
+
Committee::SchemaValidator::OpenAPI3::ResponseValidator.
|
33
|
+
new(@operation_object, validator_option).
|
34
|
+
call(status, headers, data, strict)
|
30
35
|
end
|
31
36
|
|
32
37
|
def link_exist?
|
@@ -50,7 +55,11 @@ class Committee::SchemaValidator
|
|
50
55
|
return unless @operation_object
|
51
56
|
|
52
57
|
validator = Committee::SchemaValidator::OpenAPI3::RequestValidator.new(@operation_object, validator_option: validator_option)
|
53
|
-
validator.call(request, request.env[validator_option.params_key], request
|
58
|
+
validator.call(request, request.env[validator_option.params_key], header(request))
|
59
|
+
end
|
60
|
+
|
61
|
+
def header(request)
|
62
|
+
request.env[validator_option.headers_key]
|
54
63
|
end
|
55
64
|
|
56
65
|
def request_unpack(request)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class Committee::SchemaValidator
|
2
2
|
class Option
|
3
|
-
attr_reader :coerce_recursive, :params_key, :allow_form_params, :allow_query_params, :optimistic_json, :coerce_form_params, :headers_key, :coerce_query_params, :coerce_path_params, :check_content_type, :check_header, :coerce_date_times, :
|
3
|
+
attr_reader :coerce_recursive, :params_key, :allow_form_params, :allow_query_params, :optimistic_json, :coerce_form_params, :headers_key, :coerce_query_params, :coerce_path_params, :check_content_type, :check_header, :coerce_date_times, :prefix, :validate_success_only
|
4
4
|
|
5
5
|
def initialize(options, schema, schema_type)
|
6
6
|
@headers_key = options[:headers_key] || "committee.headers"
|
@@ -9,7 +9,7 @@ class Committee::SchemaValidator
|
|
9
9
|
@prefix = options[:prefix]
|
10
10
|
|
11
11
|
# response option
|
12
|
-
@
|
12
|
+
@validate_success_only = options.fetch(:validate_success_only, schema.driver.default_validate_success_only)
|
13
13
|
|
14
14
|
@coerce_recursive = options.fetch(:coerce_recursive, true)
|
15
15
|
@allow_form_params = options.fetch(:allow_form_params, true)
|
@@ -21,10 +21,7 @@ class Committee::SchemaValidator
|
|
21
21
|
@coerce_query_params = options.fetch(:coerce_query_params, schema.driver.default_query_params)
|
22
22
|
@coerce_path_params = options.fetch(:coerce_path_params, schema.driver.default_path_params)
|
23
23
|
|
24
|
-
raise 'OpenAPI3 not support @check_content_type option' if schema_type == :open_api_3 && options[:check_content_type]
|
25
24
|
@check_content_type = options.fetch(:check_content_type, true)
|
26
|
-
|
27
|
-
raise 'OpenAPI3 not support @check_header option' if schema_type == :open_api_3 && options[:check_header]
|
28
25
|
@check_header = options.fetch(:check_header, true)
|
29
26
|
end
|
30
27
|
end
|