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.
Files changed (39) hide show
  1. checksums.yaml +5 -5
  2. data/lib/committee/drivers/hyper_schema.rb +8 -2
  3. data/lib/committee/drivers/open_api_2.rb +7 -2
  4. data/lib/committee/drivers/open_api_3.rb +9 -4
  5. data/lib/committee/drivers.rb +46 -0
  6. data/lib/committee/middleware/base.rb +9 -11
  7. data/lib/committee/middleware/response_validation.rb +8 -5
  8. data/lib/committee/request_unpacker.rb +4 -1
  9. data/lib/committee/{parameter_coercer.rb → schema_validator/hyper_schema/parameter_coercer.rb} +1 -2
  10. data/lib/committee/schema_validator/hyper_schema/request_validator.rb +1 -5
  11. data/lib/committee/schema_validator/hyper_schema/response_validator.rb +3 -9
  12. data/lib/committee/schema_validator/hyper_schema/router.rb +1 -1
  13. data/lib/committee/schema_validator/hyper_schema.rb +5 -4
  14. data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +46 -19
  15. data/lib/committee/schema_validator/open_api_3/request_validator.rb +14 -4
  16. data/lib/committee/schema_validator/open_api_3/response_validator.rb +9 -20
  17. data/lib/committee/schema_validator/open_api_3/router.rb +15 -4
  18. data/lib/committee/schema_validator/open_api_3.rb +14 -5
  19. data/lib/committee/schema_validator/option.rb +2 -5
  20. data/lib/committee/schema_validator/schema_validator.rb +15 -0
  21. data/lib/committee/test/methods.rb +3 -5
  22. data/lib/committee.rb +3 -1
  23. data/test/drivers/open_api_3_test.rb +2 -1
  24. data/test/drivers_test.rb +69 -0
  25. data/test/middleware/base_test.rb +20 -5
  26. data/test/middleware/request_validation_open_api_3_test.rb +136 -259
  27. data/test/middleware/request_validation_test.rb +18 -3
  28. data/test/middleware/response_validation_open_api_3_test.rb +83 -16
  29. data/test/middleware/response_validation_test.rb +19 -7
  30. data/test/middleware/stub_test.rb +1 -1
  31. data/test/{parameter_coercer_test.rb → schema_validator/hyper_schema/parameter_coercer_test.rb} +3 -3
  32. data/test/schema_validator/hyper_schema/router_test.rb +6 -0
  33. data/test/schema_validator/open_api_3/operation_wrapper_test.rb +29 -13
  34. data/test/schema_validator/open_api_3/request_validator_test.rb +31 -132
  35. data/test/schema_validator/open_api_3/response_validator_test.rb +19 -5
  36. data/test/test/methods_new_version_test.rb +19 -3
  37. data/test/test/methods_test.rb +15 -8
  38. data/test/test_helper.rb +15 -16
  39. metadata +8 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2643d1595d38c9d9197a11c43ca05bb9a2da9c09
4
- data.tar.gz: ebb69b45502334c6ad97d9d64a05d8f6855c5748
2
+ SHA256:
3
+ metadata.gz: 5019abacff975df04498c8e2f04d04601dd6a99d1f3a621acafa6cf0873170fa
4
+ data.tar.gz: 0a08b6be4a680675cd985abf1fad733e55eee9a47368f852148cf732de5f68ff
5
5
  SHA512:
6
- metadata.gz: f22b717d735d46455064a996423961f27c5115c15634f053397649d114c6759b229b5ce2eea09086c9f7733f270cfc342faf3bf1f77d6740130cb66b39e5f22e
7
- data.tar.gz: c1bc25f4841bfb2e10567e9b20a18f51daadb7f8c2c55068c8954f31692f8b90e02fac5beec1ed91c813e8db77e4e44a8bd7afbbc73b902d243990f6b54cae18
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
- Schema.new(self, open_api)
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
@@ -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
- if schema
32
- # Expect the type we want by now. If we don't have it, the user passed
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
- return schema
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
- raise(ArgumentError, "Committee: need option `schema` or `open_api_3`")
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 :validate_errors
3
+ attr_reader :validate_success_only
4
4
 
5
5
  def initialize(app, options = {})
6
6
  super
7
- @validate_errors = options[:validate_errors]
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
- def validate?(status)
29
- status != 204 and validate_errors || (200...300).include?(status)
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
@@ -1,6 +1,5 @@
1
- # TODO: Support OpenAPI3
2
1
  module Committee
3
- class ParameterCoercer
2
+ class SchemaValidator::HyperSchema::ParameterCoercer
4
3
  def initialize(params, schema, options = {})
5
4
  @params = params
6
5
  @schema = schema
@@ -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 :validate_errors
3
+ attr_reader :validate_success_only
4
4
 
5
5
  def initialize(link, options = {})
6
6
  @link = link
7
- @validate_errors = options[:validate_errors]
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 self.class.validate?(status, validate_errors: validate_errors) && !@validator.validate(data)
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,7 +25,7 @@ module Committee
25
25
  else
26
26
  nil
27
27
  end
28
- end.compact.sort.first
28
+ end.compact.sort_by(&:first).first
29
29
  link_with_matches.nil? ? nil : link_with_matches.slice(1, 2)
30
30
  end
31
31
 
@@ -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, validate_errors: validator_option.validate_errors).call(status, headers, data)
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
- def validate_response_params(status_code, content_type, params)
31
- return request_operation.validate_response_body(status_code, content_type, params)
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::InvalidRequest.new(e.message)
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
- OpenAPIParser::SchemaValidator::Options.new(coerce_value: coerce_value,datetime_coerce_class: datetime_coerce_class)
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
- OpenAPIParser::SchemaValidator::Options.new(coerce_value: coerce_value,datetime_coerce_class: datetime_coerce_class)
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
- OpenAPIParser::SchemaValidator::Options.new(coerce_value: coerce_value,datetime_coerce_class: datetime_coerce_class)
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
- # TODO: support other content type
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
- # TODO: support @check_content_type
10
- # check_content_type!(request, params) if @check_content_type
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
- # TODO: support header
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 :validate_errors
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
- @validate_errors = validator_option.validate_errors
9
+ @validate_success_only = validator_option.validate_success_only
10
+ @check_header = validator_option.check_header
9
11
  end
10
12
 
11
- def self.validate?(status, options = {})
12
- validate_errors = options[:validate_errors]
13
+ def call(status, headers, response_data, strict)
14
+ return unless Committee::Middleware::ResponseValidation.validate?(status, validate_success_only)
13
15
 
14
- status != 204 && (validate_errors || (200...300).include?(status))
15
- end
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
- # @return [Committee::SchemaValidator::OpenAPI3::OperationWrapper]
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
- @prefix = validator_option.prefix
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 @prefix
11
- #path = request.path
12
- #path.start_with?(@prefix)
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
- Committee::SchemaValidator::OpenAPI3::ResponseValidator.new(@operation_object, validator_option).call(status, headers, data)
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.env[validator_option.headers_key])
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, :validate_errors, :prefix
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
- @validate_errors = options[:validate_errors]
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