committee 1.15.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +5 -5
  2. data/bin/committee-stub +11 -38
  3. data/lib/committee/bin/committee_stub.rb +67 -0
  4. data/lib/committee/drivers/driver.rb +47 -0
  5. data/lib/committee/drivers/hyper_schema/driver.rb +105 -0
  6. data/lib/committee/drivers/hyper_schema/link.rb +68 -0
  7. data/lib/committee/drivers/hyper_schema/schema.rb +22 -0
  8. data/lib/committee/drivers/hyper_schema.rb +12 -0
  9. data/lib/committee/drivers/open_api_2/driver.rb +252 -0
  10. data/lib/committee/drivers/open_api_2/header_schema_builder.rb +33 -0
  11. data/lib/committee/drivers/open_api_2/link.rb +36 -0
  12. data/lib/committee/drivers/open_api_2/parameter_schema_builder.rb +83 -0
  13. data/lib/committee/drivers/open_api_2/schema.rb +26 -0
  14. data/lib/committee/drivers/open_api_2/schema_builder.rb +33 -0
  15. data/lib/committee/drivers/open_api_2.rb +13 -0
  16. data/lib/committee/drivers/open_api_3/driver.rb +51 -0
  17. data/lib/committee/drivers/open_api_3/schema.rb +41 -0
  18. data/lib/committee/drivers/open_api_3.rb +11 -0
  19. data/lib/committee/drivers/schema.rb +23 -0
  20. data/lib/committee/drivers.rb +84 -0
  21. data/lib/committee/errors.rb +17 -0
  22. data/lib/committee/middleware/base.rb +46 -29
  23. data/lib/committee/middleware/request_validation.rb +31 -49
  24. data/lib/committee/middleware/response_validation.rb +48 -25
  25. data/lib/committee/middleware/stub.rb +62 -37
  26. data/lib/committee/middleware.rb +11 -0
  27. data/lib/committee/request_unpacker.rb +58 -50
  28. data/lib/committee/schema_validator/hyper_schema/parameter_coercer.rb +79 -0
  29. data/lib/committee/schema_validator/hyper_schema/request_validator.rb +55 -0
  30. data/lib/committee/schema_validator/hyper_schema/response_generator.rb +102 -0
  31. data/lib/committee/schema_validator/hyper_schema/response_validator.rb +89 -0
  32. data/lib/committee/schema_validator/hyper_schema/router.rb +46 -0
  33. data/lib/committee/schema_validator/hyper_schema/string_params_coercer.rb +105 -0
  34. data/lib/committee/schema_validator/hyper_schema.rb +119 -0
  35. data/lib/committee/schema_validator/open_api_3/operation_wrapper.rb +139 -0
  36. data/lib/committee/schema_validator/open_api_3/request_validator.rb +52 -0
  37. data/lib/committee/schema_validator/open_api_3/response_validator.rb +29 -0
  38. data/lib/committee/schema_validator/open_api_3/router.rb +45 -0
  39. data/lib/committee/schema_validator/open_api_3.rb +120 -0
  40. data/lib/committee/schema_validator/option.rb +60 -0
  41. data/lib/committee/schema_validator.rb +23 -0
  42. data/lib/committee/test/methods.rb +68 -38
  43. data/lib/committee/test/schema_coverage.rb +101 -0
  44. data/lib/committee/utils.rb +28 -0
  45. data/lib/committee/validation_error.rb +5 -2
  46. data/lib/committee/version.rb +5 -0
  47. data/lib/committee.rb +31 -18
  48. data/test/bin/committee_stub_test.rb +57 -0
  49. data/test/bin_test.rb +25 -0
  50. data/test/committee_test.rb +77 -0
  51. data/test/drivers/hyper_schema/driver_test.rb +49 -0
  52. data/test/drivers/hyper_schema/link_test.rb +56 -0
  53. data/test/drivers/open_api_2/driver_test.rb +156 -0
  54. data/test/drivers/open_api_2/header_schema_builder_test.rb +26 -0
  55. data/test/drivers/open_api_2/link_test.rb +52 -0
  56. data/test/drivers/open_api_2/parameter_schema_builder_test.rb +195 -0
  57. data/test/drivers/open_api_3/driver_test.rb +84 -0
  58. data/test/drivers_test.rb +154 -0
  59. data/test/middleware/base_test.rb +96 -7
  60. data/test/middleware/request_validation_open_api_3_test.rb +626 -0
  61. data/test/middleware/request_validation_test.rb +423 -32
  62. data/test/middleware/response_validation_open_api_3_test.rb +291 -0
  63. data/test/middleware/response_validation_test.rb +125 -23
  64. data/test/middleware/stub_test.rb +81 -20
  65. data/test/request_unpacker_test.rb +126 -52
  66. data/test/schema_validator/hyper_schema/parameter_coercer_test.rb +111 -0
  67. data/test/schema_validator/hyper_schema/request_validator_test.rb +151 -0
  68. data/test/schema_validator/hyper_schema/response_generator_test.rb +142 -0
  69. data/test/{response_validator_test.rb → schema_validator/hyper_schema/response_validator_test.rb} +43 -6
  70. data/test/schema_validator/hyper_schema/router_test.rb +88 -0
  71. data/test/schema_validator/hyper_schema/string_params_coercer_test.rb +137 -0
  72. data/test/schema_validator/open_api_3/operation_wrapper_test.rb +218 -0
  73. data/test/schema_validator/open_api_3/request_validator_test.rb +110 -0
  74. data/test/schema_validator/open_api_3/response_validator_test.rb +92 -0
  75. data/test/test/methods_new_version_test.rb +97 -0
  76. data/test/test/methods_test.rb +334 -27
  77. data/test/test/schema_coverage_test.rb +216 -0
  78. data/test/test_helper.rb +108 -1
  79. data/test/validation_error_test.rb +3 -1
  80. metadata +190 -27
  81. data/lib/committee/query_params_coercer.rb +0 -45
  82. data/lib/committee/request_validator.rb +0 -44
  83. data/lib/committee/response_generator.rb +0 -35
  84. data/lib/committee/response_validator.rb +0 -59
  85. data/lib/committee/router.rb +0 -62
  86. data/test/query_params_coercer_test.rb +0 -70
  87. data/test/request_validator_test.rb +0 -103
  88. data/test/response_generator_test.rb +0 -61
  89. data/test/router_test.rb +0 -38
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Drivers
5
+ module OpenAPI2
6
+ # Link abstracts an API link specifically for OpenAPI 2.
7
+ class Link
8
+ # The link's input media type. i.e. How requests should be encoded.
9
+ attr_accessor :enc_type
10
+
11
+ attr_accessor :href
12
+
13
+ # The link's output media type. i.e. How responses should be encoded.
14
+ attr_accessor :media_type
15
+
16
+ attr_accessor :method
17
+
18
+ # The link's input schema. i.e. How we validate an endpoint's incoming
19
+ # parameters.
20
+ attr_accessor :schema
21
+
22
+ attr_accessor :status_success
23
+
24
+ # The link's output schema. i.e. How we validate an endpoint's response
25
+ # data.
26
+ attr_accessor :target_schema
27
+
28
+ attr_accessor :header_schema
29
+
30
+ def rel
31
+ raise "Committee: rel not implemented for OpenAPI"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Drivers
5
+ module OpenAPI2
6
+ # ParameterSchemaBuilder converts OpenAPI 2 link parameters, which are not
7
+ # quite JSON schemas (but will be in OpenAPI 3) into synthetic schemas that
8
+ # we can use to do some basic request validation.
9
+ class ParameterSchemaBuilder < SchemaBuilder
10
+ # Returns a tuple of (schema, schema_data) where only one of the two
11
+ # values is present. This is either a full schema that's ready to go _or_
12
+ # a hash of unparsed schema data.
13
+ def call
14
+ if link_data["parameters"]
15
+ body_param = link_data["parameters"].detect { |p| p["in"] == "body" }
16
+ if body_param
17
+ check_required_fields!(body_param)
18
+
19
+ if link_data["parameters"].detect { |p| p["in"] == "form" } != nil
20
+ raise ArgumentError, "Committee: can't mix body parameter " \
21
+ "with form parameters."
22
+ end
23
+
24
+ schema_data = body_param["schema"]
25
+ [nil, schema_data]
26
+ else
27
+ link_schema = JsonSchema::Schema.new
28
+ link_schema.properties = {}
29
+ link_schema.required = []
30
+
31
+ parameters = link_data["parameters"].reject { |param_data| param_data["in"] == "header" }
32
+ parameters.each do |param_data|
33
+ check_required_fields!(param_data)
34
+
35
+ param_schema = JsonSchema::Schema.new
36
+
37
+ # We could probably use more validation here, but the formats of
38
+ # OpenAPI 2 are based off of what's available in JSON schema, and
39
+ # therefore this should map over quite well.
40
+ param_schema.type = [param_data["type"]]
41
+
42
+ param_schema.enum = param_data["enum"] unless param_data["enum"].nil?
43
+
44
+ # validation: string
45
+ param_schema.format = param_data["format"] unless param_data["format"].nil?
46
+ param_schema.pattern = Regexp.new(param_data["pattern"]) unless param_data["pattern"].nil?
47
+ param_schema.min_length = param_data["minLength"] unless param_data["minLength"].nil?
48
+ param_schema.max_length = param_data["maxLength"] unless param_data["maxLength"].nil?
49
+
50
+ # validation: array
51
+ param_schema.min_items = param_data["minItems"] unless param_data["minItems"].nil?
52
+ param_schema.max_items = param_data["maxItems"] unless param_data["maxItems"].nil?
53
+ param_schema.unique_items = param_data["uniqueItems"] unless param_data["uniqueItems"].nil?
54
+
55
+ # validation: number/integer
56
+ param_schema.min = param_data["minimum"] unless param_data["minimum"].nil?
57
+ param_schema.min_exclusive = param_data["exclusiveMinimum"] unless param_data["exclusiveMinimum"].nil?
58
+ param_schema.max = param_data["maximum"] unless param_data["maximum"].nil?
59
+ param_schema.max_exclusive = param_data["exclusiveMaximum"] unless param_data["exclusiveMaximum"].nil?
60
+ param_schema.multiple_of = param_data["multipleOf"] unless param_data["multipleOf"].nil?
61
+
62
+ # And same idea: despite parameters not being schemas, the items
63
+ # key (if preset) is actually a schema that defines each item of an
64
+ # array type, so we can just reflect that directly onto our
65
+ # artificial schema.
66
+ if param_data["type"] == "array" && param_data["items"]
67
+ param_schema.items = param_data["items"]
68
+ end
69
+
70
+ link_schema.properties[param_data["name"]] = param_schema
71
+ if param_data["required"] == true
72
+ link_schema.required << param_data["name"]
73
+ end
74
+ end
75
+
76
+ [link_schema, nil]
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Drivers
5
+ module OpenAPI2
6
+ class Schema < ::Committee::Drivers::Schema
7
+ attr_accessor :base_path
8
+ attr_accessor :consumes
9
+
10
+ # A link back to the derivative instance of Committee::Drivers::Driver
11
+ # that create this schema.
12
+ attr_accessor :driver
13
+
14
+ attr_accessor :definitions
15
+ attr_accessor :produces
16
+ attr_accessor :routes
17
+ attr_reader :validator_option
18
+
19
+ def build_router(options)
20
+ @validator_option = Committee::SchemaValidator::Option.new(options, self, :hyper_schema)
21
+ Committee::SchemaValidator::HyperSchema::Router.new(self, @validator_option)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Drivers
5
+ module OpenAPI2
6
+ class SchemaBuilder
7
+ def initialize(link_data)
8
+ @link_data = link_data
9
+ end
10
+
11
+ private
12
+
13
+ LINK_REQUIRED_FIELDS = [
14
+ :name
15
+ ].map(&:to_s).freeze
16
+
17
+ attr_accessor :link_data
18
+
19
+ def check_required_fields!(param_data)
20
+ LINK_REQUIRED_FIELDS.each do |field|
21
+ if !param_data[field]
22
+ raise ArgumentError,
23
+ "Committee: no #{field} section in link data."
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ require_relative 'header_schema_builder'
33
+ require_relative 'parameter_schema_builder'
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Drivers
5
+ module OpenAPI2
6
+ end
7
+ end
8
+ end
9
+
10
+ require_relative 'open_api_2/driver'
11
+ require_relative 'open_api_2/link'
12
+ require_relative 'open_api_2/schema'
13
+ require_relative 'open_api_2/schema_builder'
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Drivers
5
+ module OpenAPI3
6
+ class Driver < ::Committee::Drivers::Driver
7
+ def default_coerce_date_times
8
+ true
9
+ end
10
+
11
+ # Whether parameters that were form-encoded will be coerced by default.
12
+ def default_coerce_form_params
13
+ true
14
+ end
15
+
16
+ def default_allow_get_body
17
+ false
18
+ end
19
+
20
+ # Whether parameters in a request's path will be considered and coerced by
21
+ # default.
22
+ def default_path_params
23
+ true
24
+ end
25
+
26
+ # Whether parameters in a request's query string will be considered and
27
+ # coerced by default.
28
+ def default_query_params
29
+ true
30
+ end
31
+
32
+ def default_validate_success_only
33
+ false
34
+ end
35
+
36
+ def name
37
+ :open_api_3
38
+ end
39
+
40
+ # @return [Committee::Drivers::OpenAPI3::Schema]
41
+ def parse(open_api)
42
+ schema_class.new(self, open_api)
43
+ end
44
+
45
+ def schema_class
46
+ Committee::Drivers::OpenAPI3::Schema
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Drivers
5
+ module OpenAPI3
6
+ class Schema < ::Committee::Drivers::Schema
7
+ attr_reader :open_api
8
+ attr_reader :validator_option
9
+
10
+ # @!attribute [r] open_api
11
+ # @return [OpenAPIParser::Schemas::OpenAPI]
12
+
13
+ def initialize(driver, open_api)
14
+ @open_api = open_api
15
+ @driver = driver
16
+ end
17
+
18
+ def supports_stub?
19
+ false
20
+ end
21
+
22
+ def driver # we don't use attr_reader because this method override super class
23
+ @driver
24
+ end
25
+
26
+ def build_router(options)
27
+ @validator_option = Committee::SchemaValidator::Option.new(options, self, :open_api_3)
28
+ Committee::SchemaValidator::OpenAPI3::Router.new(self, @validator_option)
29
+ end
30
+
31
+ # OpenAPI3 only
32
+ def operation_object(path, method)
33
+ request_operation = open_api.request_operation(method, path)
34
+ return nil unless request_operation
35
+
36
+ Committee::SchemaValidator::OpenAPI3::OperationWrapper.new(request_operation)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Drivers
5
+ module OpenAPI3
6
+ end
7
+ end
8
+ end
9
+
10
+ require_relative 'open_api_3/driver'
11
+ require_relative 'open_api_3/schema'
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Drivers
5
+ # Schema is a base class for driver schema implementations.
6
+ class Schema
7
+ # A link back to the derivative instance of Committee::Drivers::Driver
8
+ # that create this schema.
9
+ def driver
10
+ raise "needs implementation"
11
+ end
12
+
13
+ def build_router(options)
14
+ raise "needs implementation"
15
+ end
16
+
17
+ # Stubs are supported in JSON Hyper-Schema and OpenAPI 2, but not yet in OpenAPI 3
18
+ def supports_stub?
19
+ true
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Drivers
5
+ # Gets a driver instance from the specified name. Raises ArgumentError for
6
+ # an unknown driver name.
7
+ def self.driver_from_name(name)
8
+ case name
9
+ when :hyper_schema
10
+ Committee::Drivers::HyperSchema::Driver.new
11
+ when :open_api_2
12
+ Committee::Drivers::OpenAPI2::Driver.new
13
+ when :open_api_3
14
+ Committee::Drivers::OpenAPI3::Driver.new
15
+ else
16
+ raise ArgumentError, %{Committee: unknown driver "#{name}".}
17
+ end
18
+ end
19
+
20
+ # load and build drive from JSON file
21
+ # @param [String] schema_path
22
+ # @return [Committee::Driver]
23
+ def self.load_from_json(schema_path, parser_options: {})
24
+ load_from_data(JSON.parse(File.read(schema_path)), schema_path, parser_options: parser_options)
25
+ end
26
+
27
+ # load and build drive from YAML file
28
+ # @param [String] schema_path
29
+ # @return [Committee::Driver]
30
+ def self.load_from_yaml(schema_path, parser_options: {})
31
+ data = YAML.respond_to?(:unsafe_load_file) ? YAML.unsafe_load_file(schema_path) : YAML.load_file(schema_path)
32
+ load_from_data(data, schema_path, parser_options: parser_options)
33
+ end
34
+
35
+ # load and build drive from file
36
+ # @param [String] schema_path
37
+ # @return [Committee::Driver]
38
+ def self.load_from_file(schema_path, parser_options: {})
39
+ case File.extname(schema_path)
40
+ when '.json'
41
+ load_from_json(schema_path, parser_options: parser_options)
42
+ when '.yaml', '.yml'
43
+ load_from_yaml(schema_path, parser_options: parser_options)
44
+ else
45
+ raise "Committee only supports the following file extensions: '.json', '.yaml', '.yml'"
46
+ end
47
+ end
48
+
49
+ # load and build drive from Hash object
50
+ # @param [Hash] hash
51
+ # @return [Committee::Driver]
52
+ def self.load_from_data(hash, schema_path = nil, parser_options: {})
53
+ if hash['openapi']&.start_with?('3.0.')
54
+ # From the next major version, we want to ensure `{ strict_reference_validation: true }`
55
+ # as a parser option here, but since it may break existing implementations, just warn
56
+ # if it is not explicitly set. See: https://github.com/interagent/committee/issues/343#issuecomment-997400329
57
+ opts = parser_options.dup
58
+
59
+ Committee.warn_deprecated_until_6(!opts.key?(:strict_reference_validation), 'openapi_parser will default to strict reference validation ' +
60
+ 'from next version. Pass config `strict_reference_validation: true` (or false, if you must) ' +
61
+ 'to quiet this warning.')
62
+ opts[:strict_reference_validation] ||= false
63
+
64
+ openapi = OpenAPIParser.parse_with_filepath(hash, schema_path, opts)
65
+ return Committee::Drivers::OpenAPI3::Driver.new.parse(openapi)
66
+ end
67
+
68
+ driver = if hash['swagger'] == '2.0'
69
+ Committee::Drivers::OpenAPI2::Driver.new
70
+ else
71
+ Committee::Drivers::HyperSchema::Driver.new
72
+ end
73
+
74
+ # TODO: in the future, pass `opts` here and allow optionality in other drivers?
75
+ driver.parse(hash)
76
+ end
77
+ end
78
+ end
79
+
80
+ require_relative "drivers/driver"
81
+ require_relative "drivers/schema"
82
+ require_relative "drivers/hyper_schema"
83
+ require_relative "drivers/open_api_2"
84
+ require_relative "drivers/open_api_3"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Committee
2
4
  class Error < StandardError
