openapi_first 1.1.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openapi_first/body_parser.rb +3 -1
  3. data/lib/openapi_first/configuration.rb +3 -1
  4. data/lib/openapi_first/definition/operation.rb +65 -5
  5. data/lib/openapi_first/definition/path_item.rb +1 -0
  6. data/lib/openapi_first/definition/request_body.rb +1 -0
  7. data/lib/openapi_first/definition/response.rb +7 -0
  8. data/lib/openapi_first/definition.rb +43 -3
  9. data/lib/openapi_first/error_response.rb +1 -1
  10. data/lib/openapi_first/errors.rb +6 -0
  11. data/lib/openapi_first/failure.rb +28 -4
  12. data/lib/openapi_first/middlewares/request_validation.rb +2 -5
  13. data/lib/openapi_first/middlewares/response_validation.rb +1 -4
  14. data/lib/openapi_first/plugins/default/error_response.rb +4 -4
  15. data/lib/openapi_first/plugins/default.rb +1 -1
  16. data/lib/openapi_first/plugins/jsonapi/error_response.rb +3 -2
  17. data/lib/openapi_first/plugins/jsonapi.rb +1 -1
  18. data/lib/openapi_first/plugins.rb +1 -0
  19. data/lib/openapi_first/request_validation/request_body_validator.rb +1 -1
  20. data/lib/openapi_first/request_validation/validator.rb +1 -0
  21. data/lib/openapi_first/response_validation/validator.rb +1 -0
  22. data/lib/openapi_first/runtime_request.rb +63 -3
  23. data/lib/openapi_first/runtime_response.rb +43 -4
  24. data/lib/openapi_first/schema/validation_error.rb +2 -0
  25. data/lib/openapi_first/schema/validation_result.rb +2 -7
  26. data/lib/openapi_first/schema.rb +1 -0
  27. data/lib/openapi_first/version.rb +1 -1
  28. data/lib/openapi_first.rb +21 -3
  29. metadata +6 -17
  30. data/.github/CODEOWNERS +0 -1
  31. data/.github/workflows/ruby.yml +0 -13
  32. data/.gitignore +0 -11
  33. data/CHANGELOG.md +0 -274
  34. data/Gemfile +0 -18
  35. data/Gemfile.lock +0 -170
  36. data/Gemfile.rack2 +0 -15
  37. data/Gemfile.rack2.lock +0 -99
  38. data/LICENSE.txt +0 -21
  39. data/README.md +0 -225
  40. data/openapi_first.gemspec +0 -47
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4bb6d0ee4dbe98020ef124e79a99ced0d0111b1e2891cc8cb3ed8bb697973d2b
4
- data.tar.gz: 77ce896a68f885614f64fd680af0dde1860c07ae772ce7d5f7d1b75b5abb4528
3
+ metadata.gz: caea1ca3e63935e0d04a52bade49e9f8270da3bdcf3b3cb89e3791cc202c1a79
4
+ data.tar.gz: 3d1da5e269e0328ec383b30cf29486b1d49aa91317e428626c26f9284eb1d55c
5
5
  SHA512:
6
- metadata.gz: 841e32068646969d92ab26fff9c332e849056463c99805c8e0dbbb437918113a09750fb5ceffd3940716911ea50bc20685acbcd862aa6acc270e43fe43675c87
7
- data.tar.gz: d9a225b3e3119512fc3ed585f00f6f915e21f3199206f520d20733999441567e651fcb129588092c4e7ea9dcc4ce1e77c24365b04b8a8d9d232b8a1a5cd8e1da
6
+ metadata.gz: 0a48588e004db687e0dee2b54982837aebe3dcb3d65cf3191141b59d7f63d945083dd0bc364346acaa4442420101310ffc174383ee6e2897468b6680f90c4172
7
+ data.tar.gz: 352789bc86107fc3253cb08a3771399e5ecee50189c119903bc678f1de7eb078a6bc89a797bc22322ae52beb3de30c64de612fb6350d572cc6a6a1c1cb6fd067
@@ -3,10 +3,12 @@
3
3
  require 'multi_json'
4
4
 
5
5
  module OpenapiFirst
6
+ # @!visibility private
6
7
  class BodyParser
7
8
  def self.const_missing(const_name)
