committee 2.5.1 → 3.0.0.alpha

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 (50) hide show
  1. checksums.yaml +5 -5
  2. data/lib/committee.rb +16 -5
  3. data/lib/committee/bin/committee_stub.rb +4 -0
  4. data/lib/committee/drivers.rb +12 -29
  5. data/lib/committee/drivers/hyper_schema.rb +9 -0
  6. data/lib/committee/drivers/open_api_2.rb +9 -20
  7. data/lib/committee/drivers/open_api_3.rb +70 -0
  8. data/lib/committee/errors.rb +3 -0
  9. data/lib/committee/middleware/base.rb +20 -62
  10. data/lib/committee/middleware/request_validation.rb +5 -62
  11. data/lib/committee/middleware/response_validation.rb +5 -19
  12. data/lib/committee/middleware/stub.rb +5 -1
  13. data/lib/committee/parameter_coercer.rb +1 -0
  14. data/lib/committee/request_unpacker.rb +2 -5
  15. data/lib/committee/schema_validator/hyper_schema.rb +92 -0
  16. data/lib/committee/{request_validator.rb → schema_validator/hyper_schema/request_validator.rb} +1 -1
  17. data/lib/committee/{response_generator.rb → schema_validator/hyper_schema/response_generator.rb} +1 -1
  18. data/lib/committee/{response_validator.rb → schema_validator/hyper_schema/response_validator.rb} +6 -6
  19. data/lib/committee/{router.rb → schema_validator/hyper_schema/router.rb} +10 -4
  20. data/lib/committee/{string_params_coercer.rb → schema_validator/hyper_schema/string_params_coercer.rb} +1 -1
  21. data/lib/committee/schema_validator/open_api_3.rb +67 -0
  22. data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +98 -0
  23. data/lib/committee/schema_validator/open_api_3/request_validator.rb +17 -0
  24. data/lib/committee/schema_validator/open_api_3/response_validator.rb +35 -0
  25. data/lib/committee/schema_validator/open_api_3/router.rb +26 -0
  26. data/lib/committee/schema_validator/option.rb +31 -0
  27. data/lib/committee/test/methods.rb +14 -117
  28. data/test/bin/committee_stub_test.rb +6 -0
  29. data/test/drivers/open_api_2_test.rb +0 -81
  30. data/test/drivers/open_api_3_test.rb +81 -0
  31. data/test/drivers_test.rb +2 -42
  32. data/test/middleware/base_test.rb +42 -21
  33. data/test/middleware/request_validation_open_api_3_test.rb +499 -0
  34. data/test/middleware/request_validation_test.rb +18 -0
  35. data/test/middleware/response_validation_open_api_3_test.rb +96 -0
  36. data/test/middleware/response_validation_test.rb +7 -30
  37. data/test/middleware/stub_test.rb +9 -0
  38. data/test/request_unpacker_test.rb +55 -21
  39. data/test/{request_validator_test.rb → schema_validator/hyper_schema/request_validator_test.rb} +4 -4
  40. data/test/{response_generator_test.rb → schema_validator/hyper_schema/response_generator_test.rb} +11 -11
  41. data/test/{response_validator_test.rb → schema_validator/hyper_schema/response_validator_test.rb} +3 -3
  42. data/test/{router_test.rb → schema_validator/hyper_schema/router_test.rb} +8 -10
  43. data/test/{string_params_coercer_test.rb → schema_validator/hyper_schema/string_params_coercer_test.rb} +3 -3
  44. data/test/schema_validator/open_api_3/operation_wrapper_test.rb +132 -0
  45. data/test/schema_validator/open_api_3/request_validator_test.rb +151 -0
  46. data/test/schema_validator/open_api_3/response_validator_test.rb +55 -0
  47. data/test/test/methods_new_version_test.rb +11 -20
  48. data/test/test/methods_test.rb +51 -55
  49. data/test/test_helper.rb +22 -8
  50. metadata +46 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 0b9f998d8056dd74bde5f8c3f522dc156d37af573a96ed089abe3d8a9e99c5d5