3
5
  end
@@ -6,9 +8,21 @@ module Committee
6
8
  end
7
9
 
8
10
  class InvalidRequest < Error
11
+ attr_reader :original_error
12
+
13
+ def initialize(error_message=nil, original_error: nil)
14
+ @original_error = original_error
15
+ super(error_message)
16
+ end
9
17
  end
10
18
 
11
19
  class InvalidResponse < Error
20
+ attr_reader :original_error
21
+
22
+ def initialize(error_message=nil, original_error: nil)
23
+ @original_error = original_error
24
+ super(error_message)
25
+ end
12
26
  end
13
27
 
14
28
  class NotFound < Error
@@ -16,4 +30,7 @@ module Committee
16
30
 
17
31
  class ReferenceNotFound < Error
18
32
  end
33
+
34
+ class OpenAPI3Unsupported < Error
35
+ end
19
36
  end
@@ -1,40 +1,57 @@
1
- module Committee::Middleware
2
- class Base
3
- def initialize(app, options={})
4
- @app = app
5
-
6
- @error_class = options.fetch(:error_class, Committee::ValidationError)
7
- @params_key = options[:params_key] || "committee.params"
8
- @raise = options[:raise]
9
-
10
- schema = options[:schema] || raise("need option `schema`")
11
- if schema.is_a?(String)
12
- warn_string_deprecated
13
- schema = JSON.parse(schema)
1
+ # frozen_string_literal: true
2
+
3
+ module Committee
4
+ module Middleware
5
+ class Base
6
+ def initialize(app, options={})
7
+ @app = app
8
+
9
+ @error_class = options.fetch(:error_class, Committee::ValidationError)
10
+ @error_handler = options[:error_handler]
11
+ @ignore_error = options.fetch(:ignore_error, false)
12
+
13
+ @raise = options[:raise]
14
+ @schema = self.class.get_schema(options)
15
+
16
+ @router = @schema.build_router(options)
17
+ @accept_request_filter = options[:accept_request_filter] || -> (_) { true }
14
18
  end