8
9
  super unless const_name == :ParsingError
9
- warn 'DEPRECATION WARNING: OpenapiFirst::BodyParser::ParsingError is deprecated. Use OpenapiFirst::ParseError instead.' # rubocop:disable Layout/LineLength
10
+ warn 'DEPRECATION WARNING: OpenapiFirst::BodyParser::ParsingError is deprecated. ' \
11
+ 'Use OpenapiFirst::ParseError instead.'
10
12
  OpenapiFirst::ParseError
11
13
  end
12
14
 
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
+ # Global configuration. Currently only used for the request validation middleware.
4
5
  class Configuration
5
6
  def initialize
6
7
  @request_validation_error_response = OpenapiFirst.plugin(:default)::ErrorResponse
7
8
  @request_validation_raise_error = false
8
9
  end
9
10
 
10
- attr_reader :request_validation_error_response, :request_validation_raise_error
11
+ attr_reader :request_validation_error_response
12
+ attr_accessor :request_validation_raise_error
11
13
 
12
14
  def request_validation_error_response=(mod)
13
15
  @request_validation_error_response = if mod.is_a?(Symbol)
@@ -8,11 +8,14 @@ require_relative 'responses'
8
8
 
9
9
  module OpenapiFirst
10
10
  class Definition
11
+ # Represents an operation object in the OpenAPI 3.X specification.
12
+ # Use this class to access information about the operation. Use `#[key]` to read the raw data.
13
+ # When using the middleware you can access the operation object via `env[OpenapiFirst::REQUEST].operation`.
11
14
  class Operation
12
15
  extend Forwardable
16
+
13
17
  def_delegators :operation_object,
14
- :[],
15
- :dig
18
+ :[]
16
19
 
17
20
  WRITE_METHODS = Set.new(%w[post put patch delete]).freeze
18
21
  private_constant :WRITE_METHODS
@@ -25,31 +28,62 @@ module OpenapiFirst
25
28
  @operation_object = @path_item_object[request_method]
26
29
  end
27
30
 
28
- attr_reader :path, :method, :openapi_version
31
+ # Returns the path of the operation as in the API description.
32
+ # @return [String] The path of the operation.
33
+ attr_reader :path
34
+
35
+ # Returns the (downcased) request method of the operation.
36
+ # Example: "get"
37
+ # @return [String] The request method of the operation.
38
+ attr_reader :method
29
39
  alias request_method method
30
40
 
41
+ attr_reader :openapi_version # :nodoc:
42
+
43
+ # Returns the operation ID as defined in the API description.
44
+ # @return [String, nil]
31
45
  def operation_id
32
46
  operation_object['operationId']
33
47
  end
34
48
 
49
+ # Checks if the operation is a read operation.
50
+ # This is the case for all request methods except POST, PUT, PATCH and DELETE.
51
+ # @return [Boolean] `true` if the operation is a read operation, `false` otherwise.
35
52
  def read?
36
53
  !write?
37
54
  end
38
55
 
56
+ # Checks if the operation is a write operation.
57
+ # This is the case for POST, PUT, PATCH and DELETE request methods.
58
+ # @return [Boolean] `true` if the operation is a write operation, `false` otherwise.
39
59
  def write?
40
60
  WRITE_METHODS.include?(method)
41
61
  end
42
62
 
63
+ # Returns the request body definition if defined in the API description.
64
+ # @return [RequestBody, nil] The request body of the operation, or `nil` if not present.
43
65
  def request_body
44
66
  @request_body ||= RequestBody.new(operation_object['requestBody'], self) if operation_object['requestBody']
45
67
  end
46
68
 
69
+ # Checks if a response status is defined for this operation.
70
+ # @param status [Integer, String] The response status to check.
71
+ # @return [Boolean] `true` if the response status is defined, `false` otherwise.
47
72
  def response_status_defined?(status)
48
73
  responses.status_defined?(status)
49
74
  end
50
75
 
51
- def_delegators :responses, :response_for
76
+ # Returns the response object for a given status.
77
+ # @param status [Integer, String] The response status.
78
+ # @param content_type [String] Content-Type of the current response.
79
+ # @return [Response, nil] The response object for the given status, or `nil` if not found.
80
+ def response_for(status, content_type)
81
+ responses.response_for(status, content_type)
82
+ end
52
83
 
