openapi3_parser 0.5.2 → 0.6.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +3 -3
  4. data/CHANGELOG.md +7 -1
  5. data/README.md +102 -15
  6. data/lib/openapi3_parser/document.rb +14 -14
  7. data/lib/openapi3_parser/document/reference_registry.rb +72 -0
  8. data/lib/openapi3_parser/node/array.rb +10 -2
  9. data/lib/openapi3_parser/node/components.rb +9 -9
  10. data/lib/openapi3_parser/node/contact.rb +3 -3
  11. data/lib/openapi3_parser/node/context.rb +129 -0
  12. data/lib/openapi3_parser/node/discriminator.rb +2 -2
  13. data/lib/openapi3_parser/node/encoding.rb +5 -5
  14. data/lib/openapi3_parser/node/example.rb +4 -4
  15. data/lib/openapi3_parser/node/external_documentation.rb +2 -2
  16. data/lib/openapi3_parser/node/info.rb +6 -6
  17. data/lib/openapi3_parser/node/license.rb +2 -2
  18. data/lib/openapi3_parser/node/link.rb +6 -6
  19. data/lib/openapi3_parser/node/map.rb +8 -4
  20. data/lib/openapi3_parser/node/media_type.rb +5 -5
  21. data/lib/openapi3_parser/node/oauth_flow.rb +4 -4
  22. data/lib/openapi3_parser/node/oauth_flows.rb +4 -4
  23. data/lib/openapi3_parser/node/object.rb +8 -4
  24. data/lib/openapi3_parser/node/openapi.rb +8 -8
  25. data/lib/openapi3_parser/node/operation.rb +12 -12
  26. data/lib/openapi3_parser/node/parameter.rb +2 -2
  27. data/lib/openapi3_parser/node/parameter_like.rb +11 -11
  28. data/lib/openapi3_parser/node/path_item.rb +12 -12
  29. data/lib/openapi3_parser/node/placeholder.rb +34 -0
  30. data/lib/openapi3_parser/node/request_body.rb +3 -3
  31. data/lib/openapi3_parser/node/response.rb +4 -4
  32. data/lib/openapi3_parser/node/responses.rb +1 -1
  33. data/lib/openapi3_parser/node/schema.rb +36 -36
  34. data/lib/openapi3_parser/node/security_scheme.rb +8 -8
  35. data/lib/openapi3_parser/node/server.rb +3 -3
  36. data/lib/openapi3_parser/node/server_variable.rb +3 -3
  37. data/lib/openapi3_parser/node/tag.rb +3 -3
  38. data/lib/openapi3_parser/node/xml.rb +5 -5
  39. data/lib/openapi3_parser/node_factory/array.rb +15 -13
  40. data/lib/openapi3_parser/node_factory/callback.rb +2 -2
  41. data/lib/openapi3_parser/node_factory/context.rb +111 -0
  42. data/lib/openapi3_parser/node_factory/field.rb +5 -7
  43. data/lib/openapi3_parser/node_factory/fields/reference.rb +43 -24
  44. data/lib/openapi3_parser/node_factory/link.rb +1 -1
  45. data/lib/openapi3_parser/node_factory/map.rb +14 -12
  46. data/lib/openapi3_parser/node_factory/object.rb +9 -5
  47. data/lib/openapi3_parser/node_factory/object_factory/node_builder.rb +21 -28
  48. data/lib/openapi3_parser/node_factory/optional_reference.rb +4 -0
  49. data/lib/openapi3_parser/node_factory/parameter_like.rb +0 -2
  50. data/lib/openapi3_parser/node_factory/path_item.rb +7 -4
  51. data/lib/openapi3_parser/node_factory/paths.rb +2 -2
  52. data/lib/openapi3_parser/node_factory/reference.rb +17 -10
  53. data/lib/openapi3_parser/node_factory/responses.rb +2 -2
  54. data/lib/openapi3_parser/node_factory/security_requirement.rb +2 -2
  55. data/lib/openapi3_parser/source.rb +27 -24
  56. data/lib/openapi3_parser/{context → source}/location.rb +13 -1
  57. data/lib/openapi3_parser/{context → source}/pointer.rb +2 -2
  58. data/lib/openapi3_parser/source/resolved_reference.rb +67 -0
  59. data/lib/openapi3_parser/validators/duplicate_parameters.rb +8 -4
  60. data/lib/openapi3_parser/validators/reference.rb +3 -3
  61. data/lib/openapi3_parser/version.rb +1 -1
  62. data/openapi3_parser.gemspec +1 -1
  63. metadata +11 -10
  64. data/lib/openapi3_parser/context.rb +0 -162
  65. data/lib/openapi3_parser/document/reference_register.rb +0 -48
  66. data/lib/openapi3_parser/node_factory/recursive_pointer.rb +0 -17
  67. data/lib/openapi3_parser/source/reference_resolver.rb +0 -82
