openapi3_parser 0.4.0 → 0.5.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +8 -0
- data/README.md +1 -1
- data/TODO.md +4 -4
- data/lib/openapi3_parser/array_sentence.rb +12 -0
- data/lib/openapi3_parser/cautious_dig.rb +39 -0
- data/lib/openapi3_parser/context.rb +53 -1
- data/lib/openapi3_parser/context/location.rb +1 -0
- data/lib/openapi3_parser/context/pointer.rb +67 -5
- data/lib/openapi3_parser/document.rb +45 -4
- data/lib/openapi3_parser/error.rb +9 -0
- data/lib/openapi3_parser/node/array.rb +14 -4
- data/lib/openapi3_parser/node/map.rb +45 -3
- data/lib/openapi3_parser/node/object.rb +25 -5
- data/lib/openapi3_parser/node_factory.rb +0 -150
- data/lib/openapi3_parser/node_factory/array.rb +198 -0
- data/lib/openapi3_parser/node_factory/callback.rb +24 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/components.rb +24 -25
- data/lib/openapi3_parser/{node_factories → node_factory}/contact.rb +5 -6
- data/lib/openapi3_parser/{node_factories → node_factory}/discriminator.rb +6 -7
- data/lib/openapi3_parser/{node_factories → node_factory}/encoding.rb +6 -8
- data/lib/openapi3_parser/{node_factories → node_factory}/example.rb +4 -5
- data/lib/openapi3_parser/{node_factories → node_factory}/external_documentation.rb +4 -5
- data/lib/openapi3_parser/node_factory/field.rb +129 -0
- data/lib/openapi3_parser/node_factory/fields/reference.rb +54 -18
- data/lib/openapi3_parser/{node_factories → node_factory}/header.rb +4 -5
- data/lib/openapi3_parser/{node_factories → node_factory}/info.rb +6 -7
- data/lib/openapi3_parser/{node_factories → node_factory}/license.rb +4 -5
- data/lib/openapi3_parser/{node_factories → node_factory}/link.rb +6 -8
- data/lib/openapi3_parser/node_factory/map.rb +206 -21
- data/lib/openapi3_parser/{node_factories → node_factory}/media_type.rb +17 -16
- data/lib/openapi3_parser/{node_factories → node_factory}/oauth_flow.rb +2 -4
- data/lib/openapi3_parser/{node_factories → node_factory}/oauth_flows.rb +4 -6
- data/lib/openapi3_parser/node_factory/object.rb +66 -63
- data/lib/openapi3_parser/node_factory/object_factory/dsl.rb +50 -0
- data/lib/openapi3_parser/node_factory/object_factory/field_config.rb +88 -0
- data/lib/openapi3_parser/node_factory/object_factory/node_builder.rb +96 -0
- data/lib/openapi3_parser/node_factory/object_factory/validator.rb +172 -0
- data/lib/openapi3_parser/node_factory/openapi.rb +65 -0
- data/lib/openapi3_parser/node_factory/operation.rb +87 -0
- data/lib/openapi3_parser/node_factory/optional_reference.rb +7 -3
- data/lib/openapi3_parser/{node_factories → node_factory}/parameter.rb +16 -22
- data/lib/openapi3_parser/node_factory/parameter_like.rb +42 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/path_item.rb +27 -29
- data/lib/openapi3_parser/{node_factories → node_factory}/paths.rb +21 -27
- data/lib/openapi3_parser/node_factory/recursive_pointer.rb +17 -0
- data/lib/openapi3_parser/node_factory/reference.rb +48 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/request_body.rb +15 -19
- data/lib/openapi3_parser/{node_factories → node_factory}/response.rb +18 -21
- data/lib/openapi3_parser/node_factory/responses.rb +51 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/schema.rb +33 -33
- data/lib/openapi3_parser/node_factory/security_requirement.rb +21 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/security_scheme.rb +4 -6
- data/lib/openapi3_parser/{node_factories → node_factory}/server.rb +6 -8
- data/lib/openapi3_parser/{node_factories → node_factory}/server_variable.rb +10 -12
- data/lib/openapi3_parser/{node_factories → node_factory}/tag.rb +4 -6
- data/lib/openapi3_parser/node_factory/type_checker.rb +103 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/xml.rb +4 -5
- data/lib/openapi3_parser/source/reference_resolver.rb +3 -3
- data/lib/openapi3_parser/validation/error.rb +9 -0
- data/lib/openapi3_parser/validation/input_validator.rb +18 -0
- data/lib/openapi3_parser/validation/validatable.rb +44 -0
- data/lib/openapi3_parser/validators/mutually_exclusive_fields.rb +121 -0
- data/lib/openapi3_parser/validators/required_fields.rb +37 -0
- data/lib/openapi3_parser/validators/unexpected_fields.rb +52 -0
- data/lib/openapi3_parser/version.rb +1 -1
- metadata +48 -38
- data/lib/openapi3_parser/node_factories/array.rb +0 -114
- data/lib/openapi3_parser/node_factories/callback.rb +0 -27
- data/lib/openapi3_parser/node_factories/map.rb +0 -120
- data/lib/openapi3_parser/node_factories/openapi.rb +0 -62
- data/lib/openapi3_parser/node_factories/operation.rb +0 -84
- data/lib/openapi3_parser/node_factories/parameter/parameter_like.rb +0 -41
- data/lib/openapi3_parser/node_factories/reference.rb +0 -35
- data/lib/openapi3_parser/node_factories/responses.rb +0 -60
- data/lib/openapi3_parser/node_factories/security_requirement.rb +0 -26
- data/lib/openapi3_parser/node_factory/field_config.rb +0 -88
- data/lib/openapi3_parser/node_factory/object/node_builder.rb +0 -97
- data/lib/openapi3_parser/node_factory/object/validator.rb +0 -176
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "forwardable"
|
4
|
+
|
3
5
|
module Openapi3Parser
|
4
6
|
module Node
|
5
7
|
class Map
|
8
|
+
extend Forwardable
|
6
9
|
include Enumerable
|
7
10
|
|
11
|
+
def_delegators :node_data, :each, :keys, :empty?
|
8
12
|
attr_reader :node_data, :node_context
|
9
13
|
|
10
14
|
def initialize(data, context)
|
@@ -12,12 +16,50 @@ module Openapi3Parser
|
|
12
16
|
@node_context = context
|
13
17
|
end
|
14
18
|
|
19
|
+
# Look up an attribute of the node by the name it has in the OpenAPI
|
20
|
+
# document.
|
21
|
+
#
|
22
|
+
# @example Look up by OpenAPI naming
|
23
|
+
# obj["externalDocs"]
|
24
|
+
#
|
25
|
+
# @example Look up by symbol
|
26
|
+
# obj[:servers]
|
27
|
+
#
|
28
|
+
# @example Look up an extension
|
29
|
+
# obj["x-myExtension"]
|
30
|
+
#
|
31
|
+
# @param [String, Symbol] value
|
32
|
+
#
|
33
|
+
# @return anything
|
15
34
|
def [](value)
|
16
|
-
node_data[value]
|
35
|
+
node_data[value.to_s]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Look up an extension provided for this map, doesn't need a prefix of
|
39
|
+
# "x-"
|
40
|
+
#
|
41
|
+
# @example Looking up an extension provided as "x-extra"
|
42
|
+
# obj.extension("extra")
|
43
|
+
#
|
44
|
+
# @param [String, Symbol] value
|
45
|
+
#
|
46
|
+
# @return [Hash, Array, Numeric, String, true, false, nil]
|
47
|
+
def extension(value)
|
48
|
+
node_data["x-#{value}"]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Used to access a node relative to this node
|
52
|
+
# @param [Context::Pointer, ::Array, ::String] pointer_like
|
53
|
+
# @return anything
|
54
|
+
def node_at(pointer_like)
|
55
|
+
current_pointer = node_context.document_location.pointer
|
56
|
+
node_context.document.node_at(pointer_like, current_pointer)
|
17
57
|
end
|
18
58
|
|
19
|
-
|
20
|
-
|
59
|
+
# @return [String]
|
60
|
+
def inspect
|
61
|
+
fragment = node_context.document_location.pointer.fragment
|
62
|
+
%{#{self.class.name}(#{fragment})}
|
21
63
|
end
|
22
64
|
end
|
23
65
|
end
|
@@ -1,12 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "forwardable"
|
4
|
+
|
3
5
|
require "openapi3_parser/markdown"
|
4
6
|
|
5
7
|
module Openapi3Parser
|
6
8
|
module Node
|
7
9
|
class Object
|
10
|
+
extend Forwardable
|
8
11
|
include Enumerable
|
9
12
|
|
13
|
+
def_delegators :node_data, :each, :keys, :empty?
|
10
14
|
attr_reader :node_data, :node_context
|
11
15
|
|
12
16
|
def initialize(data, context)
|
@@ -46,11 +50,6 @@ module Openapi3Parser
|
|
46
50
|
node_data["x-#{value}"]
|
47
51
|
end
|
48
52
|
|
49
|
-
# Iterate through the attributes of this object
|
50
|
-
def each(&block)
|
51
|
-
node_data.each(&block)
|
52
|
-
end
|
53
|
-
|
54
53
|
# Used to render fields that can be in markdown syntax into HTML
|
55
54
|
# @param [String, nil] value
|
56
55
|
# @return [String, nil]
|
@@ -58,6 +57,27 @@ module Openapi3Parser
|
|
58
57
|
return if value.nil?
|
59
58
|
Markdown.to_html(value)
|
60
59
|
end
|
60
|
+
|
61
|
+
# Used to access a node relative to this node
|
62
|
+
#
|
63
|
+
# @example Looking up the parent node of this node
|
64
|
+
# obj.node_at("#..")
|
65
|
+
#
|
66
|
+
# @example Jumping way down the tree
|
67
|
+
# obj.node_at("#properties/Field/type")
|
68
|
+
#
|
69
|
+
# @param [Context::Pointer, ::Array, ::String] pointer_like
|
70
|
+
# @return anything
|
71
|
+
def node_at(pointer_like)
|
72
|
+
current_pointer = node_context.document_location.pointer
|
73
|
+
node_context.document.node_at(pointer_like, current_pointer)
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [String]
|
77
|
+
def inspect
|
78
|
+
fragment = node_context.document_location.pointer.fragment
|
79
|
+
%{#{self.class.name}(#{fragment})}
|
80
|
+
end
|
61
81
|
end
|
62
82
|
end
|
63
83
|
end
|
@@ -1,157 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "openapi3_parser/context"
|
4
|
-
require "openapi3_parser/error"
|
5
|
-
require "openapi3_parser/validation/error"
|
6
|
-
require "openapi3_parser/validation/error_collection"
|
7
|
-
|
8
3
|
module Openapi3Parser
|
9
4
|
module NodeFactory
|
10
|
-
module ClassMethods
|
11
|
-
def input_type(type)
|
12
|
-
@input_type = type
|
13
|
-
end
|
14
|
-
|
15
|
-
def valid_input_type?(type)
|
16
|
-
return true unless @input_type
|
17
|
-
type.is_a?(@input_type)
|
18
|
-
end
|
19
|
-
|
20
|
-
def expected_input_type
|
21
|
-
@input_type
|
22
|
-
end
|
23
|
-
|
24
|
-
def allow_default
|
25
|
-
@allow_default = true
|
26
|
-
end
|
27
|
-
|
28
|
-
def disallow_default
|
29
|
-
@allow_default = false
|
30
|
-
end
|
31
|
-
|
32
|
-
def allowed_default?
|
33
|
-
@allow_default.nil? || @allow_default
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.included(base)
|
38
|
-
base.extend(ClassMethods)
|
39
|
-
end
|
40
|
-
|
41
|
-
def allowed_default?
|
42
|
-
self.class.allowed_default?
|
43
|
-
end
|
44
|
-
|
45
5
|
EXTENSION_REGEX = /^x-(.*)/
|
46
|
-
|
47
|
-
attr_reader :context, :processed_input
|
48
|
-
|
49
|
-
def initialize(context)
|
50
|
-
@context = context
|
51
|
-
input = nil_input? ? default : context.input
|
52
|
-
@processed_input = input.nil? ? nil : process_input(input)
|
53
|
-
end
|
54
|
-
|
55
|
-
def valid?
|
56
|
-
errors.empty?
|
57
|
-
end
|
58
|
-
|
59
|
-
def errors
|
60
|
-
@errors ||= build_errors
|
61
|
-
end
|
62
|
-
|
63
|
-
def node
|
64
|
-
@node ||= build_valid_node
|
65
|
-
end
|
66
|
-
|
67
|
-
def nil_input?
|
68
|
-
context.input.nil?
|
69
|
-
end
|
70
|
-
|
71
|
-
def resolved_input
|
72
|
-
@resolved_input ||= processed_input ? build_resolved_input : nil
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
def validate(_input, _context); end
|
78
|
-
|
79
|
-
def validate_input
|
80
|
-
transform_errors(validate(context.input, context))
|
81
|
-
end
|
82
|
-
|
83
|
-
def build_resolved_input
|
84
|
-
context.input
|
85
|
-
end
|
86
|
-
|
87
|
-
def transform_errors(errors)
|
88
|
-
error_objects = Array(errors).map do |error|
|
89
|
-
if error.is_a?(Validation::Error)
|
90
|
-
error
|
91
|
-
else
|
92
|
-
Validation::Error.new(error, context, self.class)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
Validation::ErrorCollection.new(error_objects)
|
96
|
-
end
|
97
|
-
|
98
|
-
def build_errors
|
99
|
-
return Validation::ErrorCollection.new if nil_input? && allowed_default?
|
100
|
-
unless valid_type?
|
101
|
-
error = Validation::Error.new(
|
102
|
-
"Invalid type. #{validate_type}", context, self.class
|
103
|
-
)
|
104
|
-
return Validation::ErrorCollection.new([error])
|
105
|
-
end
|
106
|
-
validate_input
|
107
|
-
end
|
108
|
-
|
109
|
-
def build_valid_node
|
110
|
-
if nil_input? && allowed_default?
|
111
|
-
return default.nil? ? nil : build_node(processed_input)
|
112
|
-
end
|
113
|
-
|
114
|
-
unless valid_type?
|
115
|
-
raise Openapi3Parser::Error::InvalidType,
|
116
|
-
"Invalid type for #{context.location_summary}. "\
|
117
|
-
"#{validate_type}"
|
118
|
-
end
|
119
|
-
|
120
|
-
validate_before_build
|
121
|
-
build_node(processed_input)
|
122
|
-
end
|
123
|
-
|
124
|
-
def validate_before_build
|
125
|
-
errors = Array(validate(context.input, context))
|
126
|
-
return unless errors.any?
|
127
|
-
raise Openapi3Parser::Error::InvalidData,
|
128
|
-
"Invalid data for #{context.location_summary}. "\
|
129
|
-
"#{errors.join(', ')}"
|
130
|
-
end
|
131
|
-
|
132
|
-
def valid_type?
|
133
|
-
validate_type.nil?
|
134
|
-
end
|
135
|
-
|
136
|
-
def validate_type
|
137
|
-
valid_type = self.class.valid_input_type?(context.input)
|
138
|
-
return "Expected #{self.class.expected_input_type}" unless valid_type
|
139
|
-
end
|
140
|
-
|
141
|
-
def process_input(input)
|
142
|
-
input
|
143
|
-
end
|
144
|
-
|
145
|
-
def build_node(input)
|
146
|
-
input
|
147
|
-
end
|
148
|
-
|
149
|
-
def default
|
150
|
-
nil
|
151
|
-
end
|
152
|
-
|
153
|
-
def extension?(key)
|
154
|
-
key.match(EXTENSION_REGEX)
|
155
|
-
end
|
156
6
|
end
|
157
7
|
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openapi3_parser/context"
|
4
|
+
require "openapi3_parser/node_factory"
|
5
|
+
require "openapi3_parser/node_factory/type_checker"
|
6
|
+
require "openapi3_parser/node/array"
|
7
|
+
require "openapi3_parser/validation/validatable"
|
8
|
+
|
9
|
+
module Openapi3Parser
|
10
|
+
module NodeFactory
|
11
|
+
class Array
|
12
|
+
attr_reader :context, :data, :default, :value_input_type,
|
13
|
+
:value_factory, :validation
|
14
|
+
|
15
|
+
def initialize(
|
16
|
+
context,
|
17
|
+
default: [],
|
18
|
+
value_input_type: nil,
|
19
|
+
value_factory: nil,
|
20
|
+
validate: nil
|
21
|
+
)
|
22
|
+
@context = context
|
23
|
+
@default = default
|
24
|
+
@value_input_type = value_input_type
|
25
|
+
@value_factory = value_factory
|
26
|
+
@validation = validate
|
27
|
+
@data = build_data(context.input)
|
28
|
+
end
|
29
|
+
|
30
|
+
def raw_input
|
31
|
+
context.input
|
32
|
+
end
|
33
|
+
|
34
|
+
def resolved_input
|
35
|
+
@resolved_input ||= build_resolved_input
|
36
|
+
end
|
37
|
+
|
38
|
+
def nil_input?
|
39
|
+
context.input.nil?
|
40
|
+
end
|
41
|
+
|
42
|
+
def valid?
|
43
|
+
errors.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def errors
|
47
|
+
@errors ||= ValidNodeBuilder.errors(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
def node
|
51
|
+
@node ||= begin
|
52
|
+
data = ValidNodeBuilder.data(self)
|
53
|
+
data.nil? ? nil : build_node(data)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def inspect
|
58
|
+
%{#{self.class.name}(#{context.source_location.inspect})}
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def build_data(raw_input)
|
64
|
+
use_default = nil_input? || !raw_input.is_a?(::Array)
|
65
|
+
return if use_default && default.nil?
|
66
|
+
process_data(use_default ? default : raw_input)
|
67
|
+
end
|
68
|
+
|
69
|
+
def process_data(data)
|
70
|
+
data.each_with_index.map do |value, i|
|
71
|
+
if value_factory
|
72
|
+
initialize_value_factory(Context.next_field(context, i))
|
73
|
+
else
|
74
|
+
value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def initialize_value_factory(field_context)
|
80
|
+
if value_factory.is_a?(Class)
|
81
|
+
value_factory.new(field_context)
|
82
|
+
else
|
83
|
+
value_factory.call(field_context)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def build_node(data)
|
88
|
+
Node::Array.new(data, context) if data
|
89
|
+
end
|
90
|
+
|
91
|
+
def build_resolved_input
|
92
|
+
return unless data
|
93
|
+
|
94
|
+
data.map do |value|
|
95
|
+
value.respond_to?(:resolved_input) ? value.resolved_input : value
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class ValidNodeBuilder
|
100
|
+
def self.errors(factory)
|
101
|
+
new(factory).errors
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.data(factory)
|
105
|
+
new(factory).data
|
106
|
+
end
|
107
|
+
|
108
|
+
def initialize(factory)
|
109
|
+
@factory = factory
|
110
|
+
@validatable = Validation::Validatable.new(factory)
|
111
|
+
end
|
112
|
+
|
113
|
+
def errors
|
114
|
+
return validatable.collection if factory.nil_input?
|
115
|
+
TypeChecker.validate_type(validatable, type: ::Array)
|
116
|
+
return validatable.collection if validatable.errors.any?
|
117
|
+
collate_errors
|
118
|
+
validatable.collection
|
119
|
+
end
|
120
|
+
|
121
|
+
def data
|
122
|
+
return default_value if factory.nil_input?
|
123
|
+
|
124
|
+
TypeChecker.raise_on_invalid_type(factory.context, type: ::Array)
|
125
|
+
check_values(raise_on_invalid: true)
|
126
|
+
validate(raise_on_invalid: true)
|
127
|
+
|
128
|
+
factory.data.map do |value|
|
129
|
+
value.respond_to?(:node) ? value.node : value
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private_class_method :new
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
attr_reader :factory, :validatable
|
138
|
+
|
139
|
+
def collate_errors
|
140
|
+
check_values(raise_on_invalid: false)
|
141
|
+
validate(raise_on_invalid: false)
|
142
|
+
|
143
|
+
factory.data.each do |value|
|
144
|
+
validatable.add_errors(value.errors) if value.respond_to?(:errors)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def default_value
|
149
|
+
if factory.nil_input? && factory.default.nil?
|
150
|
+
nil
|
151
|
+
else
|
152
|
+
factory.data
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def check_values(raise_on_invalid: false)
|
157
|
+
return unless factory.value_input_type
|
158
|
+
|
159
|
+
factory.context.input.each_index do |index|
|
160
|
+
check_field_type(
|
161
|
+
Context.next_field(factory.context, index), raise_on_invalid
|
162
|
+
)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def check_field_type(context, raise_on_invalid)
|
167
|
+
if raise_on_invalid
|
168
|
+
TypeChecker.raise_on_invalid_type(context,
|
169
|
+
type: factory.value_input_type)
|
170
|
+
else
|
171
|
+
TypeChecker.validate_type(validatable,
|
172
|
+
type: factory.value_input_type,
|
173
|
+
context: context)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def validate(raise_on_invalid: false)
|
178
|
+
run_validation
|
179
|
+
|
180
|
+
return if !raise_on_invalid || validatable.errors.empty?
|
181
|
+
|
182
|
+
first_error = validatable.errors.first
|
183
|
+
raise Openapi3Parser::Error::InvalidData,
|
184
|
+
"Invalid data for #{first_error.context.location_summary}. "\
|
185
|
+
"#{first_error.message}"
|
186
|
+
end
|
187
|
+
|
188
|
+
def run_validation
|
189
|
+
if factory.validation.is_a?(Symbol)
|
190
|
+
factory.send(:validation, validatable)
|
191
|
+
else
|
192
|
+
factory.validation&.call(validatable)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|