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
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Openapi3Parser
4
+ module NodeFactory
5
+ class RecursivePointer
6
+ attr_reader :reference_context
7
+
8
+ def initialize(reference_context)
9
+ @reference_context = reference_context
10
+ end
11
+
12
+ def node
13
+ reference_context.node
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/error"
4
+ require "openapi3_parser/node_factory/fields/reference"
5
+ require "openapi3_parser/node_factory/object"
6
+ require "openapi3_parser/node_factory/recursive_pointer"
7
+
8
+ module Openapi3Parser
9
+ module NodeFactory
10
+ class Reference < NodeFactory::Object
11
+ field "$ref", input_type: String, required: true, factory: :ref_factory
12
+
13
+ attr_reader :factory
14
+
15
+ def initialize(context, factory)
16
+ @factory = factory
17
+ super(context)
18
+ end
19
+
20
+ def in_recursive_loop?
21
+ data["$ref"].in_recursive_loop?
22
+ end
23
+
24
+ def recursive_pointer
25
+ NodeFactory::RecursivePointer.new(data["$ref"].reference_context)
26
+ end
27
+
28
+ private
29
+
30
+ def build_node
31
+ if in_recursive_loop?
32
+ raise Error::InRecursiveStructure,
33
+ "Can't build node as it references itself, use "\
34
+ "recursive_pointer"
35
+ end
36
+ data["$ref"].node
37
+ end
38
+
39
+ def ref_factory(context)
40
+ NodeFactory::Fields::Reference.new(context, factory)
41
+ end
42
+
43
+ def build_resolved_input
44
+ data["$ref"].resolved_input
45
+ end
46
+ end
47
+ end
48
+ end
@@ -2,17 +2,15 @@
2
2
 
3
3
  require "openapi3_parser/context"
4
4
  require "openapi3_parser/node/request_body"
5
+ require "openapi3_parser/node_factory/map"
5
6
  require "openapi3_parser/node_factory/object"
6
- require "openapi3_parser/node_factories/media_type"
7
- require "openapi3_parser/node_factories/map"
7
+ require "openapi3_parser/node_factory/media_type"
8
8
  require "openapi3_parser/validation/error"
9
9
  require "openapi3_parser/validators/media_type"
10
10
 
11
11
  module Openapi3Parser
12
- module NodeFactories
13
- class RequestBody
14
- include NodeFactory::Object
15
-
12
+ module NodeFactory
13
+ class RequestBody < NodeFactory::Object
16
14
  allow_extensions
17
15
  field "description", input_type: String
18
16
  field "content", factory: :content_factory, required: true
@@ -25,9 +23,9 @@ module Openapi3Parser
25
23
  end
26
24
 
27
25
  def content_factory(context)
