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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/CHANGELOG.md +8 -0
  4. data/README.md +1 -1
  5. data/TODO.md +4 -4
  6. data/lib/openapi3_parser/array_sentence.rb +12 -0
  7. data/lib/openapi3_parser/cautious_dig.rb +39 -0
  8. data/lib/openapi3_parser/context.rb +53 -1
  9. data/lib/openapi3_parser/context/location.rb +1 -0
  10. data/lib/openapi3_parser/context/pointer.rb +67 -5
  11. data/lib/openapi3_parser/document.rb +45 -4
  12. data/lib/openapi3_parser/error.rb +9 -0
  13. data/lib/openapi3_parser/node/array.rb +14 -4
  14. data/lib/openapi3_parser/node/map.rb +45 -3
  15. data/lib/openapi3_parser/node/object.rb +25 -5
  16. data/lib/openapi3_parser/node_factory.rb +0 -150
  17. data/lib/openapi3_parser/node_factory/array.rb +198 -0
  18. data/lib/openapi3_parser/node_factory/callback.rb +24 -0
  19. data/lib/openapi3_parser/{node_factories → node_factory}/components.rb +24 -25
  20. data/lib/openapi3_parser/{node_factories → node_factory}/contact.rb +5 -6
  21. data/lib/openapi3_parser/{node_factories → node_factory}/discriminator.rb +6 -7
  22. data/lib/openapi3_parser/{node_factories → node_factory}/encoding.rb +6 -8
  23. data/lib/openapi3_parser/{node_factories → node_factory}/example.rb +4 -5
  24. data/lib/openapi3_parser/{node_factories → node_factory}/external_documentation.rb +4 -5
  25. data/lib/openapi3_parser/node_factory/field.rb +129 -0
  26. data/lib/openapi3_parser/node_factory/fields/reference.rb +54 -18
  27. data/lib/openapi3_parser/{node_factories → node_factory}/header.rb +4 -5
  28. data/lib/openapi3_parser/{node_factories → node_factory}/info.rb +6 -7
  29. data/lib/openapi3_parser/{node_factories → node_factory}/license.rb +4 -5
  30. data/lib/openapi3_parser/{node_factories → node_factory}/link.rb +6 -8
  31. data/lib/openapi3_parser/node_factory/map.rb +206 -21
  32. data/lib/openapi3_parser/{node_factories → node_factory}/media_type.rb +17 -16
  33. data/lib/openapi3_parser/{node_factories → node_factory}/oauth_flow.rb +2 -4
  34. data/lib/openapi3_parser/{node_factories → node_factory}/oauth_flows.rb +4 -6
  35. data/lib/openapi3_parser/node_factory/object.rb +66 -63
  36. data/lib/openapi3_parser/node_factory/object_factory/dsl.rb +50 -0
  37. data/lib/openapi3_parser/node_factory/object_factory/field_config.rb +88 -0
  38. data/lib/openapi3_parser/node_factory/object_factory/node_builder.rb +96 -0
  39. data/lib/openapi3_parser/node_factory/object_factory/validator.rb +172 -0
  40. data/lib/openapi3_parser/node_factory/openapi.rb +65 -0
  41. data/lib/openapi3_parser/node_factory/operation.rb +87 -0
  42. data/lib/openapi3_parser/node_factory/optional_reference.rb +7 -3
  43. data/lib/openapi3_parser/{node_factories → node_factory}/parameter.rb +16 -22
  44. data/lib/openapi3_parser/node_factory/parameter_like.rb +42 -0
  45. data/lib/openapi3_parser/{node_factories → node_factory}/path_item.rb +27 -29
  46. data/lib/openapi3_parser/{node_factories → node_factory}/paths.rb +21 -27
  47. data/lib/openapi3_parser/node_factory/recursive_pointer.rb +17 -0
  48. data/lib/openapi3_parser/node_factory/reference.rb +48 -0
  49. data/lib/openapi3_parser/{node_factories → node_factory}/request_body.rb +15 -19
  50. data/lib/openapi3_parser/{node_factories → node_factory}/response.rb +18 -21
  51. data/lib/openapi3_parser/node_factory/responses.rb +51 -0
  52. data/lib/openapi3_parser/{node_factories → node_factory}/schema.rb +33 -33
  53. data/lib/openapi3_parser/node_factory/security_requirement.rb +21 -0
  54. data/lib/openapi3_parser/{node_factories → node_factory}/security_scheme.rb +4 -6
  55. data/lib/openapi3_parser/{node_factories → node_factory}/server.rb +6 -8
  56. data/lib/openapi3_parser/{node_factories → node_factory}/server_variable.rb +10 -12
  57. data/lib/openapi3_parser/{node_factories → node_factory}/tag.rb +4 -6
  58. data/lib/openapi3_parser/node_factory/type_checker.rb +103 -0
  59. data/lib/openapi3_parser/{node_factories → node_factory}/xml.rb +4 -5
  60. data/lib/openapi3_parser/source/reference_resolver.rb +3 -3
  61. data/lib/openapi3_parser/validation/error.rb +9 -0
  62. data/lib/openapi3_parser/validation/input_validator.rb +18 -0
  63. data/lib/openapi3_parser/validation/validatable.rb +44 -0
  64. data/lib/openapi3_parser/validators/mutually_exclusive_fields.rb +121 -0
  65. data/lib/openapi3_parser/validators/required_fields.rb +37 -0
  66. data/lib/openapi3_parser/validators/unexpected_fields.rb +52 -0
  67. data/lib/openapi3_parser/version.rb +1 -1
  68. metadata +48 -38
  69. data/lib/openapi3_parser/node_factories/array.rb +0 -114
  70. data/lib/openapi3_parser/node_factories/callback.rb +0 -27
  71. data/lib/openapi3_parser/node_factories/map.rb +0 -120
  72. data/lib/openapi3_parser/node_factories/openapi.rb +0 -62
  73. data/lib/openapi3_parser/node_factories/operation.rb +0 -84
  74. data/lib/openapi3_parser/node_factories/parameter/parameter_like.rb +0 -41
  75. data/lib/openapi3_parser/node_factories/reference.rb +0 -35
  76. data/lib/openapi3_parser/node_factories/responses.rb +0 -60
  77. data/lib/openapi3_parser/node_factories/security_requirement.rb +0 -26
  78. data/lib/openapi3_parser/node_factory/field_config.rb +0 -88
  79. data/lib/openapi3_parser/node_factory/object/node_builder.rb +0 -97
  80. data/lib/openapi3_parser/node_factory/object/validator.rb +0 -176
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openapi3_parser/node/header"
4
- require "openapi3_parser/node_factories/parameter/parameter_like"
4
+ require "openapi3_parser/node_factory/parameter_like"
5
5
  require "openapi3_parser/node_factory/object"