15
- if schema.is_a?(Hash)
16
- schema = JsonSchema.parse!(schema)
17
- schema.expand_references!
19
+
20
+ def call(env)
21
+ request = Rack::Request.new(env)
22
+
23
+ if @router.includes_request?(request) && @accept_request_filter.call(request)
24
+ handle(request)
25
+ else
26
+ @app.call(request.env)
27
+ end
18
28
  end
19
- @schema = schema
20
29
 
21
- @router = Committee::Router.new(@schema, options)
22
- end
30
+ class << self
31
+ def get_schema(options)
32
+ schema = options[:schema]
33
+ if !schema && options[:schema_path]
34
+ # In the future, we could have `parser_options` as an exposed config?
35
+ parser_options = options.key?(:strict_reference_validation) ? { strict_reference_validation: options[:strict_reference_validation] } : {}
36
+ schema = Committee::Drivers::load_from_file(options[:schema_path], parser_options: parser_options)
37
+ end
38
+ raise(ArgumentError, "Committee: need option `schema` or `schema_path`") unless schema
23
39
 
24
- def call(env)
25
- request = Rack::Request.new(env)
40
+ # Expect the type we want by now. If we don't have it, the user passed
41
+ # something else non-standard in.
42
+ if !schema.is_a?(Committee::Drivers::Schema)
43
+ raise ArgumentError, "Committee: schema expected to be an instance of Committee::Drivers::Schema."
44
+ end
26
45
 