84
+ # Returns the schema for a given content type.
85
+ # @param content_type [String] The content type.
86
+ # @return [Schema, nil] The schema for the given content type, or `nil` if not found.
53
87
  def schema_for(content_type)
54
88
  content = @request_body_object['content']
55
89
  return unless content&.any?
@@ -60,46 +94,72 @@ module OpenapiFirst
60
94
  end
61
95
  end
62
96
 
97
+ # Returns a unique name for this operation. Used for generating error messages.
98
+ # @visibility private
63
99
  def name
64
100
  @name ||= "#{method.upcase} #{path} (#{operation_id})"
65
101
  end
66
102
 
103
+ # Returns the path parameters of the operation.
104
+ # @return [Array<Hash>] The path parameters of the operation.
67
105
  def path_parameters
68
106
  all_parameters['path']
69
107
  end
70
108
 
109
+ # Returns the query parameters of the operation.
110
+ # Returns parameters defined on the path and in the operation.
111
+ # @return [Array<Hash>] The query parameters of the operation.
71
112
  def query_parameters
72
113
  all_parameters['query']
73
114
  end
74
115
 
116
+ # Returns the header parameters of the operation.
117
+ # Returns parameters defined on the path and in the operation.
118
+ # @return [Array<Hash>] The header parameters of the operation.
75
119
  def header_parameters
76
120
  all_parameters['header']
77
121
  end
78
122
 
123
+ # Returns the cookie parameters of the operation.
124
+ # Returns parameters defined on the path and in the operation.
125
+ # @return [Array<Hash>] The cookie parameters of the operation.
79
126
  def cookie_parameters
80
127
  all_parameters['cookie']
81
128
  end
82
129
 
130
+ # Returns the schema for the path parameters.
131
+ # @visibility private
132
+ # @return [Schema, nil] The schema for the path parameters, or `nil` if not found.
83
133
  def path_parameters_schema
84
134
  @path_parameters_schema ||= build_schema(path_parameters)
85
135
  end
86
136
 
137
+ # Returns the schema for the query parameters.
138
+ # @visibility private
139
+ # @return [Schema, nil] The schema for the query parameters, or `nil` if not found.
87
140
  def query_parameters_schema
88
141
  @query_parameters_schema ||= build_schema(query_parameters)
89
142
  end
90
143
 
144
+ # Returns the schema for the header parameters.
145
+ # @visibility private
146
+ # @return [Schema, nil] The schema for the header parameters, or `nil` if not found.
91
147
  def header_parameters_schema
92
148
  @header_parameters_schema ||= build_schema(header_parameters)
93
149
  end
94
150
 
151
+ # Returns the schema for the cookie parameters.
152
+ # @visibility private
153
+ # @return [Schema, nil] The schema for the cookie parameters, or `nil` if not found.
95
154
  def cookie_parameters_schema
96
155
  @cookie_parameters_schema ||= build_schema(cookie_parameters)
97
156
  end
98
157
 
99
158
  private
100
159
 
160
+ WRITE_METHODS = Set.new(%w[post put patch delete]).freeze
161
+
101
162
  IGNORED_HEADERS = Set['Content-Type', 'Accept', 'Authorization'].freeze
102
- private_constant :IGNORED_HEADERS
103
163
 
104
164
  def all_parameters
105
165
  @all_parameters ||= (@path_item_object.fetch('parameters', []) + operation_object.fetch('parameters', []))
@@ -4,6 +4,7 @@ require_relative 'operation'
4
4
 
5
5
  module OpenapiFirst
6
6
  class Definition
7
+ # A pathItem as defined in the OpenAPI document.
7
8
  class PathItem
8
9
  def initialize(path, path_item_object, openapi_version:)
9
10
  @path = path
@@ -4,6 +4,7 @@ require_relative '../schema'
4
4
 
5
5
  module OpenapiFirst
6
6
  class Definition
7
+ # Represents a request body definition in the OpenAPI document that belongs to an operation.
7
8
  class RequestBody
8
9
  def initialize(request_body_object, operation)
9
10
  @request_body_object = request_body_object
@@ -2,6 +2,9 @@
2
2
 
3
3
  module OpenapiFirst