6
6
 
7
7
  module Openapi3Parser
8
- module NodeFactories
9
- class Header
10
- include NodeFactory::Object
11
- include Parameter::ParameterLike
8
+ module NodeFactory
9
+ class Header < NodeFactory::Object
10
+ include ParameterLike
12
11
 
13
12
  allow_extensions
14
13
 
@@ -1,22 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openapi3_parser/node/info"
4
- require "openapi3_parser/node_factories/license"
5
- require "openapi3_parser/node_factories/contact"
4
+ require "openapi3_parser/node_factory/license"
5
+ require "openapi3_parser/node_factory/contact"
6
6
  require "openapi3_parser/node_factory/object"
7
+ require "openapi3_parser/validation/input_validator"
7
8
  require "openapi3_parser/validators/url"
8
9
 
9
10
  module Openapi3Parser
10
- module NodeFactories
11
- class Info
12
- include NodeFactory::Object
13
-
11
+ module NodeFactory
12
+ class Info < NodeFactory::Object
14
13
  allow_extensions
15
14
  field "title", input_type: String, required: true
16
15
  field "description", input_type: String
17
16
  field "termsOfService",
18
17
  input_type: String,
19
- validate: ->(input) { Validators::Url.call(input) }
18
+ validate: Validation::InputValidator.new(Validators::Url)
20
19
  field "contact", factory: Contact
21
20
  field "license", factory: License
22
21
  field "version", input_type: String, required: true
@@ -3,17 +3,16 @@
3
3
  require "openapi3_parser/node/license"
4
4
  require "openapi3_parser/node_factory/object"
5
5
  require "openapi3_parser/validators/url"
6
+ require "openapi3_parser/validation/input_validator"
6
7
 
7
8
  module Openapi3Parser
8
- module NodeFactories
9
- class License
10
- include NodeFactory::Object
11
-
9
+ module NodeFactory
10
+ class License < NodeFactory::Object
12
11
  allow_extensions
13
12
  field "name", input_type: String, required: true
14
13
  field "url",
15
14
  input_type: String,
16
- validate: ->(input) { Validators::Url.call(input) }
15
+ validate: Validation::InputValidator.new(Validators::Url)
17
16
 
18
17
  private
19
18
 