@@ -8,12 +8,12 @@ module Openapi3Parser
8
8
  class SecurityScheme < Node::Object
9
9
  # @return [String, nil]
10
10
  def type
11
- node_data["type"]
11
+ self["type"]
12
12
  end
13
13
 
14
14
  # @return [String, nil]
15
15
  def description
16
- node_data["description"]
16
+ self["description"]
17
17
  end
18
18
 
19
19
  # @return [String, nil]
@@ -23,32 +23,32 @@ module Openapi3Parser
23
23
 
24
24
  # @return [String, nil]
25
25
  def name
26
- node_data["name"]
26
+ self["name"]
27
27
  end
28
28
 
29
29
  # @return [String, nil]
30
30
  def in
31
- node_data["in"]
31
+ self["in"]
32
32
  end
33
33
 
34
34
  # @return [String, nil]
35
35
  def scheme
36
- node_data["scheme"]
36
+ self["scheme"]
37
37
  end
38
38
 
39
39
  # @return [String, nil]
40
40
  def bearer_format
41
- node_data["bearerFormat"]
41
+ self["bearerFormat"]
42
42
  end
43
43
 
44
44
  # @return [OauthFlows, nil]
45
45
  def flows
46
- node_data["flows"]
46
+ self["flows"]
47
47
  end
48
48
 
49
49
  # @return [String, nil]
50
50
  def open_id_connect_url
51
- node_data["openIdConnectUrl"]
51
+ self["openIdConnectUrl"]
52
52
  end
53
53
  end
54
54
  end
@@ -8,12 +8,12 @@ module Openapi3Parser
8
8
  class Server < Node::Object
9
9
  # @return [String]
10
10
  def url
11
- node_data["url"]
11
+ self["url"]
12
12
  end
13
13
 
14
14
  # @return [String, nil]
15
15
  def description
16
- node_data["description"]
16
+ self["description"]
17
17
  end
18
18
 
19
19
  # @return [String, nil]
@@ -23,7 +23,7 @@ module Openapi3Parser
23
23
 
24
24
  # @return [Map<String, ServerVariable>]
25
25
  def variables
26
- node_data["variables"]
26
+ self["variables"]
27
27
  end
28
28
  end
29
29
  end
@@ -8,17 +8,17 @@ module Openapi3Parser
8
8
  class ServerVariable < Node::Object
9
9
  # @return [Node::Array<String>, nil]
10
10
  def enum
11
- node_data["enum"]
11
+ self["enum"]
12
12
  end
13
13
 
14
14
  # @return [String]
15
15
  def default
16
- node_data["default"]
16
+ self["default"]
17
17
  end
18
18
 
19
19
  # @return [String, nil]
20
20
  def description
21
- node_data["description"]
21
+ self["description"]
22
22
  end
23
23
 
24
24
  # @return [String, nil]
@@ -8,12 +8,12 @@ module Openapi3Parser
8
8
  class Tag < Node::Object
9
9
  # @return [String]
10
10
  def name
11
- node_data["name"]
11
+ self["name"]
12
12
  end
13
13
 
14
14
  # @return [String, nil]
15
15
  def description
16
- node_data["description"]
16
+ self["description"]
17
17
  end
18
18
 
19
19
  # @return [String, nil]
@@ -23,7 +23,7 @@ module Openapi3Parser
23
23
 
24
24
  # @return [ExternalDocumentation, nil]
