openapi3_parser 0.7.0 → 0.8.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/.gitignore +1 -0
- data/.rubocop.yml +2 -4
- data/.travis.yml +11 -0
- data/CHANGELOG.md +13 -0
- data/README.md +4 -1
- data/TODO.md +1 -1
- data/lib/openapi3_parser.rb +2 -3
- data/lib/openapi3_parser/array_sentence.rb +1 -0
- data/lib/openapi3_parser/document.rb +2 -1
- data/lib/openapi3_parser/node/array.rb +1 -1
- data/lib/openapi3_parser/node/context.rb +23 -0
- data/lib/openapi3_parser/node/map.rb +1 -1
- data/lib/openapi3_parser/node/object.rb +1 -0
- data/lib/openapi3_parser/node/operation.rb +8 -0
- data/lib/openapi3_parser/node/path_item.rb +8 -0
- data/lib/openapi3_parser/node/placeholder.rb +8 -5
- data/lib/openapi3_parser/node/schema.rb +3 -2
- data/lib/openapi3_parser/node_factory.rb +1 -1
- data/lib/openapi3_parser/node_factory/array.rb +34 -26
- data/lib/openapi3_parser/node_factory/context.rb +9 -2
- data/lib/openapi3_parser/node_factory/discriminator.rb +2 -0
- data/lib/openapi3_parser/node_factory/field.rb +2 -0
- data/lib/openapi3_parser/node_factory/fields/reference.rb +1 -0
- data/lib/openapi3_parser/node_factory/map.rb +6 -4
- data/lib/openapi3_parser/node_factory/media_type.rb +1 -0
- data/lib/openapi3_parser/node_factory/object.rb +4 -1
- data/lib/openapi3_parser/node_factory/object_factory/dsl.rb +1 -1
- data/lib/openapi3_parser/node_factory/object_factory/field_config.rb +1 -0
- data/lib/openapi3_parser/node_factory/object_factory/node_builder.rb +1 -0
- data/lib/openapi3_parser/node_factory/object_factory/validator.rb +6 -4
- data/lib/openapi3_parser/node_factory/openapi.rb +2 -0
- data/lib/openapi3_parser/node_factory/operation.rb +11 -0
- data/lib/openapi3_parser/node_factory/parameter.rb +2 -0
- data/lib/openapi3_parser/node_factory/parameter_like.rb +1 -0
- data/lib/openapi3_parser/node_factory/path_item.rb +27 -5
- data/lib/openapi3_parser/node_factory/paths.rb +1 -1
- data/lib/openapi3_parser/node_factory/response.rb +1 -0
- data/lib/openapi3_parser/node_factory/responses.rb +1 -1
- data/lib/openapi3_parser/node_factory/schema.rb +3 -0
- data/lib/openapi3_parser/node_factory/server_variable.rb +1 -0
- data/lib/openapi3_parser/node_factory/type_checker.rb +6 -0
- data/lib/openapi3_parser/source.rb +2 -0
- data/lib/openapi3_parser/source/location.rb +6 -0
- data/lib/openapi3_parser/source/pointer.rb +5 -0
- data/lib/openapi3_parser/source_input.rb +2 -0
- data/lib/openapi3_parser/source_input/file.rb +2 -0
- data/lib/openapi3_parser/source_input/raw.rb +3 -0
- data/lib/openapi3_parser/source_input/resolve_next.rb +1 -0
- data/lib/openapi3_parser/source_input/string_parser.rb +3 -2
- data/lib/openapi3_parser/source_input/url.rb +2 -0
- data/lib/openapi3_parser/validation/error.rb +2 -0
- data/lib/openapi3_parser/validators/component_keys.rb +1 -1
- data/lib/openapi3_parser/validators/duplicate_parameters.rb +1 -0
- data/lib/openapi3_parser/validators/email.rb +1 -1
- data/lib/openapi3_parser/validators/media_type.rb +1 -1
- data/lib/openapi3_parser/validators/mutually_exclusive_fields.rb +2 -2
- data/lib/openapi3_parser/validators/reference.rb +2 -0
- data/lib/openapi3_parser/validators/required_fields.rb +2 -2
- data/lib/openapi3_parser/validators/unexpected_fields.rb +3 -2
- data/lib/openapi3_parser/version.rb +1 -1
- data/openapi3_parser.gemspec +9 -7
- metadata +44 -16
@@ -19,9 +19,11 @@ module Openapi3Parser
|
|
19
19
|
def validate_mapping(validatable)
|
20
20
|
input = validatable.input
|
21
21
|
return if input.empty?
|
22
|
+
|
22
23
|
string_keys = input.keys.map(&:class).uniq == [String]
|
23
24
|
string_values = input.values.map(&:class).uniq == [String]
|
24
25
|
return if string_keys && string_values
|
26
|
+
|
25
27
|
validatable.add_error("Expected string keys and string values")
|
26
28
|
end
|
27
29
|
end
|
@@ -70,8 +70,10 @@ module Openapi3Parser
|
|
70
70
|
|
71
71
|
def errors
|
72
72
|
return validatable.collection if factory.nil_input?
|
73
|
+
|
73
74
|
TypeChecker.validate_type(validatable, type: factory.input_type)
|
74
75
|
return validatable.collection if validatable.errors.any?
|
76
|
+
|
75
77
|
validate(raise_on_invalid: false)
|
76
78
|
validatable.collection
|
77
79
|
end
|
@@ -60,6 +60,7 @@ module Openapi3Parser
|
|
60
60
|
def build_data(raw_input)
|
61
61
|
use_default = nil_input? || !raw_input.is_a?(::Hash)
|
62
62
|
return if use_default && default.nil?
|
63
|
+
|
63
64
|
process_data(use_default ? default : raw_input)
|
64
65
|
end
|
65
66
|
|
@@ -114,8 +115,10 @@ module Openapi3Parser
|
|
114
115
|
|
115
116
|
def errors
|
116
117
|
return validatable.collection if factory.nil_input?
|
118
|
+
|
117
119
|
TypeChecker.validate_type(validatable, type: ::Hash)
|
118
120
|
return validatable.collection if validatable.errors.any?
|
121
|
+
|
119
122
|
collate_errors
|
120
123
|
validatable.collection
|
121
124
|
end
|
@@ -175,12 +178,11 @@ module Openapi3Parser
|
|
175
178
|
def check_values(raise_on_invalid: false)
|
176
179
|
return unless factory.value_input_type
|
177
180
|
|
178
|
-
factory.context.input.
|
181
|
+
factory.context.input.each do |key, value|
|
179
182
|
next if factory.allow_extensions && key.to_s =~ EXTENSION_REGEX
|
180
183
|
|
181
|
-
check_field_type(
|
182
|
-
|
183
|
-
)
|
184
|
+
check_field_type(Context.next_field(factory.context, key, value),
|
185
|
+
raise_on_invalid)
|
184
186
|
end
|
185
187
|
end
|
186
188
|
|
@@ -78,6 +78,7 @@ module Openapi3Parser
|
|
78
78
|
def build_data(raw_input)
|
79
79
|
use_default = nil_input? || !raw_input.is_a?(::Hash)
|
80
80
|
return if use_default && default.nil?
|
81
|
+
|
81
82
|
process_data(use_default ? default : raw_input)
|
82
83
|
end
|
83
84
|
|
@@ -85,7 +86,8 @@ module Openapi3Parser
|
|
85
86
|
field_configs.each_with_object(raw_data.dup) do |(field, config), memo|
|
86
87
|
memo[field] = nil unless memo[field]
|
87
88
|
next unless config.factory?
|
88
|
-
|
89
|
+
|
90
|
+
next_context = Context.next_field(context, field, memo[field])
|
89
91
|
memo[field] = config.initialize_factory(next_context, self)
|
90
92
|
end
|
91
93
|
end
|
@@ -95,6 +97,7 @@ module Openapi3Parser
|
|
95
97
|
|
96
98
|
data.each_with_object({}) do |(key, value), memo|
|
97
99
|
next if value.respond_to?(:nil_input?) && value.nil_input?
|
100
|
+
|
98
101
|
memo[key] = if value.respond_to?(:resolved_input)
|
99
102
|
value.resolved_input
|
100
103
|
else
|
@@ -31,6 +31,7 @@ module Openapi3Parser
|
|
31
31
|
|
32
32
|
def node_data(parent_context)
|
33
33
|
return build_node_data(parent_context) if empty_and_allowed_to_be?
|
34
|
+
|
34
35
|
TypeChecker.raise_on_invalid_type(factory.context, type: ::Hash)
|
35
36
|
validate(raise_on_invalid: true)
|
36
37
|
build_node_data(parent_context)
|
@@ -81,6 +81,7 @@ module Openapi3Parser
|
|
81
81
|
factory.data.each do |name, field|
|
82
82
|
# references can reference themselves and become in a loop
|
83
83
|
next if in_recursive_loop?(field)
|
84
|
+
|
84
85
|
has_factory_errors = handle_factory_checks(name)
|
85
86
|
|
86
87
|
next if has_factory_errors || !field.respond_to?(:errors)
|
@@ -108,10 +109,11 @@ module Openapi3Parser
|
|
108
109
|
def check_field(name, field_config)
|
109
110
|
return if factory.raw_input[name].nil?
|
110
111
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
112
|
+
context = Context.next_field(factory.context,
|
113
|
+
name,
|
114
|
+
factory.raw_input[name])
|
115
|
+
field_validatable = Validation::Validatable.new(factory,
|
116
|
+
context: context)
|
115
117
|
|
116
118
|
valid_input_type = field_config.check_input_type(field_validatable,
|
117
119
|
building_node)
|
@@ -25,6 +25,10 @@ module Openapi3Parser
|
|
25
25
|
private
|
26
26
|
|
27
27
|
def build_object(data, context)
|
28
|
+
if data["servers"].node.empty?
|
29
|
+
data["servers"] = path_item_server_data(context)
|
30
|
+
end
|
31
|
+
|
28
32
|
Node::Operation.new(data, context)
|
29
33
|
end
|
30
34
|
|
@@ -72,6 +76,13 @@ module Openapi3Parser
|
|
72
76
|
NodeFactory::Array.new(context,
|
73
77
|
value_factory: NodeFactory::Server)
|
74
78
|
end
|
79
|
+
|
80
|
+
def path_item_server_data(node_context)
|
81
|
+
path_item_servers = node_context.parent_node.node_data["servers"]
|
82
|
+
Node::Placeholder.new(path_item_servers.node_factory,
|
83
|
+
"servers",
|
84
|
+
node_context)
|
85
|
+
end
|
75
86
|
end
|
76
87
|
end
|
77
88
|
end
|
@@ -47,11 +47,13 @@ module Openapi3Parser
|
|
47
47
|
|
48
48
|
def default_style
|
49
49
|
return "simple" if %w[path header].include?(context.input["in"])
|
50
|
+
|
50
51
|
"form"
|
51
52
|
end
|
52
53
|
|
53
54
|
def validate_in(validatable)
|
54
55
|
return if %w[header query cookie path].include?(validatable.input)
|
56
|
+
|
55
57
|
validatable.add_error("in can only be header, query, cookie, or path")
|
56
58
|
end
|
57
59
|
end
|
@@ -23,11 +23,22 @@ module Openapi3Parser
|
|
23
23
|
private
|
24
24
|
|
25
25
|
def build_object(data, node_context)
|
26
|
-
ref = data.delete("$ref")
|
27
|
-
|
26
|
+
ref = data.delete("$ref")
|
27
|
+
context = if node_context.input.keys == %w[$ref]
|
28
|
+
referenced_factory = ref.node_factory.referenced_factory
|
29
|
+
Node::Context.resolved_reference(
|
30
|
+
node_context,
|
31
|
+
referenced_factory.context
|
32
|
+
)
|
33
|
+
else
|
34
|
+
node_context
|
35
|
+
end
|
28
36
|
|
29
|
-
|
30
|
-
|
37
|
+
reference_data = ref.nil_input? ? {} : ref.node.node_data
|
38
|
+
|
39
|
+
data = merge_data(reference_data, data).tap do |d|
|
40
|
+
d["servers"] = root_server_data(context) if d["servers"].node.empty?
|
41
|
+
end
|
31
42
|
|
32
43
|
Node::PathItem.new(data, context)
|
33
44
|
end
|
@@ -57,7 +68,11 @@ module Openapi3Parser
|
|
57
68
|
|
58
69
|
def merge_data(base, priority)
|
59
70
|
base.merge(priority) do |_, old, new|
|
60
|
-
new.nil? ?
|
71
|
+
if new.nil? || new.respond_to?(:nil_input?) && new.nil_input?
|
72
|
+
old
|
73
|
+
else
|
74
|
+
new
|
75
|
+
end
|
61
76
|
end
|
62
77
|
end
|
63
78
|
|
@@ -76,6 +91,13 @@ module Openapi3Parser
|
|
76
91
|
value_factory: factory,
|
77
92
|
validate: validate_parameters)
|
78
93
|
end
|
94
|
+
|
95
|
+
def root_server_data(node_context)
|
96
|
+
root_servers = node_context.document.root.node_data["servers"]
|
97
|
+
Node::Placeholder.new(root_servers.node_factory,
|
98
|
+
"servers",
|
99
|
+
node_context)
|
100
|
+
end
|
79
101
|
end
|
80
102
|
end
|
81
103
|
end
|
@@ -61,6 +61,7 @@ module Openapi3Parser
|
|
61
61
|
def read_only_or_write_only(validatable)
|
62
62
|
input = validatable.input
|
63
63
|
return if [input["readOnly"], input["writeOnly"]].uniq != [true]
|
64
|
+
|
64
65
|
validatable.add_error("readOnly and writeOnly cannot both be true")
|
65
66
|
end
|
66
67
|
|
@@ -114,11 +115,13 @@ module Openapi3Parser
|
|
114
115
|
def additional_properties_input_type(validatable)
|
115
116
|
input = validatable.input
|
116
117
|
return if [true, false].include?(input) || input.is_a?(Hash)
|
118
|
+
|
117
119
|
validatable.add_error("Expected a Boolean or an Object")
|
118
120
|
end
|
119
121
|
|
120
122
|
def additional_properties_factory(context)
|
121
123
|
return context.input if [true, false].include?(context.input)
|
124
|
+
|
122
125
|
referenceable_schema(context)
|
123
126
|
end
|
124
127
|
end
|
@@ -27,9 +27,11 @@ module Openapi3Parser
|
|
27
27
|
|
28
28
|
def validate_type(validatable, context)
|
29
29
|
return true unless type
|
30
|
+
|
30
31
|
context ||= validatable.context
|
31
32
|
valid_type?(context.input).tap do |valid|
|
32
33
|
next if valid
|
34
|
+
|
33
35
|
validatable.add_error("Invalid type. #{field_error_message}",
|
34
36
|
context)
|
35
37
|
end
|
@@ -37,9 +39,11 @@ module Openapi3Parser
|
|
37
39
|
|
38
40
|
def validate_keys(validatable, context)
|
39
41
|
return true unless type
|
42
|
+
|
40
43
|
context ||= validatable.context
|
41
44
|
valid_keys?(context.input).tap do |valid|
|
42
45
|
next if valid
|
46
|
+
|
43
47
|
validatable.add_error("Invalid keys. #{keys_error_message}",
|
44
48
|
context)
|
45
49
|
end
|
@@ -47,6 +51,7 @@ module Openapi3Parser
|
|
47
51
|
|
48
52
|
def raise_on_invalid_type(context)
|
49
53
|
return true if !type || valid_type?(context.input)
|
54
|
+
|
50
55
|
raise Error::InvalidType,
|
51
56
|
"Invalid type for #{context.location_summary}: "\
|
52
57
|
"#{field_error_message}"
|
@@ -54,6 +59,7 @@ module Openapi3Parser
|
|
54
59
|
|
55
60
|
def raise_on_invalid_keys(context)
|
56
61
|
return true if !type || valid_keys?(context.input)
|
62
|
+
|
57
63
|
raise Error::InvalidType,
|
58
64
|
"Invalid keys for #{context.location_summary}: "\
|
59
65
|
"#{keys_error_message}"
|
@@ -93,6 +93,7 @@ module Openapi3Parser
|
|
93
93
|
# @return [Object]
|
94
94
|
def data_at_pointer(json_pointer)
|
95
95
|
return data if json_pointer.empty?
|
96
|
+
|
96
97
|
data.dig(*json_pointer) if data.respond_to?(:dig)
|
97
98
|
end
|
98
99
|
|
@@ -104,6 +105,7 @@ module Openapi3Parser
|
|
104
105
|
# @return [String]
|
105
106
|
def relative_to_root
|
106
107
|
return "" if root?
|
108
|
+
|
107
109
|
source_input.relative_to(document.root_source.source_input)
|
108
110
|
end
|
109
111
|
|
@@ -1,15 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "forwardable"
|
4
|
+
|
3
5
|
module Openapi3Parser
|
4
6
|
class Source
|
5
7
|
# Class used to represent a location within an OpenAPI document.
|
6
8
|
# It contains a source, which is the source file/data used for the contents
|
7
9
|
# and the pointer which indicates where in the object like file the data is
|
8
10
|
class Location
|
11
|
+
extend Forwardable
|
12
|
+
|
9
13
|
def self.next_field(location, field)
|
10
14
|
new(location.source, location.pointer.segments + [field])
|
11
15
|
end
|
12
16
|
|
17
|
+
def_delegators :pointer, :root?
|
13
18
|
attr_reader :source, :pointer
|
14
19
|
|
15
20
|
# @param [Openapi3Parser::Source] source
|
@@ -21,6 +26,7 @@ module Openapi3Parser
|
|
21
26
|
|
22
27
|
def ==(other)
|
23
28
|
return false unless other.instance_of?(self.class)
|
29
|
+
|
24
30
|
source == other.source && pointer == other.pointer
|
25
31
|
end
|
26
32
|
|
@@ -12,6 +12,7 @@ module Openapi3Parser
|
|
12
12
|
absolute = fragment[0] == "/"
|
13
13
|
segments = fragment.split("/").map do |part|
|
14
14
|
next if part == ""
|
15
|
+
|
15
16
|
unescaped = CGI.unescape(part.gsub("%20", "+"))
|
16
17
|
unescaped.match?(/\A\d+\z/) ? unescaped.to_i : unescaped
|
17
18
|
end
|
@@ -49,6 +50,10 @@ module Openapi3Parser
|
|
49
50
|
%{#{self.class.name}(segments: #{segments}, fragment: "#{fragment}")}
|
50
51
|
end
|
51
52
|
|
53
|
+
def root?
|
54
|
+
segments.empty?
|
55
|
+
end
|
56
|
+
|
52
57
|
class MergePointers
|
53
58
|
private_class_method :new
|
54
59
|
|
@@ -45,6 +45,7 @@ module Openapi3Parser
|
|
45
45
|
def contents
|
46
46
|
raise access_error if access_error
|
47
47
|
raise parse_error if parse_error
|
48
|
+
|
48
49
|
@contents
|
49
50
|
end
|
50
51
|
|
@@ -61,6 +62,7 @@ module Openapi3Parser
|
|
61
62
|
|
62
63
|
def initialize_contents
|
63
64
|
return if access_error
|
65
|
+
|
64
66
|
@contents = parse_contents
|
65
67
|
rescue ::StandardError => e
|
66
68
|
@parse_error = Error::UnparsableInput.new(e.message)
|