@@ -1,15 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openapi3_parser/node/license"
4
+ require "openapi3_parser/node_factory/map"
4
5
  require "openapi3_parser/node_factory/object"
5
- require "openapi3_parser/node_factories/map"
6
- require "openapi3_parser/node_factories/server"
6
+ require "openapi3_parser/node_factory/server"
7
7
 
8
8
  module Openapi3Parser
9
- module NodeFactories
10
- class Link
11
- include NodeFactory::Object
12
-
9
+ module NodeFactory
10
+ class Link < NodeFactory::Object
13
11
  allow_extensions
14
12
 
15
13
  # @todo The link object in OAS is pretty meaty and there's lot of scope
@@ -31,11 +29,11 @@ module Openapi3Parser
31
29
  end
32
30
 
33
31
  def parameters_factory(context)
34
- NodeFactories::Map.new(context)
32
+ NodeFactory::Map.new(context)
35
33
  end
36
34
 
37
35
  def server_factory(context)
38
- NodeFactories::Server.new(context)
36
+ NodeFactory::Server.new(context)
39
37
  end
40
38
  end
41
39
  end
@@ -1,42 +1,107 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "openapi3_parser/array_sentence"
4
+ require "openapi3_parser/context"
3
5
  require "openapi3_parser/node_factory"
4
- require "openapi3_parser/validation/error_collection"
6
+ require "openapi3_parser/node_factory/type_checker"
7
+ require "openapi3_parser/node/map"
8
+ require "openapi3_parser/validation/validatable"
9
+ require "openapi3_parser/validators/unexpected_fields"
5
10
 
6
11
  module Openapi3Parser
7
12
  module NodeFactory
8
- module Map
9
- include NodeFactory
13
+ class Map
14
+ attr_reader :allow_extensions, :context, :data, :default,
15
+ :key_input_type, :value_input_type, :value_factory,
16
+ :validation
10
17
 
