openapi3_parser 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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