openapi3_parser 0.6.1 → 0.9.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 +5 -5
- data/.github/workflows/ci.yml +23 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +18 -3
- data/.ruby-version +1 -1
- data/CHANGELOG.md +41 -0
- data/README.md +7 -4
- data/TODO.md +1 -1
- data/lib/openapi3_parser.rb +2 -3
- data/lib/openapi3_parser/array_sentence.rb +2 -1
- data/lib/openapi3_parser/document.rb +3 -2
- data/lib/openapi3_parser/error.rb +11 -1
- data/lib/openapi3_parser/node/array.rb +9 -1
- data/lib/openapi3_parser/node/context.rb +34 -4
- data/lib/openapi3_parser/node/map.rb +17 -1
- data/lib/openapi3_parser/node/object.rb +17 -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 +16 -12
- data/lib/openapi3_parser/node/schema.rb +50 -3
- 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 +12 -10
- 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 +7 -5
- data/lib/openapi3_parser/node_factory/object_factory/field_config.rb +9 -7
- data/lib/openapi3_parser/node_factory/object_factory/node_builder.rb +12 -13
- data/lib/openapi3_parser/node_factory/object_factory/validator.rb +25 -21
- data/lib/openapi3_parser/node_factory/openapi.rb +2 -0
- data/lib/openapi3_parser/node_factory/operation.rb +9 -0
- data/lib/openapi3_parser/node_factory/parameter.rb +2 -0
- data/lib/openapi3_parser/node_factory/parameter_like.rb +2 -1
- 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/reference.rb +9 -1
- data/lib/openapi3_parser/node_factory/request_body.rb +2 -4
- 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 +5 -2
- 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 +9 -4
- data/lib/openapi3_parser/source_input.rb +9 -9
- data/lib/openapi3_parser/source_input/file.rb +3 -1
- data/lib/openapi3_parser/source_input/raw.rb +4 -1
- 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 +3 -1
- data/lib/openapi3_parser/validation/error.rb +2 -0
- data/lib/openapi3_parser/validation/error_collection.rb +1 -1
- data/lib/openapi3_parser/validation/input_validator.rb +1 -1
- 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 +3 -3
- data/lib/openapi3_parser/validators/media_type.rb +1 -1
- data/lib/openapi3_parser/validators/mutually_exclusive_fields.rb +8 -8
- 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/validators/url.rb +2 -7
- data/lib/openapi3_parser/version.rb +1 -1
- data/openapi3_parser.gemspec +12 -8
- metadata +81 -25
- data/.travis.yml +0 -11
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "ostruct"
|
|
4
3
|
require "openapi3_parser/node_factory/object_factory/field_config"
|
|
5
4
|
|
|
6
5
|
module Openapi3Parser
|
|
7
6
|
module NodeFactory
|
|
8
7
|
module ObjectFactory
|
|
9
8
|
module Dsl
|
|
9
|
+
MutuallyExclusiveField = Struct.new(:fields, :required, keyword_init: true)
|
|
10
|
+
|
|
10
11
|
def field(name, **options)
|
|
11
12
|
@field_configs ||= {}
|
|
12
|
-
@field_configs[name] = FieldConfig.new(options)
|
|
13
|
+
@field_configs[name] = FieldConfig.new(**options)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def field_configs
|
|
@@ -30,8 +31,9 @@ module Openapi3Parser
|
|
|
30
31
|
|
|
31
32
|
def mutually_exclusive(*fields, required: false)
|
|
32
33
|
@mutually_exclusive_fields ||= []
|
|
33
|
-
@mutually_exclusive_fields <<
|
|
34
|
-
fields: fields,
|
|
34
|
+
@mutually_exclusive_fields << MutuallyExclusiveField.new(
|
|
35
|
+
fields: fields,
|
|
36
|
+
required: required
|
|
35
37
|
)
|
|
36
38
|
end
|
|
37
39
|
|
|
@@ -41,7 +43,7 @@ module Openapi3Parser
|
|
|
41
43
|
|
|
42
44
|
def validate(*items, &block)
|
|
43
45
|
@validations ||= []
|
|
44
|
-
@validations
|
|
46
|
+
@validations.concat(items)
|
|
45
47
|
@validations << block if block
|
|
46
48
|
end
|
|
47
49
|
|
|
@@ -22,10 +22,11 @@ module Openapi3Parser
|
|
|
22
22
|
!given_factory.nil?
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def initialize_factory(context, parent_factory)
|
|
26
|
-
|
|
25
|
+
def initialize_factory(context, parent_factory = nil)
|
|
26
|
+
case given_factory
|
|
27
|
+
when Class
|
|
27
28
|
given_factory.new(context)
|
|
28
|
-
|
|
29
|
+
when Symbol
|
|
29
30
|
parent_factory.send(given_factory, context)
|
|
30
31
|
else
|
|
31
32
|
given_factory.call(context)
|
|
@@ -36,7 +37,7 @@ module Openapi3Parser
|
|
|
36
37
|
given_required
|
|
37
38
|
end
|
|
38
39
|
|
|
39
|
-
def check_input_type(validatable, building_node)
|
|
40
|
+
def check_input_type(validatable, building_node: false)
|
|
40
41
|
return true if !given_input_type || validatable.input.nil?
|
|
41
42
|
|
|
42
43
|
if building_node
|
|
@@ -47,7 +48,7 @@ module Openapi3Parser
|
|
|
47
48
|
end
|
|
48
49
|
end
|
|
49
50
|
|
|
50
|
-
def validate_field(validatable, building_node)
|
|
51
|
+
def validate_field(validatable, building_node: false)
|
|
51
52
|
return true if !given_validate || validatable.input.nil?
|
|
52
53
|
|
|
53
54
|
run_validation(validatable)
|
|
@@ -61,9 +62,10 @@ module Openapi3Parser
|
|
|
61
62
|
"Invalid data for #{location_summary}: #{error.message}"
|
|
62
63
|
end
|
|
63
64
|
|
|
64
|
-
def default(factory)
|
|
65
|
+
def default(factory = nil)
|
|
65
66
|
return given_default.call if given_default.is_a?(Proc)
|
|
66
|
-
return factory
|
|
67
|
+
return factory&.send(given_default) if given_default.is_a?(Symbol)
|
|
68
|
+
|
|
67
69
|
given_default
|
|
68
70
|
end
|
|
69
71
|
|
|
@@ -8,8 +8,8 @@ module Openapi3Parser
|
|
|
8
8
|
new(factory).errors
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def self.node_data(factory,
|
|
12
|
-
new(factory).node_data(
|
|
11
|
+
def self.node_data(factory, node_context)
|
|
12
|
+
new(factory).node_data(node_context)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def initialize(factory)
|
|
@@ -22,18 +22,17 @@ module Openapi3Parser
|
|
|
22
22
|
|
|
23
23
|
TypeChecker.validate_type(validatable, type: ::Hash)
|
|
24
24
|
|
|
25
|
-
if validatable.errors.empty?
|
|
26
|
-
validatable.add_errors(validate(raise_on_invalid: false))
|
|
27
|
-
end
|
|
25
|
+
validatable.add_errors(validate(raise_on_invalid: false)) if validatable.errors.empty?
|
|
28
26
|
|
|
29
27
|
validatable.collection
|
|
30
28
|
end
|
|
31
29
|
|
|
32
|
-
def node_data(
|
|
33
|
-
return build_node_data(
|
|
30
|
+
def node_data(node_context)
|
|
31
|
+
return build_node_data(node_context) if empty_and_allowed_to_be?
|
|
32
|
+
|
|
34
33
|
TypeChecker.raise_on_invalid_type(factory.context, type: ::Hash)
|
|
35
34
|
validate(raise_on_invalid: true)
|
|
36
|
-
build_node_data(
|
|
35
|
+
build_node_data(node_context)
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
private_class_method :new
|
|
@@ -47,22 +46,22 @@ module Openapi3Parser
|
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
def validate(raise_on_invalid:)
|
|
50
|
-
Validator.call(factory, raise_on_invalid)
|
|
49
|
+
Validator.call(factory, raise_on_invalid: raise_on_invalid)
|
|
51
50
|
end
|
|
52
51
|
|
|
53
|
-
def build_node_data(
|
|
52
|
+
def build_node_data(node_context)
|
|
54
53
|
return if factory.nil_input? && factory.data.nil?
|
|
55
54
|
|
|
56
55
|
factory.data.each_with_object({}) do |(key, value), memo|
|
|
57
|
-
memo[key] = resolve_value(key, value,
|
|
56
|
+
memo[key] = resolve_value(key, value, node_context)
|
|
58
57
|
end
|
|
59
58
|
end
|
|
60
59
|
|
|
61
|
-
def resolve_value(key, value,
|
|
60
|
+
def resolve_value(key, value, node_context)
|
|
62
61
|
resolved = determine_value_or_default(key, value)
|
|
63
62
|
|
|
64
63
|
if resolved.respond_to?(:node)
|
|
65
|
-
Node::Placeholder.new(value, key,
|
|
64
|
+
Node::Placeholder.new(value, key, node_context)
|
|
66
65
|
else
|
|
67
66
|
resolved
|
|
68
67
|
end
|
|
@@ -7,15 +7,15 @@ module Openapi3Parser
|
|
|
7
7
|
module ObjectFactory
|
|
8
8
|
class Validator
|
|
9
9
|
private_class_method :new
|
|
10
|
-
attr_reader :factory, :validatable, :
|
|
10
|
+
attr_reader :factory, :validatable, :raise_on_invalid
|
|
11
11
|
|
|
12
|
-
def self.call(*args)
|
|
13
|
-
new(*args).call
|
|
12
|
+
def self.call(*args, **kwargs)
|
|
13
|
+
new(*args, **kwargs).call
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def initialize(factory,
|
|
16
|
+
def initialize(factory, raise_on_invalid: false)
|
|
17
17
|
@factory = factory
|
|
18
|
-
@
|
|
18
|
+
@raise_on_invalid = raise_on_invalid
|
|
19
19
|
@validatable = Validation::Validatable.new(factory)
|
|
20
20
|
end
|
|
21
21
|
|
|
@@ -34,7 +34,7 @@ module Openapi3Parser
|
|
|
34
34
|
Validators::RequiredFields.call(
|
|
35
35
|
validatable,
|
|
36
36
|
required_fields: factory.required_fields,
|
|
37
|
-
raise_on_invalid:
|
|
37
|
+
raise_on_invalid: raise_on_invalid
|
|
38
38
|
)
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -43,7 +43,7 @@ module Openapi3Parser
|
|
|
43
43
|
validatable,
|
|
44
44
|
allow_extensions: factory.allowed_extensions?,
|
|
45
45
|
allowed_fields: factory.allowed_fields,
|
|
46
|
-
raise_on_invalid:
|
|
46
|
+
raise_on_invalid: raise_on_invalid
|
|
47
47
|
)
|
|
48
48
|
end
|
|
49
49
|
|
|
@@ -51,7 +51,7 @@ module Openapi3Parser
|
|
|
51
51
|
Validators::MutuallyExclusiveFields.call(
|
|
52
52
|
validatable,
|
|
53
53
|
mutually_exclusive_fields: factory.mutually_exclusive_fields,
|
|
54
|
-
raise_on_invalid:
|
|
54
|
+
raise_on_invalid: raise_on_invalid
|
|
55
55
|
)
|
|
56
56
|
end
|
|
57
57
|
|
|
@@ -66,7 +66,8 @@ module Openapi3Parser
|
|
|
66
66
|
class CheckInvalidFields
|
|
67
67
|
extend Forwardable
|
|
68
68
|
attr_reader :validator
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
def_delegators :validator, :factory, :raise_on_invalid, :validatable
|
|
70
71
|
private_class_method :new
|
|
71
72
|
|
|
72
73
|
def self.call(validator)
|
|
@@ -81,22 +82,21 @@ module Openapi3Parser
|
|
|
81
82
|
factory.data.each do |name, field|
|
|
82
83
|
# references can reference themselves and become in a loop
|
|
83
84
|
next if in_recursive_loop?(field)
|
|
85
|
+
|
|
84
86
|
has_factory_errors = handle_factory_checks(name)
|
|
85
87
|
|
|
86
88
|
next if has_factory_errors || !field.respond_to?(:errors)
|
|
87
89
|
|
|
88
90
|
# We don't add errors when we're building a node as they will
|
|
89
91
|
# be raised when that child node is built
|
|
90
|
-
validatable.add_errors(field.errors) unless
|
|
92
|
+
validatable.add_errors(field.errors) unless raise_on_invalid
|
|
91
93
|
end
|
|
92
94
|
end
|
|
93
95
|
|
|
94
96
|
private
|
|
95
97
|
|
|
96
98
|
def handle_factory_checks(name)
|
|
97
|
-
field_errors = if factory.field_configs[name]
|
|
98
|
-
check_field(name, factory.field_configs[name])
|
|
99
|
-
end
|
|
99
|
+
field_errors = (check_field(name, factory.field_configs[name]) if factory.field_configs[name])
|
|
100
100
|
|
|
101
101
|
(field_errors || []).any?
|
|
102
102
|
end
|
|
@@ -108,16 +108,20 @@ module Openapi3Parser
|
|
|
108
108
|
def check_field(name, field_config)
|
|
109
109
|
return if factory.raw_input[name].nil?
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
context = Context.next_field(factory.context,
|
|
112
|
+
name,
|
|
113
|
+
factory.raw_input[name])
|
|
114
|
+
field_validatable = Validation::Validatable.new(factory,
|
|
115
|
+
context: context)
|
|
115
116
|
|
|
116
|
-
valid_input_type = field_config.check_input_type(
|
|
117
|
-
|
|
117
|
+
valid_input_type = field_config.check_input_type(
|
|
118
|
+
field_validatable,
|
|
119
|
+
building_node: raise_on_invalid
|
|
120
|
+
)
|
|
118
121
|
|
|
119
122
|
if valid_input_type
|
|
120
|
-
field_config.validate_field(field_validatable,
|
|
123
|
+
field_config.validate_field(field_validatable,
|
|
124
|
+
building_node: raise_on_invalid)
|
|
121
125
|
end
|
|
122
126
|
|
|
123
127
|
validatable.add_errors(field_validatable.errors)
|
|
@@ -137,7 +141,7 @@ module Openapi3Parser
|
|
|
137
141
|
|
|
138
142
|
errors = validator.validatable.errors
|
|
139
143
|
|
|
140
|
-
return if errors.empty? || !validator.
|
|
144
|
+
return if errors.empty? || !validator.raise_on_invalid
|
|
141
145
|
|
|
142
146
|
location_summary = errors.first.context.location_summary
|
|
143
147
|
raise Error::InvalidData,
|
|
@@ -25,6 +25,8 @@ module Openapi3Parser
|
|
|
25
25
|
private
|
|
26
26
|
|
|
27
27
|
def build_object(data, context)
|
|
28
|
+
data["servers"] = path_item_server_data(context) if data["servers"].node.empty?
|
|
29
|
+
|
|
28
30
|
Node::Operation.new(data, context)
|
|
29
31
|
end
|
|
30
32
|
|
|
@@ -72,6 +74,13 @@ module Openapi3Parser
|
|
|
72
74
|
NodeFactory::Array.new(context,
|
|
73
75
|
value_factory: NodeFactory::Server)
|
|
74
76
|
end
|
|
77
|
+
|
|
78
|
+
def path_item_server_data(node_context)
|
|
79
|
+
path_item_servers = node_context.parent_node.node_data["servers"]
|
|
80
|
+
Node::Placeholder.new(path_item_servers.node_factory,
|
|
81
|
+
"servers",
|
|
82
|
+
node_context)
|
|
83
|
+
end
|
|
75
84
|
end
|
|
76
85
|
end
|
|
77
86
|
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
|
|
@@ -13,7 +13,7 @@ module Openapi3Parser
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def examples_factory(context)
|
|
16
|
-
factory = NodeFactory::OptionalReference.new(NodeFactory::
|
|
16
|
+
factory = NodeFactory::OptionalReference.new(NodeFactory::Example)
|
|
17
17
|
NodeFactory::Map.new(context,
|
|
18
18
|
default: nil,
|
|
19
19
|
value_factory: factory)
|
|
@@ -28,6 +28,7 @@ module Openapi3Parser
|
|
|
28
28
|
|
|
29
29
|
def validate_content(validatable)
|
|
30
30
|
return if validatable.input.size == 1
|
|
31
|
+
|
|
31
32
|
validatable.add_error("Must only have one item")
|
|
32
33
|
end
|
|
33
34
|
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
|
|
@@ -32,11 +32,19 @@ module Openapi3Parser
|
|
|
32
32
|
referenced_factory.resolves?(control_factory)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
def errors
|
|
36
|
+
if in_recursive_loop?
|
|
37
|
+
@errors ||= Validation::ErrorCollection.new
|
|
38
|
+
else
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
35
43
|
private
|
|
36
44
|
|
|
37
45
|
def build_node(node_context)
|
|
38
46
|
TypeChecker.raise_on_invalid_type(context, type: ::Hash)
|
|
39
|
-
ObjectFactory::Validator.call(self, true)
|
|
47
|
+
ObjectFactory::Validator.call(self, raise_on_invalid: true)
|
|
40
48
|
data["$ref"].node(node_context)
|
|
41
49
|
end
|
|
42
50
|
|
|
@@ -32,11 +32,9 @@ module Openapi3Parser
|
|
|
32
32
|
def call(validatable)
|
|
33
33
|
# This validation isn't actually mentioned in the spec, but it
|
|
34
34
|
# doesn't seem to make sense if this is an empty hash.
|
|
35
|
-
if validatable.input.size.zero?
|
|
36
|
-
return validatable.add_error("Expected to have at least 1 item")
|
|
37
|
-
end
|
|
35
|
+
return validatable.add_error("Expected to have at least 1 item") if validatable.input.size.zero?
|
|
38
36
|
|
|
39
|
-
validatable.input.
|
|
37
|
+
validatable.input.each_key do |key|
|
|
40
38
|
message = Validators::MediaType.call(key)
|
|
41
39
|
next unless message
|
|
42
40
|
|