25
25
  def external_docs
26
- node_data["externalDocs"]
26
+ self["externalDocs"]
27
27
  end
28
28
  end
29
29
  end
@@ -8,27 +8,27 @@ module Openapi3Parser
8
8
  class Xml < Node::Object
9
9
  # @return [String, nil]
10
10
  def name
11
- node_data["name"]
11
+ self["name"]
12
12
  end
13
13
 
14
14
  # @return [String, nil]
15
15
  def namespace
16
- node_data["namespace"]
16
+ self["namespace"]
17
17
  end
18
18
 
19
19
  # @return [String, nil]
20
20
  def prefix
21
- node_data["prefix"]
21
+ self["prefix"]
22
22
  end
23
23
 
24
24
  # @return [Boolean]
25
25
  def attribute?
26
- node_data["attribute"]
26
+ self["attribute"]
27
27
  end
28
28
 
29
29
  # @return [Boolean]
30
30
  def wrapped?
31
- node_data["wrapped"]
31
+ self["wrapped"]
32
32
  end
33
33
  end
34
34
  end
@@ -41,11 +41,9 @@ module Openapi3Parser
41
41
  @errors ||= ValidNodeBuilder.errors(self)
42
42
  end
43
43
 
44
- def node
45
- @node ||= begin
46
- data = ValidNodeBuilder.data(self)
47
- data.nil? ? nil : build_node(data)
48
- end
44
+ def node(node_context)
45
+ data = ValidNodeBuilder.data(self, node_context)
46
+ data.nil? ? nil : build_node(data, node_context)
49
47
  end
50
48
 
51
49
  def inspect
@@ -78,8 +76,8 @@ module Openapi3Parser
78
76
  end
79
77
  end
80
78
 
81
- def build_node(data)
82
- Node::Array.new(data, context) if data
79
+ def build_node(data, node_context)
80
+ Node::Array.new(data, node_context) if data
83
81
  end
84
82
 
85
83
  def build_resolved_input
@@ -95,8 +93,8 @@ module Openapi3Parser
95
93
  new(factory).errors
96
94
  end
97
95
 
98
- def self.data(factory)
99
- new(factory).data
96
+ def self.data(factory, parent_context)
97
+ new(factory).data(parent_context)
100
98
  end
101
99
 
102
100
  def initialize(factory)
@@ -112,15 +110,19 @@ module Openapi3Parser
112
110
  validatable.collection
113
111
  end
114
112
 
115
- def data
113
+ def data(parent_context)
116
114
  return default_value if factory.nil_input?
117
115
 
118
116
  TypeChecker.raise_on_invalid_type(factory.context, type: ::Array)
119
117
  check_values(raise_on_invalid: true)
120
118
  validate(raise_on_invalid: true)
121
119
 
122
- factory.data.map do |value|
123
- value.respond_to?(:node) ? value.node : value
120
+ factory.data.each_with_index.map do |value, i|
121
+ if value.respond_to?(:node)
122
+ Node::Placeholder.new(value, i, parent_context)
123
+ else
124
+ value
125
+ end
124
126
  end
125
127
  end
126
128
 
@@ -175,7 +177,7 @@ module Openapi3Parser
175
177
 
176
178
  first_error = validatable.errors.first
177
179
  raise Openapi3Parser::Error::InvalidData,
178
- "Invalid data for #{first_error.context.location_summary}. "\
180
+ "Invalid data for #{first_error.context.location_summary}: "\
179
181
  "#{first_error.message}"
180
182
  end
181
183
 
@@ -13,8 +13,8 @@ module Openapi3Parser
13
13
 
14
14
  private
15
15
 