4
- data.tar.gz: 70c8fb47979080bddcb70f7d736a82d7739cdbe89945303bb06767c8dcce6949
2
+ SHA1:
3
+ metadata.gz: 2643d1595d38c9d9197a11c43ca05bb9a2da9c09
4
+ data.tar.gz: ebb69b45502334c6ad97d9d64a05d8f6855c5748
5
5
  SHA512:
6
- metadata.gz: 4aa59b2147ab36885982da4da3e7b0cc20e70e4d9b6bd7b687a10cdbd1e1192a7ba58e0d160fc9ca11d5da646f1e0469a28859505fb48c37497da30f6d9cfb82
7
- data.tar.gz: 6111d776b668b12b702c910655c901e5743e10adc3d4873b0f26b030b67b1d7a602cc2e141f037ea0729f5f73ac7e7a120794d715616330987afffa0693b7812
6
+ metadata.gz: f22b717d735d46455064a996423961f27c5115c15634f053397649d114c6759b229b5ce2eea09086c9f7733f270cfc342faf3bf1f77d6740130cb66b39e5f22e
7
+ data.tar.gz: c1bc25f4841bfb2e10567e9b20a18f51daadb7f8c2c55068c8954f31692f8b90e02fac5beec1ed91c813e8db77e4e44a8bd7afbbc73b902d243990f6b54cae18
@@ -1,26 +1,37 @@
1
1
  require "json"
2
2
  require "json_schema"
3
3
  require "rack"
4
+ require 'openapi_parser'
4
5
 
5
6
  require_relative "committee/errors"
6
- require_relative "committee/string_params_coercer"
7
7
  require_relative "committee/parameter_coercer"
8
8
  require_relative "committee/request_unpacker"
9
- require_relative "committee/request_validator"
10
- require_relative "committee/response_generator"
11
- require_relative "committee/response_validator"
12
- require_relative "committee/router"
9
+
13
10
  require_relative "committee/validation_error"
14
11
 
15
12
  require_relative "committee/drivers"
16
13
  require_relative "committee/drivers/hyper_schema"
17
14
  require_relative "committee/drivers/open_api_2"
15
+ require_relative "committee/drivers/open_api_3"
18
16
 
19
17
  require_relative "committee/middleware/base"
20
18
  require_relative "committee/middleware/request_validation"
21
19
  require_relative "committee/middleware/response_validation"
22
20
  require_relative "committee/middleware/stub"
23
21
 
22
+ require_relative "committee/schema_validator/option"
23
+ require_relative "committee/schema_validator/open_api_3"
24
+ require_relative "committee/schema_validator/open_api_3/router"
25
+ require_relative "committee/schema_validator/open_api_3/operation_wrapper"
26
+ require_relative "committee/schema_validator/open_api_3/request_validator"
27
+ require_relative "committee/schema_validator/open_api_3/response_validator"
28
+ require_relative "committee/schema_validator/hyper_schema"
29
+ require_relative "committee/schema_validator/hyper_schema/request_validator"
30
+ require_relative "committee/schema_validator/hyper_schema/response_generator"
31
+ require_relative "committee/schema_validator/hyper_schema/response_validator"
32
+ require_relative "committee/schema_validator/hyper_schema/router"
33
+ require_relative "committee/schema_validator/hyper_schema/string_params_coercer"
34
+
24
35
  require_relative "committee/bin/committee_stub"
25
36
 
26
37
  require_relative "committee/test/methods"
@@ -1,3 +1,5 @@
1
+ # Don't Support OpenAPI3
2
+
1
3
  module Committee
2
4
  module Bin
3
5
  # CommitteeStub internalizes the functionality of bin/committee-stub so
@@ -8,6 +10,8 @@ module Committee
8
10
  def get_app(schema, options)