27
- if @router.includes_request?(request)
28
- handle(request)
29
- else
30
- @app.call(request.env)
46
+ return schema
47
+ end
31
48
  end
32
- end
33
49
 
34
- private
50
+ private
35
51
 
36
- def warn_string_deprecated
37
- Committee.warn_deprecated("Committee: passing a string to `schema` option is deprecated; please send a deserialized hash instead.")
52
+ def build_schema_validator(request)
53
+ @router.build_schema_validator(request)
54
+ end
38
55
  end
39
56
  end
40
57
  end
@@ -1,59 +1,41 @@
1
- module Committee::Middleware
2
- class RequestValidation < Base
3
- def initialize(app, options={})
4
- super
5
- @allow_form_params = options.fetch(:allow_form_params, true)
6
- @allow_query_params = options.fetch(:allow_query_params, true)
7
- @check_content_type = options.fetch(:check_content_type, true)
8
- @optimistic_json = options.fetch(:optimistic_json, false)
9
- @coerce_query_params = options.fetch(:coerce_query_params, false)
10
- @strict = options[:strict]
1
+ # frozen_string_literal: true
11
2
 
12
- # deprecated
13
- @allow_extra = options[:allow_extra]
14
- end
15
-
16
- def handle(request)
17
- link = @router.find_request_link(request)
3
+ module Committee
4
+ module Middleware
5
+ class RequestValidation < Base
6
+ def initialize(app, options={})
7
+ super
18
8
 
