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.
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