openapi_first 2.1.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +0 -3
- data/lib/openapi_first/body_parser.rb +29 -16
- data/lib/openapi_first/builder.rb +143 -30
- data/lib/openapi_first/definition.rb +5 -5
- data/lib/openapi_first/error_responses/default.rb +1 -1
- data/lib/openapi_first/error_responses/jsonapi.rb +1 -1
- data/lib/openapi_first/file_loader.rb +21 -0
- data/lib/openapi_first/json.rb +26 -0
- data/lib/openapi_first/json_pointer.rb +22 -0
- data/lib/openapi_first/ref_resolver.rb +141 -0
- data/lib/openapi_first/request.rb +17 -56
- data/lib/openapi_first/request_body_parsers.rb +47 -0
- data/lib/openapi_first/request_parser.rb +11 -9
- data/lib/openapi_first/request_validator.rb +16 -9
- data/lib/openapi_first/response.rb +3 -21
- data/lib/openapi_first/response_body_parsers.rb +29 -0
- data/lib/openapi_first/response_parser.rb +9 -26
- data/lib/openapi_first/response_validator.rb +2 -2
- data/lib/openapi_first/validated_request.rb +13 -29
- data/lib/openapi_first/validators/request_body.rb +9 -23
- data/lib/openapi_first/validators/request_parameters.rb +17 -25
- data/lib/openapi_first/validators/response_body.rb +7 -3
- data/lib/openapi_first/validators/response_headers.rb +6 -4
- data/lib/openapi_first/version.rb +1 -1
- data/lib/openapi_first.rb +10 -17
- metadata +9 -23
- data/lib/openapi_first/json_refs.rb +0 -151
- data/lib/openapi_first/schema.rb +0 -44
@@ -11,82 +11,43 @@ module OpenapiFirst
|
|
11
11
|
# This class represents one of those requests.
|
12
12
|
class Request
|
13
13
|
def initialize(path:, request_method:, operation_object:,
|
14
|
-
parameters:, content_type:, content_schema:, required_body
|
15
|
-
hooks:, openapi_version:)
|
14
|
+
parameters:, content_type:, content_schema:, required_body:)
|
16
15
|
@path = path
|
17
16
|
@request_method = request_method
|
18
17
|
@content_type = content_type
|
19
18
|
@content_schema = content_schema
|
20
|
-
@required_request_body = required_body == true
|
21
19
|
@operation = operation_object
|
22
|
-
@parameters = build_parameters(parameters)
|
23
20
|
@request_parser = RequestParser.new(
|
24
|
-
query_parameters:
|
25
|
-
path_parameters:
|
26
|
-
header_parameters:
|
27
|
-
cookie_parameters:
|
21
|
+
query_parameters: parameters.query,
|
22
|
+
path_parameters: parameters.path,
|
23
|
+
header_parameters: parameters.header,
|
24
|
+
cookie_parameters: parameters.cookie,
|
28
25
|
content_type:
|
29
26
|
)
|
30
|
-
@validator = RequestValidator.new(
|
27
|
+
@validator = RequestValidator.new(
|
28
|
+
content_schema:,
|
29
|
+
required_request_body: required_body == true,
|
30
|
+
path_schema: parameters.path_schema,
|
31
|
+
query_schema: parameters.query_schema,
|
32
|
+
header_schema: parameters.header_schema,
|
33
|
+
cookie_schema: parameters.cookie_schema
|
34
|
+
)
|
31
35
|
end
|
32
36
|
|
33
37
|
attr_reader :content_type, :content_schema, :operation, :request_method, :path
|
34
38
|
|
35
39
|
def validate(request, route_params:)
|
36
|
-
|
40
|
+
parsed_request = nil
|
37
41
|
error = catch FAILURE do
|
38
|
-
|
39
|
-
@validator.call(
|
42
|
+
parsed_request = @request_parser.parse(request, route_params:)
|
43
|
+
@validator.call(parsed_request)
|
40
44
|
nil
|
41
45
|
end
|
42
|
-
ValidatedRequest.new(request,
|
43
|
-
end
|
44
|
-
|
45
|
-
# These return a Schema instance for each type of parameters
|
46
|
-
%i[path query header cookie].each do |location|
|
47
|
-
define_method(:"#{location}_schema") do
|
48
|
-
build_parameters_schema(@parameters[location])
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def required_request_body?
|
53
|
-
@required_request_body
|
46
|
+
ValidatedRequest.new(request, parsed_request:, error:, request_definition: self)
|
54
47
|
end
|
55
48
|
|
56
49
|
def operation_id
|
57
50
|
@operation['operationId']
|
58
51
|
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
IGNORED_HEADERS = Set['Content-Type', 'Accept', 'Authorization'].freeze
|
63
|
-
private_constant :IGNORED_HEADERS
|
64
|
-
|
65
|
-
def build_parameters(parameter_definitions)
|
66
|
-
result = {}
|
67
|
-
parameter_definitions&.each do |parameter|
|
68
|
-
(result[parameter['in'].to_sym] ||= []) << parameter
|
69
|
-
end
|
70
|
-
result[:header]&.reject! { IGNORED_HEADERS.include?(_1['name']) }
|
71
|
-
result
|
72
|
-
end
|
73
|
-
|
74
|
-
def build_parameters_schema(parameters)
|
75
|
-
return unless parameters
|
76
|
-
|
77
|
-
properties = {}
|
78
|
-
required = []
|
79
|
-
parameters.each do |parameter|
|
80
|
-
schema = parameter['schema']
|
81
|
-
name = parameter['name']
|
82
|
-
properties[name] = schema if schema
|
83
|
-
required << name if parameter['required']
|
84
|
-
end
|
85
|
-
|
86
|
-
{
|
87
|
-
'properties' => properties,
|
88
|
-
'required' => required
|
89
|
-
}
|
90
|
-
end
|
91
52
|
end
|
92
53
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenapiFirst
|
4
|
+
# @!visibility private
|
5
|
+
module RequestBodyParsers
|
6
|
+
DEFAULT = ->(request) { Utils.read_body(request) }
|
7
|
+
|
8
|
+
@parsers = {}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_reader :parsers
|
12
|
+
|
13
|
+
def register(pattern, parser)
|
14
|
+
parsers[pattern] = parser
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](content_type)
|
18
|
+
key = parsers.keys.find { content_type.match?(_1) }
|
19
|
+
parsers.fetch(key) { DEFAULT }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Not sure where to put this
|
24
|
+
module Utils
|
25
|
+
def self.read_body(request)
|
26
|
+
body = request.body&.read
|
27
|
+
request.body.rewind if request.body.respond_to?(:rewind)
|
28
|
+
body
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
register(/json/i, lambda do |request|
|
33
|
+
body = Utils.read_body(request)
|
34
|
+
JSON.parse(body) unless body.nil? || body.empty?
|
35
|
+
rescue JSON::ParserError
|
36
|
+
Failure.fail!(:invalid_body, message: 'Failed to parse request body as JSON')
|
37
|
+
end)
|
38
|
+
|
39
|
+
register('multipart/form-data', lambda { |request|
|
40
|
+
request.POST.transform_values do |value|
|
41
|
+
value.is_a?(Hash) && value[:tempfile] ? value[:tempfile].read : value
|
42
|
+
end
|
43
|
+
})
|
44
|
+
|
45
|
+
register('application/x-www-form-urlencoded', lambda(&:POST))
|
46
|
+
end
|
47
|
+
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'openapi_parameters'
|
4
|
-
require_relative '
|
4
|
+
require_relative 'request_body_parsers'
|
5
5
|
|
6
6
|
module OpenapiFirst
|
7
|
+
ParsedRequest = Data.define(:path, :query, :headers, :body, :cookies)
|
8
|
+
|
7
9
|
# Parse a request
|
8
10
|
class RequestParser
|
9
11
|
def initialize(
|
@@ -17,19 +19,19 @@ module OpenapiFirst
|
|
17
19
|
@path_parser = OpenapiParameters::Path.new(path_parameters) if path_parameters
|
18
20
|
@headers_parser = OpenapiParameters::Header.new(header_parameters) if header_parameters
|
19
21
|
@cookies_parser = OpenapiParameters::Cookie.new(cookie_parameters) if cookie_parameters
|
20
|
-
@
|
22
|
+
@body_parsers = RequestBodyParsers[content_type] if content_type
|
21
23
|
end
|
22
24
|
|
23
25
|
attr_reader :query, :path, :headers, :cookies
|
24
26
|
|
25
27
|
def parse(request, route_params:)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
ParsedRequest.new(
|
29
|
+
path: @path_parser&.unpack(route_params),
|
30
|
+
query: @query_parser&.unpack(request.env[Rack::QUERY_STRING]),
|
31
|
+
headers: @headers_parser&.unpack_env(request.env),
|
32
|
+
cookies: @cookies_parser&.unpack(request.env[Rack::HTTP_COOKIE]),
|
33
|
+
body: @body_parsers&.call(request)
|
34
|
+
)
|
33
35
|
end
|
34
36
|
end
|
35
37
|
end
|
@@ -7,15 +7,22 @@ require_relative 'validators/request_body'
|
|
7
7
|
module OpenapiFirst
|
8
8
|
# Validates a Request against a request definition.
|
9
9
|
class RequestValidator
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
10
|
+
def initialize(
|
11
|
+
content_schema:,
|
12
|
+
required_request_body:,
|
13
|
+
path_schema:,
|
14
|
+
query_schema:,
|
15
|
+
header_schema:,
|
16
|
+
cookie_schema:
|
17
|
+
)
|
18
|
+
@validators = []
|
19
|
+
@validators << Validators::RequestBody.new(content_schema:, required_request_body:) if content_schema
|
20
|
+
@validators.concat Validators::RequestParameters.for(
|
21
|
+
path_schema:,
|
22
|
+
query_schema:,
|
23
|
+
header_schema:,
|
24
|
+
cookie_schema:
|
25
|
+
)
|
19
26
|
end
|
20
27
|
|
21
28
|
def call(parsed_request)
|
@@ -9,14 +9,14 @@ module OpenapiFirst
|
|
9
9
|
# This is not a direct reflecton of the OpenAPI 3.X response definition, but a combination of
|
10
10
|
# status, content type and content schema.
|
11
11
|
class Response
|
12
|
-
def initialize(status:, headers:, content_type:, content_schema
|
12
|
+
def initialize(status:, headers:, headers_schema:, content_type:, content_schema:)
|
13
13
|
@status = status
|
14
14
|
@content_type = content_type
|
15
15
|
@content_schema = content_schema
|
16
16
|
@headers = headers
|
17
|
-
@headers_schema =
|
17
|
+
@headers_schema = headers_schema
|
18
18
|
@parser = ResponseParser.new(headers:, content_type:)
|
19
|
-
@validator = ResponseValidator.new(self
|
19
|
+
@validator = ResponseValidator.new(self)
|
20
20
|
end
|
21
21
|
|
22
22
|
# @attr_reader [Integer] status The HTTP status code of the response definition.
|
@@ -35,23 +35,5 @@ module OpenapiFirst
|
|
35
35
|
def parse(request)
|
36
36
|
@parser.parse(request)
|
37
37
|
end
|
38
|
-
|
39
|
-
def build_headers_schema(headers_object)
|
40
|
-
return unless headers_object&.any?
|
41
|
-
|
42
|
-
properties = {}
|
43
|
-
required = []
|
44
|
-
headers_object.each do |name, header|
|
45
|
-
schema = header['schema']
|
46
|
-
next if name.casecmp('content-type').zero?
|
47
|
-
|
48
|
-
properties[name] = schema if schema
|
49
|
-
required << name if header['required']
|
50
|
-
end
|
51
|
-
{
|
52
|
-
'properties' => properties,
|
53
|
-
'required' => required
|
54
|
-
}
|
55
|
-
end
|
56
38
|
end
|
57
39
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenapiFirst
|
4
|
+
# @visibility private
|
5
|
+
module ResponseBodyParsers
|
6
|
+
DEFAULT = ->(body) { body }
|
7
|
+
|
8
|
+
@parsers = {}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_reader :parsers
|
12
|
+
|
13
|
+
def register(pattern, parser)
|
14
|
+
parsers[pattern] = parser
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](content_type)
|
18
|
+
key = parsers.keys.find { content_type&.match?(_1) }
|
19
|
+
parsers.fetch(key) { DEFAULT }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
register(/json/i, lambda do |body|
|
24
|
+
JSON.parse(body)
|
25
|
+
rescue JSON::ParserError
|
26
|
+
Failure.fail!(:invalid_response_body, message: 'Response body is invalid: Failed to parse response body as JSON')
|
27
|
+
end)
|
28
|
+
end
|
29
|
+
end
|
@@ -1,40 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'response_body_parsers'
|
4
|
+
|
3
5
|
module OpenapiFirst
|
4
6
|
ParsedResponse = Data.define(:body, :headers)
|
5
7
|
|
6
8
|
# Parse a response
|
7
9
|
class ResponseParser
|
8
10
|
def initialize(headers:, content_type:)
|
9
|
-
@
|
10
|
-
@
|
11
|
+
@headers_parser = build_headers_parser(headers)
|
12
|
+
@body_parser = ResponseBodyParsers[content_type]
|
11
13
|
end
|
12
14
|
|
13
15
|
def parse(rack_response)
|
14
16
|
ParsedResponse.new(
|
15
|
-
body:
|
16
|
-
headers:
|
17
|
+
body: @body_parser.call(read_body(rack_response)),
|
18
|
+
headers: @headers_parser.call(rack_response.headers)
|
17
19
|
)
|
18
20
|
end
|
19
21
|
|
20
22
|
private
|
21
23
|
|
22
|
-
attr_reader :headers
|
23
|
-
|
24
|
-
def json? = @json
|
25
|
-
|
26
|
-
def parse_body(body)
|
27
|
-
return parse_json(body) if json?
|
28
|
-
|
29
|
-
body
|
30
|
-
end
|
31
|
-
|
32
|
-
def parse_json(body)
|
33
|
-
MultiJson.load(body)
|
34
|
-
rescue MultiJson::ParseError
|
35
|
-
Failure.fail!(:invalid_response_body, message: 'Response body is invalid: Failed to parse response body as JSON')
|
36
|
-
end
|
37
|
-
|
38
24
|
def read_body(rack_response)
|
39
25
|
buffered_body = +''
|
40
26
|
if rack_response.body.respond_to?(:each)
|
@@ -44,14 +30,11 @@ module OpenapiFirst
|
|
44
30
|
rack_response.body
|
45
31
|
end
|
46
32
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
# TODO: memoize unpacker
|
51
|
-
headers_as_parameters = headers.map do |name, definition|
|
33
|
+
def build_headers_parser(header_definitions)
|
34
|
+
headers_as_parameters = header_definitions.to_a.map do |name, definition|
|
52
35
|
definition.merge('name' => name, 'in' => 'header')
|
53
36
|
end
|
54
|
-
OpenapiParameters::Header.new(headers_as_parameters).unpack
|
37
|
+
OpenapiParameters::Header.new(headers_as_parameters).method(:unpack)
|
55
38
|
end
|
56
39
|
end
|
57
40
|
end
|
@@ -11,9 +11,9 @@ module OpenapiFirst
|
|
11
11
|
Validators::ResponseBody
|
12
12
|
].freeze
|
13
13
|
|
14
|
-
def initialize(response_definition
|
14
|
+
def initialize(response_definition)
|
15
15
|
@validators = VALIDATORS.filter_map do |klass|
|
16
|
-
klass.for(response_definition
|
16
|
+
klass.for(response_definition)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -8,9 +8,9 @@ module OpenapiFirst
|
|
8
8
|
class ValidatedRequest < SimpleDelegator
|
9
9
|
extend Forwardable
|
10
10
|
|
11
|
-
def initialize(original_request, error:,
|
11
|
+
def initialize(original_request, error:, parsed_request: nil, request_definition: nil)
|
12
12
|
super(original_request)
|
13
|
-
@
|
13
|
+
@parsed_request = parsed_request
|
14
14
|
@error = error
|
15
15
|
@request_definition = request_definition
|
16
16
|
end
|
@@ -33,57 +33,41 @@ module OpenapiFirst
|
|
33
33
|
|
34
34
|
# Parsed path parameters
|
35
35
|
# @return [Hash<String, anything>]
|
36
|
-
def parsed_path_parameters
|
37
|
-
@parsed_values[:path]
|
38
|
-
end
|
36
|
+
def parsed_path_parameters = @parsed_request&.path || {}
|
39
37
|
|
40
38
|
# Parsed query parameters. This only returns the query parameters that are defined in the OpenAPI spec.
|
41
39
|
# @return [Hash<String, anything>]
|
42
|
-
def parsed_query
|
43
|
-
@parsed_values[:query]
|
44
|
-
end
|
40
|
+
def parsed_query = @parsed_request&.query || {}
|
45
41
|
|
46
42
|
# Parsed headers. This only returns the query parameters that are defined in the OpenAPI spec.
|
47
43
|
# @return [Hash<String, anything>]
|
48
|
-
def parsed_headers
|
49
|
-
@parsed_values[:headers]
|
50
|
-
end
|
44
|
+
def parsed_headers = @parsed_request&.headers || {}
|
51
45
|
|
52
46
|
# Parsed cookies. This only returns the query parameters that are defined in the OpenAPI spec.
|
53
47
|
# @return [Hash<String, anything>]
|
54
|
-
def parsed_cookies
|
55
|
-
@parsed_values[:cookies]
|
56
|
-
end
|
48
|
+
def parsed_cookies = @parsed_request&.cookies || {}
|
57
49
|
|
58
50
|
# Parsed body. This parses the body according to the content type.
|
59
51
|
# Note that this returns the hole body, not only the fields that are defined in the OpenAPI spec.
|
60
52
|
# You can use JSON Schemas `additionalProperties` or `unevaluatedProperties` to
|
61
|
-
#
|
62
|
-
# @return [Hash<String, anything
|
63
|
-
def parsed_body
|
64
|
-
@parsed_values[:body]
|
65
|
-
end
|
53
|
+
# return a validation error if the body contains unknown fields.
|
54
|
+
# @return [Hash<String, anything>, anything]
|
55
|
+
def parsed_body = @parsed_request&.body
|
66
56
|
|
67
57
|
# Checks if the request is valid.
|
68
|
-
def valid?
|
69
|
-
error.nil?
|
70
|
-
end
|
58
|
+
def valid? = error.nil?
|
71
59
|
|
72
60
|
# Checks if the request is invalid.
|
73
|
-
def invalid?
|
74
|
-
!valid?
|
75
|
-
end
|
61
|
+
def invalid? = !valid?
|
76
62
|
|
77
63
|
# Returns true if the request is defined.
|
78
|
-
def known?
|
79
|
-
request_definition != nil
|
80
|
-
end
|
64
|
+
def known? = request_definition != nil
|
81
65
|
|
82
66
|
# Merged path, query, body parameters.
|
83
67
|
# Here path has the highest precedence, then query, then body.
|
84
68
|
# @return [Hash<String, anything>]
|
85
69
|
def parsed_params
|
86
|
-
@parsed_params ||= parsed_body.merge(parsed_query, parsed_path_parameters)
|
70
|
+
@parsed_params ||= parsed_body.to_h.merge(parsed_query, parsed_path_parameters) || {}
|
87
71
|
end
|
88
72
|
end
|
89
73
|
end
|
@@ -3,37 +3,23 @@
|
|
3
3
|
module OpenapiFirst
|
4
4
|
module Validators
|
5
5
|
class RequestBody
|
6
|
-
def
|
7
|
-
schema =
|
8
|
-
|
9
|
-
|
10
|
-
after_property_validation = hooks[:after_request_body_property_validation]
|
11
|
-
|
12
|
-
new(Schema.new(schema, after_property_validation:, openapi_version:),
|
13
|
-
required: request_definition.required_request_body?)
|
14
|
-
end
|
15
|
-
|
16
|
-
def initialize(schema, required:)
|
17
|
-
@schema = schema
|
18
|
-
@required = required
|
6
|
+
def initialize(content_schema:, required_request_body:)
|
7
|
+
@schema = content_schema
|
8
|
+
@required = required_request_body
|
19
9
|
end
|
20
10
|
|
21
|
-
def call(
|
22
|
-
|
23
|
-
if
|
11
|
+
def call(parsed_request)
|
12
|
+
body = parsed_request.body
|
13
|
+
if body.nil?
|
24
14
|
Failure.fail!(:invalid_body, message: 'Request body is not defined') if @required
|
25
15
|
return
|
26
16
|
end
|
27
17
|
|
28
|
-
validation =
|
18
|
+
validation = Schema::ValidationResult.new(
|
19
|
+
@schema.validate(body, access_mode: 'write')
|
20
|
+
)
|
29
21
|
Failure.fail!(:invalid_body, errors: validation.errors) if validation.error?
|
30
22
|
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def read_body(request)
|
35
|
-
request[:body]
|
36
|
-
end
|
37
23
|
end
|
38
24
|
end
|
39
25
|
end
|
@@ -2,31 +2,35 @@
|
|
2
2
|
|
3
3
|
module OpenapiFirst
|
4
4
|
module Validators
|
5
|
-
|
5
|
+
module RequestParameters
|
6
6
|
RequestHeaders = Data.define(:schema) do
|
7
|
-
def call(
|
8
|
-
validation = schema.validate(
|
7
|
+
def call(parsed_request)
|
8
|
+
validation = schema.validate(parsed_request.headers)
|
9
|
+
validation = Schema::ValidationResult.new(validation.to_a)
|
9
10
|
Failure.fail!(:invalid_header, errors: validation.errors) if validation.error?
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
13
14
|
Path = Data.define(:schema) do
|
14
|
-
def call(
|
15
|
-
validation = schema.validate(
|
15
|
+
def call(parsed_request)
|
16
|
+
validation = schema.validate(parsed_request.path)
|
17
|
+
validation = Schema::ValidationResult.new(validation.to_a)
|
16
18
|
Failure.fail!(:invalid_path, errors: validation.errors) if validation.error?
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
22
|
Query = Data.define(:schema) do
|
21
|
-
def call(
|
22
|
-
validation = schema.validate(
|
23
|
+
def call(parsed_request)
|
24
|
+
validation = schema.validate(parsed_request.query)
|
25
|
+
validation = Schema::ValidationResult.new(validation.to_a)
|
23
26
|
Failure.fail!(:invalid_query, errors: validation.errors) if validation.error?
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
27
30
|
RequestCookies = Data.define(:schema) do
|
28
|
-
def call(
|
29
|
-
validation = schema.validate(
|
31
|
+
def call(parsed_request)
|
32
|
+
validation = schema.validate(parsed_request.cookies)
|
33
|
+
validation = Schema::ValidationResult.new(validation.to_a)
|
30
34
|
Failure.fail!(:invalid_cookie, errors: validation.errors) if validation.error?
|
31
35
|
end
|
32
36
|
end
|
@@ -38,23 +42,11 @@ module OpenapiFirst
|
|
38
42
|
cookie_schema: RequestCookies
|
39
43
|
}.freeze
|
40
44
|
|
41
|
-
def self.for(
|
42
|
-
|
43
|
-
|
44
|
-
schema
|
45
|
-
klass.new(Schema.new(schema, after_property_validation:, openapi_version:)) if schema
|
45
|
+
def self.for(args)
|
46
|
+
VALIDATORS.filter_map do |key, klass|
|
47
|
+
schema = args[key]
|
48
|
+
klass.new(schema) if schema.value
|
46
49
|
end
|
47
|
-
return if validators.empty?
|
48
|
-
|
49
|
-
new(validators)
|
50
|
-
end
|
51
|
-
|
52
|
-
def initialize(validators)
|
53
|
-
@validators = validators
|
54
|
-
end
|
55
|
-
|
56
|
-
def call(parsed_values)
|
57
|
-
@validators.each { |validator| validator.call(parsed_values) }
|
58
50
|
end
|
59
51
|
end
|
60
52
|
end
|
@@ -1,13 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../schema/validation_result'
|
4
|
+
|
3
5
|
module OpenapiFirst
|
4
6
|
module Validators
|
5
7
|
class ResponseBody
|
6
|
-
def self.for(response_definition
|
8
|
+
def self.for(response_definition)
|
7
9
|
schema = response_definition&.content_schema
|
8
10
|
return unless schema
|
9
11
|
|
10
|
-
new(
|
12
|
+
new(schema)
|
11
13
|
end
|
12
14
|
|
13
15
|
def initialize(schema)
|
@@ -22,7 +24,9 @@ module OpenapiFirst
|
|
22
24
|
rescue ParseError => e
|
23
25
|
Failure.fail!(:invalid_response_body, message: e.message)
|
24
26
|
end
|
25
|
-
validation =
|
27
|
+
validation = Schema::ValidationResult.new(
|
28
|
+
schema.validate(parsed_body, access_mode: 'read')
|
29
|
+
)
|
26
30
|
Failure.fail!(:invalid_response_body, errors: validation.errors) if validation.error?
|
27
31
|
end
|
28
32
|
end
|
@@ -3,11 +3,11 @@
|
|
3
3
|
module OpenapiFirst
|
4
4
|
module Validators
|
5
5
|
class ResponseHeaders
|
6
|
-
def self.for(response_definition
|
6
|
+
def self.for(response_definition)
|
7
7
|
schema = response_definition&.headers_schema
|
8
|
-
return unless schema
|
8
|
+
return unless schema&.value
|
9
9
|
|
10
|
-
new(
|
10
|
+
new(schema)
|
11
11
|
end
|
12
12
|
|
13
13
|
def initialize(schema)
|
@@ -17,7 +17,9 @@ module OpenapiFirst
|
|
17
17
|
attr_reader :schema
|
18
18
|
|
19
19
|
def call(parsed_request)
|
20
|
-
validation =
|
20
|
+
validation = Schema::ValidationResult.new(
|
21
|
+
schema.validate(parsed_request.headers)
|
22
|
+
)
|
21
23
|
Failure.fail!(:invalid_response_header, errors: validation.errors) if validation.error?
|
22
24
|
end
|
23
25
|
end
|