9
11
  cache = {}
10
12
 
13
+ raise Committee::NotSupportOpenAPI3.new("OpenAPI3 not support stub") unless schema.support_stub?
14
+
11
15
  Rack::Builder.new {
12
16
  unless options[:tolerant]
13
17
  use Committee::Middleware::RequestValidation, schema: schema
@@ -8,39 +8,13 @@ module Committee
8
8
  Committee::Drivers::HyperSchema.new
9
9
  when :open_api_2
10
10
  Committee::Drivers::OpenAPI2.new
11
+ when :open_api_3
12
+ Committee::Drivers::OpenAPI3.new
11
13
  else
12
14
  raise ArgumentError, %{Committee: unknown driver "#{name}".}
13
15
  end
14
16
  end
15
17
 
16
- # load and build drive from JSON file
17
- # @param [String] schema_path
18
- # @return [Committee::Driver]
19
- def self.load_from_json(schema_path)
20
- json = JSON.parse(File.read(schema_path))
21
- load_from_data(json)
22
- end
23
-
24
- # load and build drive from Hash object
25
- # @param [Hash] hash
26
- # @return [Committee::Driver]
27
- def self.load_from_data(hash)
28
- driver = if hash['swagger'] == '2.0'
29
- Committee::Drivers::OpenAPI2.new
30
- else
31
- Committee::Drivers::HyperSchema.new
32
- end
33
-
34
- driver.parse(hash)
35
- end
36
-
37
- # load and build drive from file
38
- # @param [String] schema_path
39
- # @return [Committee::Driver]
40
- def self.load_from_file(schema_path)
41
- load_from_json(schema_path)
42
- end
43
-
44
18
  # Driver is a base class for driver implementations.
45
19
  class Driver
46
20
  # Whether parameters that were form-encoded will be coerced by default.
@@ -85,6 +59,15 @@ module Committee
85
59
  def driver
86
60
  raise "needs implementation"
87
61
  end
62
+
63
+ def build_router(options)
64
+ raise "needs implementation"
65
+ end
66
+
67
+ # OpenAPI3 not support stub but JSON Hyper-Schema and OpenAPI2 support
68
+ def support_stub?
69
+ true
70
+ end
88
71
  end
89
72
  end
90
- end
73
+ end
@@ -17,6 +17,10 @@ module Committee::Drivers
17
17
  false
18
18
  end
19
19
 
20
+ def default_coerce_date_times
21
+ false
22
+ end
23
+
20
24
  def name
21
25
  :hyper_schema
22
26
  end
@@ -114,6 +118,11 @@ module Committee::Drivers
114
118
  attr_accessor :driver
115
119
 
116
120
  attr_accessor :routes
121
+
122
+ def build_router(options)
123
+ validator_option = Committee::SchemaValidator::Option.new(options, self, :hyper_schema)
124
+ Committee::SchemaValidator::HyperSchema::Router.new(self, validator_option)
125
+ end
117
126
  end
118
127
 
119
128
  private
@@ -17,6 +17,10 @@ module Committee::Drivers
17
17
  true
18
18
  end
19
19
 
20
+ def default_coerce_date_times
21
+ false
22
+ end
23
+
20
24
  def name
21
25
  :open_api_2
22
26
  end
@@ -173,26 +177,6 @@ module Committee::Drivers
173
177
  # therefore this should map over quite well.
174
178
  param_schema.type = [param_data["type"]]
175
179
 
176
- param_schema.enum = param_data["enum"] unless param_data["enum"].nil?
177
-
178
- # validation: string
179
- param_schema.format = param_data["format"] unless param_data["format"].nil?
180
- param_schema.pattern = Regexp.new(param_data["pattern"]) unless param_data["pattern"].nil?
181
- param_schema.min_length = param_data["minLength"] unless param_data["minLength"].nil?
182
- param_schema.max_length = param_data["maxLength"] unless param_data["maxLength"].nil?
183
-
184
- # validation: array
185
- param_schema.min_items = param_data["minItems"] unless param_data["minItems"].nil?
186
- param_schema.max_items = param_data["maxItems"] unless param_data["maxItems"].nil?
187
- param_schema.unique_items = param_data["uniqueItems"] unless param_data["uniqueItems"].nil?
188
-
189
- # validation: number/integer
190
- param_schema.min = param_data["minimum"] unless param_data["minimum"].nil?
191
- param_schema.min_exclusive = param_data["exclusiveMinimum"] unless param_data["exclusiveMinimum"].nil?
192
- param_schema.max = param_data["maximum"] unless param_data["maximum"].nil?
193
- param_schema.max_exclusive = param_data["exclusiveMaximum"] unless param_data["exclusiveMaximum"].nil?
194
- param_schema.multiple_of = param_data["multipleOf"] unless param_data["multipleOf"].nil?
195
-
196
180
  # And same idea: despite parameters not being schemas, the items
197
181
  # key (if preset) is actually a schema that defines each item of an
198
182
  # array type, so we can just reflect that directly onto our
@@ -224,6 +208,11 @@ module Committee::Drivers
224
208
  attr_accessor :definitions
225
209
  attr_accessor :produces
226
210
  attr_accessor :routes
211
+
212
+ def build_router(options)
213
+ validator_option = Committee::SchemaValidator::Option.new(options, self, :hyper_schema)
214
+ Committee::SchemaValidator::HyperSchema::Router.new(self, validator_option)
215
+ end
227
216
  end
228
217
 
229
218
  private
@@ -0,0 +1,70 @@
1
+ module Committee::Drivers
2
+ class OpenAPI3 < Committee::Drivers::Driver
3
+ # Whether parameters that were form-encoded will be coerced by default.
4
+ def default_coerce_form_params
5
+ true
6
+ end
7
+
8
+ # Whether parameters in a request's path will be considered and coerced by
9
+ # default.
10
+ def default_path_params
11
+ true
12
+ end
13
+
14
+ # Whether parameters in a request's query string will be considered and
15
+ # coerced by default.
16
+ def default_query_params
17
+ true
18
+ end
19
+
20
+ def default_coerce_date_times
21
+ true
22
+ end
23
+
24
+ def name
25
+ :open_api_3
26
+ end
27
+
28
+ # TODO: check it's need?
29
+ def schema_class
30
+ Committee::Drivers::OpenAPI3::Schema
31
+ end
32
+
33
+ def parse(open_api)
34
+ Schema.new(self, open_api)
35
+ end
36
+
37
+ class Schema < Committee::Drivers::Schema
38
+ attr_reader :open_api
39
+
40
+ # @!attribute [r] open_api
41
+ # @return [OpenAPIParser::Schemas::OpenAPI]
42
+
43
+ def initialize(driver, open_api)
44
+ @open_api = open_api
45
+ @driver = driver
46
+ end
47
+
48
+ def support_stub?
49
+ false
50
+ end
51
+
52
+ def driver # we don't use attr_reader because this method override super class
53
+ @driver
54
+ end
55
+
56
+ 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)
59
+ end
60
+
61
+ # OpenAPI3 only
62
+ def operation_object(path, method)
63
+ request_operation = open_api.request_operation(method, path)
64
+ return nil unless request_operation
65
+
66
+ Committee::SchemaValidator::OpenAPI3::OperationWrapper.new(request_operation)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -16,4 +16,7 @@ module Committee
16
16
 