16
- def build_node(data)
17
- Node::Callback.new(data, context)
16
+ def build_node(data, node_context)
17
+ Node::Callback.new(data, node_context)
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Openapi3Parser
4
+ module NodeFactory
5
+ # This class is used to specify the data and source information for a
6
+ # NodeFactory. The same NodeFactory can be used multiple times if the
7
+ # object is referenced so it is limited in data about it's location
8
+ # within the document.
9
+ #
10
+ # @attr_reader [Any] input
11
+ # @attr_reader [Source::Location] source_location
12
+ # @attr_reader [Array<Source::Location>] refernce_locations
13
+ #
14
+ class Context
15
+ # Create a context for the root of a document
16
+ #
17
+ # @param [Any] input
18
+ # @param [Source] source
19
+ # @return [Context]
20
+ def self.root(input, source)
21
+ new(input, source_location: Source::Location.new(source, []))
22
+ end
23
+
24
+ # Create a factory context for a field within the current contexts data
25
+ # eg for a context of:
26
+ # root = Context.root({ "test" => {} }, source)
27
+ # we can get the context of "test" with:
28
+ # test = Context.next_field(root, "test")
29
+ #
30
+ # @param [Context] parent_context
31
+ # @param [String] field
32
+ # @return [Context]
33
+ def self.next_field(parent_context, field)
34
+ pc = parent_context
35
+ input = pc.input.respond_to?(:[]) ? pc.input[field] : nil
36
+ source_location = Source::Location.next_field(pc.source_location, field)
37
+ new(input,
38
+ source_location: source_location,
39
+ reference_locations: pc.reference_locations)
40
+ end
41
+
42
+ # Creates the context for a field that references another field
43
+ #
44
+ # @param [Context] reference_context
45
+ # @param [Source::Location] source_location
46
+ # @return [Context]
47
+ def self.resolved_reference(reference_context, source_location:)
48
+ reference_locations = [reference_context.source_location] +
49
+ reference_context.reference_locations
50
+
51
+ data = source_location.data if source_location.source_available?
52
+ new(data,
53
+ source_location: source_location,
54
+ reference_locations: reference_locations)
55
+ end
56
+
57
+ attr_reader :input, :source_location, :reference_locations
58
+
59
+ # @param [Any] input
60
+ # @param [Source::Location] source_location
61
+ # @param [Array<Source::Location>] reference_locations
62
+ def initialize(input,
63
+ source_location:,
64
+ reference_locations: [])
65
+ @input = input
66
+ @source_location = source_location
67
+ @reference_locations = reference_locations
68
+ end
69
+
70
+ # @return [Boolean]
71
+ def ==(other)
72
+ input == other.input &&
73
+ source_location == other.source_location &&
74
+ reference_locations == other.reference_locations
75
+ end
76
+
77
+ # @return [Source]
78
+ def source
79
+ source_location.source
80
+ end
81
+
82
+ # @param [String] reference
83
+ # @param [Object, Map, Array] factory
84
+ # @param [Boolean] recursive
85
+ # @return [Source::ResolvedReference]
86
+ def resolve_reference(reference, factory, recursive: false)
87
+ source.resolve_reference(reference, factory, self, recursive: recursive)
88
+ end
89
+
90
+ # Used to show when an recursive reference loop has begun
91
+ #
92
+ # @return [Boolean]
93
+ def self_referencing?
94
+ reference_locations.include?(source_location)
95
+ end
96
+
97
+ def inspect
98
+ %{#{self.class.name}(source_location: #{source_location}, } +
99
+ %{referenced_by: #{reference_locations.map(&:to_s).join(', ')})}
100
+ end
101
+
102
+ def location_summary
103
+ source_location.to_s
104
+ end
105
+
106
+ def to_s
107
+ location_summary
108
+ end
109
+ end
110
+ end
111
+ end
@@ -39,11 +39,9 @@ module Openapi3Parser
39
39
  @errors ||= ValidNodeBuilder.errors(self)
40
40
  end
41
41
 
42
- def node
43
- @node ||= begin
44
- data = ValidNodeBuilder.data(self)
45
- data.nil? ? nil : build_node(data)
46
- end
42
+ def node(node_context)
43
+ data = ValidNodeBuilder.data(self)
44
+ data.nil? ? nil : build_node(data, node_context)
47
45
  end
48
46
 
49
47
  def inspect
@@ -52,7 +50,7 @@ module Openapi3Parser
52
50
 
53
51
  private
54
52
 
55
- def build_node(data)
53
+ def build_node(data, _node_context)
56
54
  data
57
55
  end
58
56
 
@@ -108,7 +106,7 @@ module Openapi3Parser
108
106
 
109
107
  first_error = validatable.errors.first
110
108
  raise Openapi3Parser::Error::InvalidData,
111
- "Invalid data for #{first_error.context.location_summary}. "\
109
+ "Invalid data for #{first_error.context.location_summary}: "\
112
110
  "#{first_error.message}"
113
111
  end
114
112
 
@@ -7,55 +7,74 @@ module Openapi3Parser
7
7
  module NodeFactory
8
8
  module Fields
9
9
  class Reference < NodeFactory::Field
10
+ extend Forwardable
11
+
12
+ def_delegator :context, :self_referencing?
13
+
10
14
  def initialize(context, factory)
11
- context = Context.as_reference(context)
12
15
  super(context, input_type: String, validate: :validate)
13
16
  @factory = factory
14
17
  @reference = context.input
15
- @reference_resolver = create_reference_resolver
18
+ @resolved_reference = create_resolved_reference
16
19
  end
17
20
 
18
21
  def resolved_input
19
- return unless reference_resolver
22
+ return unless resolved_reference
20
23
 
21
- if in_recursive_loop?
22
- RecursiveResolvedInput.new(reference_context)
24
+ if context.self_referencing?
25
+ RecursiveResolvedInput.new(resolved_reference.factory)
23
26
  else
24
- reference_resolver.resolved_input
27
+ resolved_reference.resolved_input
25
28
  end
26
29
  end
27
30
 
28
- def in_recursive_loop?
29
- context.source_location == reference_context&.source_location
30
- end
31
-
32
- def reference_context
33
- context.referenced_by
31
+ def referenced_factory
32
+ resolved_reference&.factory
34
33
  end
35
34
 
36
35
  private
37
36
 
38
- attr_reader :reference, :factory, :reference_resolver
37
+ attr_reader :reference, :factory, :resolved_reference
38
+
39
+ def build_node(_data, node_context)
40
+ if resolved_reference.nil?
41
+ # this shouldn't happen unless dependant code changes
42
+ raise Openapi3Parser::Error,
43
+ "can't build node without a resolved reference"
44
+ end
45
+
46
+ reference_context = Node::Context.resolved_reference(
47
+ node_context, resolved_reference.factory.context
48
+ )
39
49
 
40
- def build_node(_data)
41
- reference_resolver&.node
50
+ resolved_reference.node(reference_context)
42
51
  end
43
52
 
44
53
  def validate(validatable)
45
54
  if !reference_validator.valid?
46
55
  validatable.add_errors(reference_validator.errors)
56
+ elsif !reference_resolves?
57
+ validatable.add_error("Reference doesn't resolve to an object")
47
58
  else
48
- validatable.add_errors(reference_resolver&.errors)
59
+ validatable.add_errors(resolved_reference&.errors)
49
60
  end
50
61
  end
51
62
 
63
+ def reference_resolves?
64
+ return true unless referenced_factory.is_a?(NodeFactory::Reference)
65
+
66
+ referenced_factory.resolves?
67
+ end
68
+
52
69
  def reference_validator
53
70
  @reference_validator ||= Validators::Reference.new(reference)
54
71
  end
55
72
 
56
- def create_reference_resolver
73
+ def create_resolved_reference
57
74
  return unless reference_validator.valid?
58
- context.register_reference(reference, factory)
75
+ context.resolve_reference(reference,
76
+ factory,
77
+ recursive: context.self_referencing?)
59
78
  end
60
79
 
61
80
  # Used in the place of a hash for resolved input so the value can
@@ -64,15 +83,15 @@ module Openapi3Parser
64
83
  extend Forwardable
65
84
  include Enumerable
66
85
 
67
- def_delegators :resolved_input, :each, :[], :keys
68
- attr_reader :context
86
+ def_delegators :value, :each, :[], :keys
87
+ attr_reader :factory
69
88
 
70
- def initialize(context)
71
- @context = context
89
+ def initialize(factory)
90
+ @factory = factory
72
91
  end
73
92
 
74
- def resolved_input
75
- context.resolved_input
93
+ def value
94
+ @factory.resolved_input
76
95
  end
77
96
  end
78
97
  end