committee 2.5.1 → 3.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
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