17
17
  class ReferenceNotFound < Error
18
18
  end
19
+
20
+ class NotSupportOpenAPI3 < Error
21
+ end
19
22
  end
@@ -4,14 +4,11 @@ module Committee::Middleware
4
4
  @app = app
5
5
 
6
6
  @error_class = options.fetch(:error_class, Committee::ValidationError)
7
- @params_key = options[:params_key] || "committee.params"
8
- @headers_key = options[:headers_key] || "committee.headers"
7
+
9
8
  @raise = options[:raise]
10
- @schema = get_schema(options)
9
+ @schema = self.class.get_schema(options)
11
10
 
12
- @router = Committee::Router.new(@schema,
13
- prefix: options[:prefix]
14
- )
11
+ @router = @schema.build_router(options)
15
12
  end
16
13
 
17
14
  def call(env)
@@ -24,70 +21,31 @@ module Committee::Middleware
24
21
  end
25
22
  end
26
23
 
27
- private
28
-
29
- # For modern use of the library a schema should be an instance of
30
- # Committee::Drivers::Schema so that we know that all the computationally
31
- # difficult parsing is already done by the time we try to handle any
32
- # request.
33
- #
34
- # However, for reasons of backwards compatibility we also allow schema
35
- # input to be a string, a data hash, or a JsonSchema::Schema. In the former
36
- # two cases we just parse as if we were sent hyper-schema. In the latter,
37
- # we have the hyper-schema driver wrap it in a new Committee object.
38
- def get_schema(options)
39
- schema = options[:schema]
40
- unless schema
41
- schema = Committee::Drivers::load_from_file(options[:schema_path]) if options[:schema_path]
42
-
43
- raise(ArgumentError, "Committee: need option `schema` or schema_path") unless schema
44
- end
45
-
46
- # These are in a separately conditional ladder so that we only show the
47
- # user one warning.
48
- if schema.is_a?(String)
49
- warn_string_deprecated
50
- elsif schema.is_a?(Hash)
51
- warn_hash_deprecated
52
- elsif schema.is_a?(JsonSchema::Schema)
53
- warn_json_schema_deprecated
54
- end
24
+ class << self
25
+ def get_schema(options)
26
+ open_api_3 = options[:open_api_3]
27
+ return open_api_3 if open_api_3
55
28
 