19
- if link && @coerce_query_params && !request.GET.nil? && !link.schema.nil?
20
- request.env["rack.request.query_hash"].merge!(
21
- Committee::QueryParamsCoercer.new(
22
- request.GET,
23
- link.schema
24
- ).call
25
- )
9
+ @strict = options[:strict]
26
10
  end
27
11
 
28
- request.env[@params_key] = Committee::RequestUnpacker.new(
29
- request,
30
- allow_form_params: @allow_form_params,
31
- allow_query_params: @allow_query_params,
32
- optimistic_json: @optimistic_json
33
- ).call
12
+ def handle(request)
13
+ begin
14
+ schema_validator = build_schema_validator(request)
15
+ schema_validator.request_validate(request)
16
+
17
+ raise Committee::NotFound, "That request method and path combination isn't defined." if !schema_validator.link_exist? && @strict
18
+ rescue Committee::BadRequest, Committee::InvalidRequest
19
+ handle_exception($!, request.env)
20
+ raise if @raise
21
+ return @error_class.new(400, :bad_request, $!.message, request).render unless @ignore_error
22
+ rescue Committee::NotFound => e
23
+ raise if @raise
24
+ return @error_class.new(404, :not_found, e.message, request).render unless @ignore_error
25
+ rescue JSON::ParserError
26
+ handle_exception($!, request.env)
27
+ raise Committee::InvalidRequest if @raise
28
+ return @error_class.new(400, :bad_request, "Request body wasn't valid JSON.", request).render unless @ignore_error
29
+ end
34
30
 
35
- if link
36
- validator = Committee::RequestValidator.new(link, check_content_type: @check_content_type)
37
- validator.call(request, request.env[@params_key])
38
- @app.call(request.env)
39
- elsif @strict
40
- raise Committee::NotFound
41
- else
42
31
  @app.call(request.env)
43
32
  end
44
- rescue Committee::BadRequest, Committee::InvalidRequest
45
- raise if @raise
46
- @error_class.new(400, :bad_request, $!.message).render
47
- rescue Committee::NotFound
48
- raise if @raise
49
- @error_class.new(
50
- 404,
51
- :not_found,
52
- "That request method and path combination isn't defined."
53
- ).render
54
- rescue JSON::ParserError
55
- raise Committee::InvalidRequest if @raise
56
- @error_class.new(400, :bad_request, "Request body wasn't valid JSON.").render
33
+
34
+ private
35
+
36
+ def handle_exception(e, env)
37
+ @error_handler.call(e, env) if @error_handler
38
+ end
57
39
  end
58
40
  end
59
41
  end