4
4
  class Definition
5
+ # Represents a response definition in the OpenAPI document.
6
+ # This is not a direct reflecton of the OpenAPI 3.X response definition, but a combination of
7
+ # status, content type and content schema.
5
8
  class Response
6
9
  def initialize(operation:, status:, response_object:, content_type:, content_schema:)
7
10
  @operation = operation
@@ -11,6 +14,10 @@ module OpenapiFirst
11
14
  @content_schema = content_schema
12
15
  end
13
16
 
17
+ # @attr_reader [Operation] operation The operation this response belongs to.
18
+ # @attr_reader [Integer] status The HTTP status code of the response definition.
19
+ # @attr_reader [String, nil] content_type Content type of this response.
20
+ # @attr_reader [Schema, nil] content_schema the Schema of the response body.
14
21
  attr_reader :operation, :status, :content_type, :content_schema
15
22
 
16
23
  def headers
@@ -1,20 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'mustermann/template'
3
+ require 'mustermann'
4
4
  require_relative 'definition/path_item'
5
5
  require_relative 'runtime_request'
6
+ require_relative 'request_validation/validator'
7
+ require_relative 'response_validation/validator'
6
8
 
7
9
  module OpenapiFirst
8
10
  # Represents an OpenAPI API Description document
11
+ # This is returned by OpenapiFirst.load.
9
12
  class Definition
10
13
  attr_reader :filepath, :paths, :openapi_version
11
14
 
12
- def initialize(resolved, filepath)
15
+ # @param resolved [Hash] The resolved OpenAPI document.
16
+ # @param filepath [String] The file path of the OpenAPI document.
17
+ def initialize(resolved, filepath = nil)
13
18
  @filepath = filepath
14
19
  @paths = resolved['paths']
15
20
  @openapi_version = detect_version(resolved)
16
21
  end
17
22
 
23
+ # Validates the request against the API description.
24
+ # @param rack_request [Rack::Request] The Rack request object.
25
+ # @param raise_error [Boolean] Whether to raise an error if validation fails.
26
+ # @return [RuntimeRequest] The validated request object.
27
+ def validate_request(rack_request, raise_error: false)
28
+ validated = request(rack_request).tap(&:validate)
29
+ validated.error&.raise! if raise_error
30
+ validated
31
+ end
32
+
33
+ # Validates the response against the API description.
34
+ # @param rack_request [Rack::Request] The Rack request object.
35
+ # @param rack_response [Rack::Response] The Rack response object.
36
+ # @param raise_error [Boolean] Whether to raise an error if validation fails.
37
+ # @return [RuntimeResponse] The validated response object.
38
+ def validate_response(rack_request, rack_response, raise_error: false)
39
+ request(rack_request).validate_response(rack_response, raise_error:)
40
+ end
41
+
42
+ # Builds a RuntimeRequest object based on the Rack request.
43
+ # @param rack_request [Rack::Request] The Rack request object.
44
+ # @return [RuntimeRequest] The RuntimeRequest object.
18
45
  def request(rack_request)
19
46
  path_item, path_params = find_path_item_and_params(rack_request.path)
20
47
  operation = path_item&.operation(rack_request.request_method.downcase)
@@ -26,14 +53,25 @@ module OpenapiFirst
26
53
  )
27
54
  end
28
55
 
56
+ # Builds a RuntimeResponse object based on the Rack request and response.
57
+ # @param rack_request [Rack::Request] The Rack request object.
58
+ # @param rack_response [Rack::Response] The Rack response object.
59
+ # @return [RuntimeResponse] The RuntimeResponse object.
29
60
  def response(rack_request, rack_response)
30
61
  request(rack_request).response(rack_response)
31
62
  end
32
63
 
64
+ # Gets all the operations defined in the API description.
65
+ # @return [Array<Operation>] An array of Operation objects.
33
66
  def operations
34
67
  @operations ||= path_items.flat_map(&:operations)
35
68
  end
36
69
 
70
+ # Gets the PathItem object for the specified path.
71
+ # @param pathname [String] The path template string.
72
+ # @return [PathItem] The PathItem object.
73
+ # Example:
74
+ # definition.path('/pets/{id}')
37
75
  def path(pathname)
38
76
  return unless paths.key?(pathname)
39
77
 