56
- if schema.is_a?(String)
57
- schema = JSON.parse(schema)
58
- end
29
+ schema = options[:schema]
59
30
 
60
- if schema.is_a?(Hash) || schema.is_a?(JsonSchema::Schema)
61
- driver = Committee::Drivers::HyperSchema.new
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
62
37
 
63
- # The driver itself has its own special cases to be able to parse
64
- # either a hash or JsonSchema::Schema object.
65
- schema = driver.parse(schema)
66
- end
38
+ return schema
39
+ end
67
40
 
68
- # Expect the type we want by now. If we don't have it, the user passed
69
- # something else non-standard in.
70
- if !schema.is_a?(Committee::Drivers::Schema)
71
- raise ArgumentError, "Committee: schema expected to be an instance of " \
72
- "Committee::Drivers::Schema."
41
+ raise(ArgumentError, "Committee: need option `schema` or `open_api_3`")
73
42
  end
74
-
75
- schema
76
43
  end
77
44
 
78
- def warn_json_schema_deprecated
79
- Committee.warn_deprecated("Committee: passing a JsonSchema::Schema to schema " \
80
- "option is deprecated; please send an instance of Committee::Drivers::Schema instead.")
81
- end
82
-
83
- def warn_hash_deprecated
84
- Committee.warn_deprecated("Committee: passing a hash to schema " \
85
- "option is deprecated; please send an instance of Committee::Drivers::Schema instead.")
86
- end
45
+ private
87
46
 
88
- def warn_string_deprecated
89
- Committee.warn_deprecated("Committee: passing a string to schema " \
90
- "option is deprecated; please send an instance of Committee::Drivers::Schema instead.")
47
+ def build_schema_validator(request)
48
+ @router.build_schema_validator(request)
91
49
  end
92
50
  end
93
51
  end
@@ -3,68 +3,19 @@ module Committee::Middleware
3
3
  def initialize(app, options={})
4
4
  super
5
5
 