11
- def self.included(base)
12
- base.extend(NodeFactory::ClassMethods)
13
- base.class_eval do
14
- input_type Hash
15
- end
18
+ # rubocop:disable Metrics/ParameterLists
19
+ def initialize(
20
+ context,
21
+ allow_extensions: false,
22
+ default: {},
23
+ key_input_type: String,
24
+ value_input_type: nil,
25
+ value_factory: nil,
26
+ validate: nil
27
+ )
28
+ @context = context
29
+ @allow_extensions = allow_extensions
30
+ @default = default
31
+ @key_input_type = key_input_type
32
+ @value_input_type = value_input_type
33
+ @value_factory = value_factory
34
+ @validation = validate
35
+ @data = build_data(context.input)
36
+ end
37
+ # rubocop:enable Metrics/ParameterLists
38
+
39
+ def raw_input
40
+ context.input
41
+ end
42
+
43
+ def resolved_input
44
+ @resolved_input ||= build_resolved_input
45
+ end
46
+
47
+ def nil_input?
48
+ context.input.nil?
49
+ end
50
+
51
+ def valid?
52
+ errors.empty?
53
+ end
54
+
55
+ def errors
56
+ @errors ||= ValidNodeBuilder.errors(self)
57
+ end
58
+
59
+ def node
60
+ @node ||= begin
61
+ data = ValidNodeBuilder.data(self)
62
+ data.nil? ? nil : build_node(data)
63
+ end
64
+ end
65
+
66
+ def inspect
67
+ %{#{self.class.name}(#{context.source_location.inspect})}
16
68
  end
17
69
 
18
70
  private
19
71
 
20
- def build_node(input)
21
- data = input.each_with_object({}) do |(key, value), memo|
22
- memo[key] = value.respond_to?(:node) ? value.node : value
72
+ def build_data(raw_input)
73
+ use_default = nil_input? || !raw_input.is_a?(::Hash)
74
+ return if use_default && default.nil?
75
+ process_data(use_default ? default : raw_input)
76
+ end
77
+
78
+ def process_data(data)
79
+ data.each_with_object({}) do |(key, value), memo|
80
+ memo[key] = if EXTENSION_REGEX =~ key || !value_factory
81
+ value
82
+ else
83
+ next_context = Context.next_field(context, key)
84
+ initialize_value_factory(next_context)
85
+ end
23
86
  end
24
- build_map(data, context)
25
87
  end
26
88
 
27
- def validate_input
28
- processed_input.each_value.inject(super) do |memo, value|
29
- errors = value.respond_to?(:errors) ? value.errors : []
30
- Validation::ErrorCollection.combine(memo, errors)
89
+ def initialize_value_factory(field_context)
90
+ if value_factory.is_a?(Class)
91
+ value_factory.new(field_context)
92
+ else
93
+ value_factory.call(field_context)
31
94
  end
32
95
  end
33
96
 
34
- def build_map(data, _)
35
- data
97
+ def build_node(data)
98
+ Node::Map.new(data, context) if data
36
99
  end
37
100
 
38
101
  def build_resolved_input
39
- processed_input.each_with_object({}) do |(key, value), memo|
102
+ return unless data
103
+
104
+ data.each_with_object({}) do |(key, value), memo|
40
105
  memo[key] = if value.respond_to?(:resolved_input)
41
106
  value.resolved_input
42
107
  else
@@ -45,8 +110,128 @@ module Openapi3Parser
45
110
  end
46
111
  end
47
112
 
48
- def default
49
- {}
113
+ class ValidNodeBuilder
114
+ def self.errors(factory)
115
+ new(factory).errors
116
+ end
117
+
118
+ def self.data(factory)
119
+ new(factory).data
120
+ end
121
+
122
+ def initialize(factory)
123
+ @factory = factory
124
+ @validatable = Validation::Validatable.new(factory)
125
+ end
126
+
127
+ def errors
128
+ return validatable.collection if factory.nil_input?
129
+ TypeChecker.validate_type(validatable, type: ::Hash)
130
+ return validatable.collection if validatable.errors.any?
131
+ collate_errors
132
+ validatable.collection
133
+ end
134
+
135
+ def data
136
+ return default_value if factory.nil_input?
137
+
138
+ TypeChecker.raise_on_invalid_type(factory.context, type: ::Hash)
139
+ check_unexpected_fields(raise_on_invalid: true)
140
+ check_keys(raise_on_invalid: true)
141
+ check_values(raise_on_invalid: true)
142
+ validate(raise_on_invalid: true)
143
+
144
+ factory.data.each_with_object({}) do |(key, value), memo|
145
+ memo[key] = value.respond_to?(:node) ? value.node : value
146
+ end
147
+ end
148
+
149
+ private_class_method :new
150
+
151
+ private
152
+
153
+ attr_reader :factory, :validatable
154
+
155
+ def collate_errors
156
+ check_unexpected_fields(raise_on_invalid: false)
157
+ check_keys(raise_on_invalid: false)
158
+ check_values(raise_on_invalid: false)
159
+ validate(raise_on_invalid: false)
160
+
161
+ factory.data.each_value do |value|
162
+ validatable.add_errors(value.errors) if value.respond_to?(:errors)
163
+ end
164
+ end
165
+
166
+ def default_value
167
+ if factory.nil_input? && factory.default.nil?
168
+ nil
169
+ else
170
+ factory.data
171
+ end
172
+ end
173
+
174
+ def check_unexpected_fields(raise_on_invalid: false)
175
+ Validators::UnexpectedFields.call(
176
+ validatable,
177
+ allowed_fields: nil,
178
+ raise_on_invalid: raise_on_invalid,
179
+ allow_extensions: factory.allow_extensions
180
+ )
181
+ end
182
+
183
+ def check_keys(raise_on_invalid: false)
184
+ return unless factory.key_input_type
185
+
186
+ if raise_on_invalid
187
+ TypeChecker.raise_on_invalid_keys(factory.context,
188
+ type: factory.key_input_type)
189
+ else
190
+ TypeChecker.validate_keys(validatable,
191
+ type: factory.key_input_type,
192
+ context: factory.context)
193
+ end
194
+ end
195
+
196
+ def check_values(raise_on_invalid: false)
197
+ return unless factory.value_input_type
198
+
199
+ factory.context.input.keys.each do |key|
200
+ check_field_type(
201
+ Context.next_field(factory.context, key), raise_on_invalid
202
+ )
203
+ end
204
+ end
205
+
206
+ def check_field_type(context, raise_on_invalid)
207
+ if raise_on_invalid
208
+ TypeChecker.raise_on_invalid_type(context,
209
+ type: factory.value_input_type)
210
+ else
211
+ TypeChecker.validate_type(validatable,
212
+ type: factory.value_input_type,
213
+ context: context)
214
+ end
215
+ end
216
+
217
+ def validate(raise_on_invalid: false)
218
+ run_validation
219
+
220
+ return if !raise_on_invalid || validatable.errors.empty?
221
+
222
+ first_error = validatable.errors.first
223
+ raise Openapi3Parser::Error::InvalidData,
224
+ "Invalid data for #{first_error.context.location_summary}. "\
225
+ "#{first_error.message}"
226
+ end
227
+
228
+ def run_validation
229
+ if factory.validation.is_a?(Symbol)
230
+ factory.send(factory.validation, validatable)
231
+ else
232
+ factory.validation&.call(validatable)
233
+ end
234
+ end
50
235
  end
51
236
  end
52
237
  end
@@ -1,18 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openapi3_parser/node/media_type"
4
+ require "openapi3_parser/node_factory/map"
4
5
  require "openapi3_parser/node_factory/object"
5
6
  require "openapi3_parser/node_factory/optional_reference"
6
- require "openapi3_parser/node_factories/map"
7
- require "openapi3_parser/node_factories/schema"
8
- require "openapi3_parser/node_factories/example"
9
- require "openapi3_parser/node_factories/encoding"
7
+ require "openapi3_parser/node_factory/schema"
8
+ require "openapi3_parser/node_factory/example"
9
+ require "openapi3_parser/node_factory/encoding"
10
10
 
11
11
  module Openapi3Parser
12
- module NodeFactories
13
- class MediaType
14
- include NodeFactory::Object
15
-
12
+ module NodeFactory
13
+ class MediaType < NodeFactory::Object
16
14
  allow_extensions
17
15
 
18
16
  field "schema", factory: :schema_factory
@@ -29,20 +27,22 @@ module Openapi3Parser
29
27
  end
30
28
 
31
29
  def schema_factory(context)
32
- factory = NodeFactories::Schema
30
+ factory = NodeFactory::Schema
33
31
  NodeFactory::OptionalReference.new(factory).call(context)
34
32
  end
35
33
 
36
34
  def examples_factory(context)
37
- factory = NodeFactory::OptionalReference.new(NodeFactories::Example)
38
- NodeFactories::Map.new(context, default: nil, value_factory: factory)
35
+ factory = NodeFactory::OptionalReference.new(NodeFactory::Example)
36
+ NodeFactory::Map.new(context,
37
+ default: nil,
38
+ value_factory: factory)
39
39
  end
40
40
 
41
41
  def encoding_factory(context)
42
- NodeFactories::Map.new(
42
+ NodeFactory::Map.new(
43
43
  context,
44
44
  validate: EncodingValidator.new(self),
45
- value_factory: NodeFactories::Encoding
45
+ value_factory: NodeFactory::Encoding
46
46
  )
47
47
  end
48
48
 
@@ -51,9 +51,10 @@ module Openapi3Parser
51
51
  @factory = factory
52
52
  end
53
53
 
54
- def call(input, _context)
55
- missing_keys = input.keys - properties
56
- error_message(missing_keys) unless missing_keys.empty?
54
+ def call(validatable)
55
+ missing_keys = validatable.input.keys - properties
56
+ return if missing_keys.empty?
57
+ validatable.add_error(error_message(missing_keys))
57
58
  end
58
59
 
59
60
  private
@@ -4,10 +4,8 @@ require "openapi3_parser/node/oauth_flow"
4
4
  require "openapi3_parser/node_factory/object"
5
5
 
6
6
  module Openapi3Parser
7
- module NodeFactories
8
- class OauthFlow
9
- include NodeFactory::Object
10
-
7
+ module NodeFactory
8
+ class OauthFlow < NodeFactory::Object
11
9
  allow_extensions
12
10
  field "authorizationUrl", input_type: String
13
11
  field "tokenUrl", input_type: String
@@ -1,15 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openapi3_parser/node/oauth_flows"
4
- require "openapi3_parser/node_factories/oauth_flow"
4
+ require "openapi3_parser/node_factory/oauth_flow"
5
5
  require "openapi3_parser/node_factory/object"
6
6
  require "openapi3_parser/node_factory/optional_reference"
7
7
 
8
8
  module Openapi3Parser
9
- module NodeFactories
10
- class OauthFlows
11
- include NodeFactory::Object
12
-
9
+ module NodeFactory
10
+ class OauthFlows < NodeFactory::Object
13
11
  allow_extensions
14
12
  field "implicit", factory: :oauth_flow_factory
15
13
  field "password", factory: :oauth_flow_factory
@@ -19,7 +17,7 @@ module Openapi3Parser
19
17
  private
20
18
 
21
19
  def oauth_flow_factory(context)
22
- NodeFactory::OptionalReference.new(NodeFactories::OauthFlow)
20
+ NodeFactory::OptionalReference.new(NodeFactory::OauthFlow)
23
21
  .call(context)
24
22
  end
25
23