@@ -42,6 +80,8 @@ module OpenapiFirst
42
80
 
43
81
  private
44
82
 
83
+ # Gets all the PathItem objects defined in the API description.
84
+ # @return [Array] An array of PathItem objects.
45
85
  def path_items
46
86
  @path_items ||= paths.flat_map do |path, path_item_object|
47
87
  PathItem.new(path, path_item_object, openapi_version:)
@@ -60,7 +100,7 @@ module OpenapiFirst
60
100
 
61
101
  def search_for_path_item(request_path)
62
102
  paths.find do |path, path_item_object|
63
- template = Mustermann::Template.new(path)
103
+ template = Mustermann.new(path)
64
104
  path_params = template.params(request_path)
65
105
  next unless path_params
66
106
  next unless path_params.size == template.names.size
@@ -29,7 +29,7 @@ module OpenapiFirst
29
29
 
30
30
  # The response status
31
31
  def status
32
- STATUS[failure.error_type] || 400
32
+ STATUS[failure.type] || 400
33
33
  end
34
34
 
35
35
  # Render this error response
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
+ # @!visibility private
4
5
  class Error < StandardError; end
6
+ # @!visibility private
5
7
  class ParseError < Error; end
8
+ # @!visibility private
6
9
  class NotFoundError < Error; end
10
+ # @!visibility private
7
11
  class RequestInvalidError < Error; end
12
+ # @!visibility private
8
13
  class ResponseNotFoundError < Error; end
14
+ # @!visibility private
9
15
  class ResponseInvalidError < Error; end
10
16
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
+ # A failure object returned when validation of request or response has failed.
4
5
  class Failure
5
6
  FAILURE = :openapi_first_validation_failure
6
7
 
@@ -29,7 +30,7 @@ module OpenapiFirst
29
30
  )
30
31
  end
31
32
 
32
- # @param type [Symbol] See TYPES.keys
33
+ # @param error_type [Symbol] See TYPES.keys
33
34
  # @param message [String] A generic error message
34
35
  # @param errors [Array<OpenapiFirst::Schema::ValidationError>]
35
36
  def initialize(error_type, message: nil, errors: nil)
@@ -43,13 +44,36 @@ module OpenapiFirst
43
44
  @errors = errors
44
45
  end
45
46
 
46
- attr_reader :error_type, :message, :errors
47
+ # @attr_reader [Symbol] error_type The type of the failure. See TYPES.keys.
48
+ # @alias type error_type
49
+ # Example: :invalid_body
50
+ attr_reader :error_type
51
+ alias type error_type
52
+
53
+ # @attr_reader [String] message A generic error message
54
+ attr_reader :message
55
+
56
+ # @attr_reader [Array<OpenapiFirst::Schema::ValidationError>] errors Schema validation errors
57
+ attr_reader :errors
47
58
 
48
59
  # Raise an exception that fits the failure.
49
60
  def raise!
50
- exception, message_prefix = TYPES.fetch(error_type)
61
+ exception, = TYPES.fetch(error_type)
62
+ raise exception, exception_message
63
+ end
64
+
65
+ def exception_message
66
+ _, message_prefix = TYPES.fetch(error_type)
67
+
68
+ "#{message_prefix} #{@message || generate_message}"
69
+ end
70
+
71
+ private
51
72
 
52
- raise exception, "#{message_prefix} #{@message || errors&.map(&:error)&.join('. ')}"
73
+ def generate_message
74
+ messages = errors&.take(4)&.map(&:error)
75
+ messages << "... (#{errors.size} errors total)" if errors && errors.size > 4
76
+ messages&.join('. ')
53
77
  end
54
78
  end
55
79
  end
@@ -26,11 +26,8 @@ module OpenapiFirst
26
26
  request = find_request(env)
27
27
  return @app.call(env) unless request
28
28
 
29
- failure = if @raise
30
- request.validate!
31
- else
32
- request.validate
33
- end
29
+ failure = request.validate
30
+ failure.raise! if failure && @raise
34
31
  return @error_response_class.new(failure:).render if failure
35
32
 
36
33
  @app.call(env)
@@ -20,11 +20,8 @@ module OpenapiFirst
20
20
  def call(env)
21
21
  request = find_request(env)
