committee 3.0.0.alpha → 3.0.0.beta
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 +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
|