6
- @allow_form_params = options.fetch(:allow_form_params, true)
7
- @allow_query_params = options.fetch(:allow_query_params, true)
8
- @check_content_type = options.fetch(:check_content_type, true)
9
- @check_header = options.fetch(:check_header, true)
10
- @optimistic_json = options.fetch(:optimistic_json, false)
11
- @strict = options[:strict]
12
- @coerce_date_times = options.fetch(:coerce_date_times, false)
13
- @coerce_recursive = options.fetch(:coerce_recursive, true)
14
-
15
- @coerce_form_params = options.fetch(:coerce_form_params,
16
- @schema.driver.default_coerce_form_params)
17
- @coerce_path_params = options.fetch(:coerce_path_params,
18
- @schema.driver.default_path_params)
19
- @coerce_query_params = options.fetch(:coerce_query_params,
20
- @schema.driver.default_query_params)
6
+ @strict = options[:strict]
21
7
 
22
8
  # deprecated
23
9
  @allow_extra = options[:allow_extra]
24
10
  end
25
11
 
26
12
  def handle(request)
27
- link, param_matches = @router.find_request_link(request)
28
-
29
- if link
30
- # Attempts to coerce parameters that appear in a link's URL to Ruby
31
- # types that can be validated with a schema.
32
- if @coerce_path_params
33
- Committee::StringParamsCoercer.new(param_matches, link.schema, coerce_recursive: @coerce_recursive).call!
34
- end
35
-
36
- # Attempts to coerce parameters that appear in a query string to Ruby
37
- # types that can be validated with a schema.
38
- if @coerce_query_params && !request.GET.nil? && !link.schema.nil?
39
- Committee::StringParamsCoercer.new(request.GET, link.schema, coerce_recursive: @coerce_recursive).call!
40
- end
41
- end
42
-
43
- request.env[@params_key], request.env[@headers_key] = Committee::RequestUnpacker.new(
44
- request,
45
- allow_form_params: @allow_form_params,
46
- allow_query_params: @allow_query_params,
47
- coerce_form_params: @coerce_form_params,
48
- optimistic_json: @optimistic_json,
49
- coerce_recursive: @coerce_recursive,
50
- schema: link ? link.schema : nil
51
- ).call
13
+ schema_validator = build_schema_validator(request)
14
+ schema_validator.request_validate(request)
52
15
 
53
- request.env[@params_key].merge!(param_matches) if param_matches
16
+ raise Committee::NotFound, "That request method and path combination isn't defined." if !schema_validator.link_exist? && @strict
54
17
 
55
- if link
56
- validator = Committee::RequestValidator.new(link, check_content_type: @check_content_type, check_header: @check_header)
57
- validator.call(request, request.env[@params_key], request.env[@headers_key])
58
-
59
- parameter_coerce!(request, link, @params_key)
60
- parameter_coerce!(request, link, "rack.request.query_hash") if !request.GET.nil? && !link.schema.nil?
61
-
62
- @app.call(request.env)
63
- elsif @strict
64
- raise Committee::NotFound, "That request method and path combination isn't defined."
65
- else
66
- @app.call(request.env)
67
- end
18
+ @app.call(request.env)
68
19
  rescue Committee::BadRequest, Committee::InvalidRequest
69
20
  raise if @raise
70
21
  @error_class.new(400, :bad_request, $!.message).render
@@ -79,13 +30,5 @@ module Committee::Middleware
79
30
  raise Committee::InvalidRequest if @raise
80
31
  @error_class.new(400, :bad_request, "Request body wasn't valid JSON.").render
81
32
  end
82
-
83
- private
84
-
85
- def parameter_coerce!(request, link, coerce_key)
86
- Committee::ParameterCoercer.
87
- new(request.env[coerce_key], link.schema, coerce_date_times: @coerce_date_times, coerce_recursive: @coerce_recursive).
88
- call!
89
- end
90
33
  end
91
34
  end