28
- NodeFactories::Map.new(
26
+ NodeFactory::Map.new(
29
27
  context,
30
- value_factory: NodeFactories::MediaType,
28
+ value_factory: NodeFactory::MediaType,
31
29
  validate: ContentValidator
32
30
  )
33
31
  end
@@ -37,22 +35,20 @@ module Openapi3Parser
37
35
  new.call(*args)
38
36
  end
39
37
 
40
- def call(input, context)
38
+ def call(validatable)
41
39
  # This validation isn't actually mentioned in the spec, but it
42
40
  # doesn't seem to make sense if this is an empty hash.
43
- return "Expected to have at least 1 item" if input.size.zero?
44
-
45
- input.keys.each_with_object([]) do |key, memo|
46
- message = Validators::MediaType.call(key)
47
- memo << create_error(key, context, message) if message
41
+ if validatable.input.size.zero?
42
+ return validatable.add_error("Expected to have at least 1 item")
48
43
  end
49
- end
50
44
 
51
- private
45
+ validatable.input.keys.each do |key|
46
+ message = Validators::MediaType.call(key)
47
+ next unless message
52
48
 
53
- def create_error(key, parent_context, message)
54
- context = Context.next_field(parent_context, key)
55
- Validation::Error.new(message, context)
49
+ context = Context.next_field(validatable.context, key)
50
+ validatable.add_error(message, context)
51
+ end
56
52
  end
57
53
  end
58
54
  end
@@ -2,21 +2,19 @@
2
2
 
3
3
  require "openapi3_parser/context"
4
4
  require "openapi3_parser/node/response"
5
+ require "openapi3_parser/node_factory/map"
5
6
  require "openapi3_parser/node_factory/object"
6
7
  require "openapi3_parser/node_factory/optional_reference"
7
- require "openapi3_parser/node_factories/map"
8
- require "openapi3_parser/node_factories/header"
9
- require "openapi3_parser/node_factories/media_type"
10
- require "openapi3_parser/node_factories/link"
11
- require "openapi3_parser/validation/error"
8
+ require "openapi3_parser/node_factory/header"
9
+ require "openapi3_parser/node_factory/media_type"
10
+ require "openapi3_parser/node_factory/link"
11
+ require "openapi3_parser/validation/input_validator"
12
12
  require "openapi3_parser/validators/media_type"
13
13
  require "openapi3_parser/validators/component_keys"
14
14
 
15
15
  module Openapi3Parser
16
- module NodeFactories
17
- class Response
18
- include NodeFactory::Object
19
-
16
+ module NodeFactory
17
+ class Response < NodeFactory::Object
20
18
  allow_extensions
21
19
  field "description", input_type: String, required: true
22
20
  field "headers", factory: :headers_factory
@@ -30,34 +28,33 @@ module Openapi3Parser
30
28
  end
31
29
 
32
30
  def headers_factory(context)
33
- factory = NodeFactory::OptionalReference.new(NodeFactories::Header)
34
- NodeFactories::Map.new(context, value_factory: factory)
31
+ factory = NodeFactory::OptionalReference.new(NodeFactory::Header)
32
+ NodeFactory::Map.new(context, value_factory: factory)
35
33
  end
36
34
 
37
35
  def content_factory(context)
38
- NodeFactories::Map.new(
36
+ NodeFactory::Map.new(
39
37
  context,
40
38
  validate: method(:validate_content),
41
- value_factory: NodeFactories::MediaType
39
+ value_factory: NodeFactory::MediaType
42
40
  )
43
41
  end
44
42
 
45
43
  def links_factory(context)
46
- factory = NodeFactory::OptionalReference.new(NodeFactories::Link)
47
- NodeFactories::Map.new(
44
+ factory = NodeFactory::OptionalReference.new(NodeFactory::Link)
45
+ NodeFactory::Map.new(
48
46
  context,
49
- validate: ->(input, _) { Validators::ComponentKeys.call(input) },
47
+ validate: Validation::InputValidator.new(Validators::ComponentKeys),
50
48
  value_factory: factory
51
49
  )
52
50
  end
53
51
 
54
- def validate_content(input, context)
55
- input.keys.each_with_object([]) do |key, memo|
52
+ def validate_content(validatable)
53
+ validatable.input.each_key do |key|
56
54
  message = Validators::MediaType.call(key)
57
55
  next unless message
58
- memo << Validation::Error.new(
59
- message, Context.next_field(context, key)
60
- )
56
+ validatable.add_error(message,
57
+ Context.next_field(validatable.context, key))
61
58
  end
62
59
  end
63
60
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/context"
4
+ require "openapi3_parser/node_factory/map"
5
+ require "openapi3_parser/node_factory/response"
6
+ require "openapi3_parser/node_factory/optional_reference"
7
+ require "openapi3_parser/node/responses"
8
+
9
+ module Openapi3Parser
10
+ module NodeFactory
11
+ class Responses < NodeFactory::Map
12
+ KEY_REGEX = /
13
+ \A
14
+ (
15
+ default
16
+ |
17
+ [1-5]([0-9][0-9]|XX)
18
+ )
19
+ \Z
20
+ /x
21
+
22
+ def initialize(context)
23
+ factory = NodeFactory::OptionalReference.new(NodeFactory::Response)
24
+
25
+ super(context,
26
+ allow_extensions: true,
27
+ value_factory: factory,
28
+ validate: :validate_keys)
29
+ end
30
+
31
+ private
32
+
33
+ def build_node(data)
34
+ Node::Responses.new(data, context)
35
+ end
36
+
37
+ def validate_keys(validatable)
38
+ invalid = validatable.input.keys.reject do |key|
39
+ NodeFactory::EXTENSION_REGEX.match(key) ||
40
+ KEY_REGEX.match(key)
41
+ end
42
+
43
+ return if invalid.empty?
44
+
45
+ codes = invalid.map { |k| "'#{k}'" }.join(", ")
46
+ validatable.add_error("Invalid responses keys: #{codes} - default, "\
47
+ "status codes and status code ranges allowed")
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,19 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openapi3_parser/node/schema"
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/array"
8
- require "openapi3_parser/node_factories/external_documentation"
9
- require "openapi3_parser/node_factories/discriminator"
10
- require "openapi3_parser/node_factories/xml"
7
+ require "openapi3_parser/node_factory/array"
8
+ require "openapi3_parser/node_factory/external_documentation"
9
+ require "openapi3_parser/node_factory/discriminator"
10
+ require "openapi3_parser/node_factory/xml"
11
11
 
12
12
  module Openapi3Parser
13
- module NodeFactories
14
- class Schema
15
- include NodeFactory::Object
16
-
13
+ module NodeFactory
14
+ class Schema < NodeFactory::Object
17
15
  allow_extensions
18
16
  field "title", input_type: String
19
17
  field "multipleOf", input_type: Numeric
@@ -40,7 +38,7 @@ module Openapi3Parser
40
38
  field "items", factory: :referenceable_schema
41
39
  field "properties", factory: :properties_factory
42
40
  field "additionalProperties",
43
- input_type: :additional_properties_input_type,
41
+ validate: :additional_properties_input_type,
44
42
  factory: :additional_properties_factory,
45
43
  default: false
46
44
  field "description", input_type: String
@@ -56,28 +54,29 @@ module Openapi3Parser
56
54
  field "example"
57
55
  field "deprecated", input_type: :boolean, default: false
58
56
 
59
- private
57
+ validate :items_for_array, :read_only_or_write_only
60
58
 
61
- def build_object(data, context)
62
- Node::Schema.new(data, context)
63
- end
59
+ private
64
60
 
65
- def validate(input, _context)
66
- errors = []
61
+ def items_for_array(validatable)
62
+ return unless validatable.input["type"] == "array"
63
+ return unless validatable.factory.resolved_input["items"].nil?
67
64
 
68
- if input["type"] == "array" && resolved_input["items"].nil?
69
- errors << "items must be defined for a type of array"
70
- end
65
+ validatable.add_error("items must be defined for a type of array")
66
+ end
71
67
 
72
- if input["readOnly"] == true && input["writeOnly"] == true
73
- errors << "readOnly and writeOnly cannot both be true"
74
- end
68
+ def read_only_or_write_only(validatable)
69
+ input = validatable.input
70
+ return if [input["readOnly"], input["writeOnly"]].uniq != [true]
71
+ validatable.add_error("readOnly and writeOnly cannot both be true")
72
+ end
75
73
 
76
- errors
74
+ def build_object(data, context)
75
+ Node::Schema.new(data, context)
77
76
  end
78
77
 
79
78
  def required_factory(context)
80
- NodeFactories::Array.new(
79
+ NodeFactory::Array.new(
81
80
  context,
82
81
  default: nil,
83
82
  value_input_type: String
@@ -85,25 +84,25 @@ module Openapi3Parser
85
84
  end
86
85
 
87
86
  def enum_factory(context)
88
- NodeFactories::Array.new(context, default: nil)
87
+ NodeFactory::Array.new(context, default: nil)
89
88
  end
90
89
 
91
90
  def disciminator_factory(context)
92
- NodeFactories::Discriminator.new(context)
91
+ NodeFactory::Discriminator.new(context)
93
92
  end
94
93
 
95
94
  def xml_factory(context)
96
- NodeFactories::Xml.new(context)
95
+ NodeFactory::Xml.new(context)
97
96
  end
98
97
 
99
98
  def external_docs_factory(context)
100
- NodeFactories::ExternalDocumentation.new(context)
99
+ NodeFactory::ExternalDocumentation.new(context)
101
100
  end
102
101
 
103
102
  def properties_factory(context)
104
- NodeFactories::Map.new(
103
+ NodeFactory::Map.new(
105
104
  context,
106
- value_factory: NodeFactories::Schema
105
+ value_factory: NodeFactory::OptionalReference.new(self.class)
107
106
  )
108
107
  end
109
108
 
@@ -112,16 +111,17 @@ module Openapi3Parser
112
111
  end
113
112
 
114
113
  def referenceable_schema_array(context)
115
- NodeFactories::Array.new(
114
+ NodeFactory::Array.new(
116
115
  context,
117
116
  default: nil,
118
117
  value_factory: NodeFactory::OptionalReference.new(self.class)
119
118
  )
120
119
  end
121
120
 
122
- def additional_properties_input_type(input)
121
+ def additional_properties_input_type(validatable)
122
+ input = validatable.input
123
123
  return if [true, false].include?(input) || input.is_a?(Hash)
124
- "Expected a boolean or an object"
124
+ validatable.add_error("Expected a Boolean or an Object")
125
125
  end
126
126
 
127
127
  def additional_properties_factory(context)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/security_requirement"
4
+ require "openapi3_parser/node_factory/array"
5
+ require "openapi3_parser/node_factory/map"
6
+
7
+ module Openapi3Parser
8
+ module NodeFactory
9
+ class SecurityRequirement < NodeFactory::Map
10
+ def initialize(context)
11
+ super(context, value_factory: NodeFactory::Array)
12
+ end
13
+
14
+ private
15
+
16
+ def build_node(data)
17
+ Node::SecurityRequirement.new(data, context)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,15 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openapi3_parser/node/security_scheme"
4
- require "openapi3_parser/node_factories/oauth_flows"
4
+ require "openapi3_parser/node_factory/oauth_flows"
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 SecurityScheme
11
- include NodeFactory::Object
12
-
9
+ module NodeFactory
10
+ class SecurityScheme < NodeFactory::Object
13
11
  allow_extensions
14
12
 
15
13
  field "type", input_type: String, required: true
@@ -28,7 +26,7 @@ module Openapi3Parser
28
26
  end
29
27
 
30
28
  def flows_factory(context)
31
- NodeFactories::OauthFlows.new(context)
29
+ NodeFactory::OauthFlows.new(context)
32
30
  end
33
31
  end
34
32
  end
@@ -1,15 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openapi3_parser/node/server"
4
+ require "openapi3_parser/node_factory/map"
4
5
  require "openapi3_parser/node_factory/object"
5
- require "openapi3_parser/node_factories/server_variable"
6
- require "openapi3_parser/node_factories/map"
6
+ require "openapi3_parser/node_factory/server_variable"
7
7
 
8
8
  module Openapi3Parser
9
- module NodeFactories
10
- class Server
11
- include NodeFactory::Object
12
-
9
+ module NodeFactory
10
+ class Server < NodeFactory::Object
13
11
  allow_extensions
14
12
  field "url", input_type: String, required: true
15
13
  field "description", input_type: String
@@ -22,9 +20,9 @@ module Openapi3Parser
22
20
  end
23
21
 
24
22
  def variables_factory(context)
25
- NodeFactories::Map.new(
23
+ NodeFactory::Map.new(
26
24
  context,
27
- value_factory: NodeFactories::ServerVariable
25
+ value_factory: NodeFactory::ServerVariable
28
26
  )
29
27
  end
30
28
  end