openapi3_parser 0.5.2 → 0.6.0

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