22
22
  status, headers, body = @app.call(env)
23
-
24
23
  body = body.to_ary if body.respond_to?(:to_ary)
25
-
26
- request.response(Rack::Response[status, headers, body]).validate!
27
-
24
+ request.validate_response(Rack::Response[status, headers, body], raise_error: true)
28
25
  [status, headers, body]
29
26
  end
30
27
 
@@ -29,10 +29,10 @@ module OpenapiFirst
29
29
  MultiJson.dump(result)
30
30
  end
31
31
 
32
- def error_type = failure.error_type
32
+ def type = failure.type
33
33
 
34
34
  def title
35
- TITLES.fetch(error_type)
35
+ TITLES.fetch(type)
36
36
  end
37
37
 
38
38
  def content_type
@@ -51,7 +51,7 @@ module OpenapiFirst
51
51
  end
52
52
 
53
53
  def pointer_key
54
- case error_type
54
+ case type
55
55
  when :invalid_body
56
56
  :pointer
57
57
  when :invalid_query, :invalid_path
@@ -64,7 +64,7 @@ module OpenapiFirst
64
64
  end
65
65
 
66
66
  def pointer(data_pointer)
67
- return data_pointer if error_type == :invalid_body
67
+ return data_pointer if type == :invalid_body
68
68
 
69
69
  data_pointer.delete_prefix('/')
70
70
  end
@@ -4,7 +4,7 @@ require_relative 'default/error_response'
4
4
 
5
5
  module OpenapiFirst
6
6
  module Plugins
7
- module Default
7
+ module Default # :nodoc:
8
8
  OpenapiFirst.register(:default, self)
9
9
  end
10
10
  end
@@ -3,6 +3,7 @@
3
3
  module OpenapiFirst
4
4
  module Plugins
5
5
  module Jsonapi
6
+ # A JSON:API conform error response. See https://jsonapi.org/.
6
7
  class ErrorResponse
7
8
  include OpenapiFirst::ErrorResponse
8
9
 
@@ -36,7 +37,7 @@ module OpenapiFirst
36
37
  end
37
38
 
38
39
  def pointer_key
39
- case failure.error_type
40
+ case failure.type
40
41
  when :invalid_body
41
42
  :pointer
42
43
  when :invalid_query, :invalid_path
@@ -49,7 +50,7 @@ module OpenapiFirst
49
50
  end
50
51
 
51
52
  def pointer(data_pointer)
52
- return data_pointer if failure.error_type == :invalid_body
53
+ return data_pointer if failure.type == :invalid_body
53
54
 
54
55
  data_pointer.delete_prefix('/')
55
56
  end
@@ -4,7 +4,7 @@ require_relative 'jsonapi/error_response'
4
4
 
5
5
  module OpenapiFirst
6
6
  module Plugins
7
- module Jsonapi
7
+ module Jsonapi # :nodoc:
8
8
  OpenapiFirst.register(:jsonapi, self)
9
9
  end
10
10
  end
@@ -4,6 +4,7 @@ module OpenapiFirst
4
4
  # Plugin System adapted from
5
5
  # Polished Ruby Programming by Jeremy Evans
6
6
  # https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewBook?id=0
7
+ # @!visibility private
7
8
  module Plugins
8
9
  PLUGINS = {} # rubocop:disable Style/MutableConstant
9
10
 
@@ -4,7 +4,7 @@ require_relative '../failure'
4
4
 
5
5
  module OpenapiFirst
6
6
  module RequestValidation
7
- class RequestBodyValidator
7
+ class RequestBodyValidator # :nodoc:
8
8
  def initialize(operation)
9
9
  @operation = operation
10
10
  end
@@ -5,6 +5,7 @@ require_relative 'request_body_validator'
5
5
 
6
6
  module OpenapiFirst
7
7
  module RequestValidation
8
+ # Validates a RuntimeRequest against an Operation.
8
9
  class Validator
9
10
  def initialize(operation)
10
11
  @operation = operation
@@ -4,6 +4,7 @@ require_relative '../failure'
4
4
 
5
5
  module OpenapiFirst
6
6
  module ResponseValidation
7
+ # Validates a RuntimeResponse against an Operation.
7
8
  class Validator
8
9
  def initialize(operation)
9
10
  @operation = operation