openapi3_parser 0.6.1 → 0.9.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 (71) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +23 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +18 -3
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +41 -0
  7. data/README.md +7 -4
  8. data/TODO.md +1 -1
  9. data/lib/openapi3_parser.rb +2 -3
  10. data/lib/openapi3_parser/array_sentence.rb +2 -1
  11. data/lib/openapi3_parser/document.rb +3 -2
  12. data/lib/openapi3_parser/error.rb +11 -1
  13. data/lib/openapi3_parser/node/array.rb +9 -1
  14. data/lib/openapi3_parser/node/context.rb +34 -4
  15. data/lib/openapi3_parser/node/map.rb +17 -1
  16. data/lib/openapi3_parser/node/object.rb +17 -0
  17. data/lib/openapi3_parser/node/operation.rb +8 -0
  18. data/lib/openapi3_parser/node/path_item.rb +8 -0
  19. data/lib/openapi3_parser/node/placeholder.rb +16 -12
  20. data/lib/openapi3_parser/node/schema.rb +50 -3
  21. data/lib/openapi3_parser/node_factory.rb +1 -1
  22. data/lib/openapi3_parser/node_factory/array.rb +34 -26
  23. data/lib/openapi3_parser/node_factory/context.rb +9 -2
  24. data/lib/openapi3_parser/node_factory/discriminator.rb +2 -0
  25. data/lib/openapi3_parser/node_factory/field.rb +2 -0
  26. data/lib/openapi3_parser/node_factory/fields/reference.rb +1 -0
  27. data/lib/openapi3_parser/node_factory/map.rb +12 -10
  28. data/lib/openapi3_parser/node_factory/media_type.rb +1 -0
  29. data/lib/openapi3_parser/node_factory/object.rb +4 -1
  30. data/lib/openapi3_parser/node_factory/object_factory/dsl.rb +7 -5
  31. data/lib/openapi3_parser/node_factory/object_factory/field_config.rb +9 -7
  32. data/lib/openapi3_parser/node_factory/object_factory/node_builder.rb +12 -13
  33. data/lib/openapi3_parser/node_factory/object_factory/validator.rb +25 -21
  34. data/lib/openapi3_parser/node_factory/openapi.rb +2 -0
  35. data/lib/openapi3_parser/node_factory/operation.rb +9 -0
  36. data/lib/openapi3_parser/node_factory/parameter.rb +2 -0
  37. data/lib/openapi3_parser/node_factory/parameter_like.rb +2 -1
  38. data/lib/openapi3_parser/node_factory/path_item.rb +27 -5
  39. data/lib/openapi3_parser/node_factory/paths.rb +1 -1
  40. data/lib/openapi3_parser/node_factory/reference.rb +9 -1
  41. data/lib/openapi3_parser/node_factory/request_body.rb +2 -4
  42. data/lib/openapi3_parser/node_factory/response.rb +1 -0
  43. data/lib/openapi3_parser/node_factory/responses.rb +1 -1
  44. data/lib/openapi3_parser/node_factory/schema.rb +5 -2
  45. data/lib/openapi3_parser/node_factory/server_variable.rb +1 -0
  46. data/lib/openapi3_parser/node_factory/type_checker.rb +6 -0
  47. data/lib/openapi3_parser/source.rb +2 -0
  48. data/lib/openapi3_parser/source/location.rb +6 -0
  49. data/lib/openapi3_parser/source/pointer.rb +9 -4
  50. data/lib/openapi3_parser/source_input.rb +9 -9
  51. data/lib/openapi3_parser/source_input/file.rb +3 -1
  52. data/lib/openapi3_parser/source_input/raw.rb +4 -1
  53. data/lib/openapi3_parser/source_input/resolve_next.rb +1 -0
  54. data/lib/openapi3_parser/source_input/string_parser.rb +3 -2
  55. data/lib/openapi3_parser/source_input/url.rb +3 -1
  56. data/lib/openapi3_parser/validation/error.rb +2 -0
  57. data/lib/openapi3_parser/validation/error_collection.rb +1 -1
  58. data/lib/openapi3_parser/validation/input_validator.rb +1 -1
  59. data/lib/openapi3_parser/validators/component_keys.rb +1 -1
  60. data/lib/openapi3_parser/validators/duplicate_parameters.rb +1 -0
  61. data/lib/openapi3_parser/validators/email.rb +3 -3
  62. data/lib/openapi3_parser/validators/media_type.rb +1 -1
  63. data/lib/openapi3_parser/validators/mutually_exclusive_fields.rb +8 -8
  64. data/lib/openapi3_parser/validators/reference.rb +2 -0
  65. data/lib/openapi3_parser/validators/required_fields.rb +2 -2
  66. data/lib/openapi3_parser/validators/unexpected_fields.rb +3 -2
  67. data/lib/openapi3_parser/validators/url.rb +2 -7
  68. data/lib/openapi3_parser/version.rb +1 -1
  69. data/openapi3_parser.gemspec +12 -8
  70. metadata +81 -25
  71. data/.travis.yml +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ca568bd4f56414a9a7467ba929750fe26ebe2ff6
4
- data.tar.gz: 3e0a2532c02e114140e3ea53458e637308b1ab53
2
+ SHA256:
3
+ metadata.gz: 81001258de6af8c21c8a7db87520d9433e3acb878483a56a616fdcd804e92848
4
+ data.tar.gz: 496f3a02ebcb386e949eab7ec6607f360a0b2e755be6bb4f1df3cd6b260a6e0d
5
5
  SHA512:
6
- metadata.gz: 916678f780b8d71fd83c09a64fc6725e678dd8356a137d4b7295c7c4ca185d824c4803bae74c46de755faa5c52736ddcbada93fc0c88ef12d8b2e0e583edc56a
7
- data.tar.gz: effde9d9458daf6d68bc5c9d79d137c32d583246f620f039b4606f1cf678d6f10bea2553a23b0930b2b73ecb0a665a66c9f5ac227e15956629095746363d634e
6
+ metadata.gz: d789d4ffe423f8854f961e902ffa6ef56b86d7dbcf506292106afa8548b72f2fbe968d98358ea7c80771a3c2e974c302096d43968c0663bdad40dc5f60a676e2
7
+ data.tar.gz: aea92fa77fbb4808872093b9149f8eb933f70a38336a0e415fa1fc9d4c15e691b8307abb0d70c0e51eee8eca89887cbc1a16b21e70f2d1812ae0fd77b75fe2a3
@@ -0,0 +1,23 @@
1
+ on: [push, pull_request]
2
+ jobs:
3
+ test:
4
+ strategy:
5
+ matrix:
6
+ ruby: [2.5, 2.6, 2.7, 3.0]
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - uses: ruby/setup-ruby@v1
11
+ with:
12
+ bundler-cache: true
13
+ ruby-version: ${{ matrix.ruby }}
14
+ - name: Test Ruby ${{ matrix.ruby }}
15
+ run: bundle exec rake
16
+ if: matrix.ruby != '2.5'
17
+ - name: Test and publish coverage for Ruby ${{ matrix.ruby }}
18
+ uses: paambaati/codeclimate-action@v2.7.5
19
+ env:
20
+ CC_TEST_REPORTER_ID: 8bc2d8e54331569aeb442094c21cb64a58d6efa0670f65ff00d9ae887f63c0b4
21
+ with:
22
+ coverageCommand: bundle exec rake
23
+ if: matrix.ruby == '2.5'
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  /pkg
3
3
  /.yardoc
4
4
  /doc
5
+ /coverage
data/.rubocop.yml CHANGED
@@ -1,15 +1,30 @@
1
+ require:
2
+ - rubocop-rake
3
+ - rubocop-rspec
4
+ AllCops:
5
+ NewCops: enable
1
6
  Style/StringLiterals:
2
7
  EnforcedStyle: double_quotes
3
8
  Metrics/MethodLength:
4
9
  Max: 30
5
10
  Metrics/AbcSize:
6
11
  Max: 30
7
- Documentation:
12
+ RSpec/ExampleLength:
13
+ Max: 30
14
+ Style/Documentation:
8
15
  Enabled: false
9
16
  Metrics/BlockLength:
10
17
  Exclude:
11
18
  - 'spec/**/*.rb'
12
- Naming/UncommunicativeMethodParamName:
19
+ - '*.gemspec'
20
+ RSpec/DescribeClass:
21
+ Exclude:
22
+ - 'spec/integration/**/*.rb'
23
+ # I'd rather have multiple expectations than lots of duplicate tests
24
+ RSpec/MultipleExpectations:
25
+ Enabled: false
26
+ # The default arbitrary number (5) is a little painful
27
+ RSpec/MultipleMemoizedHelpers:
13
28
  Enabled: false
14
- Naming/UncommunicativeBlockParamName:
29
+ RSpec/MessageSpies:
15
30
  Enabled: false
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.4.6
1
+ 2.5.8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,44 @@
1
+ # 0.9.0
2
+
3
+ - Fix unexpected fields error when using Example nodes inside a Parameter -
4
+ https://github.com/kevindew/openapi3_parser/pull/19
5
+ - Drop support for Ruby 2.4
6
+
7
+ # 0.8.2
8
+
9
+ - Fix bug falling into recursive loop on Array/Map nodes -
10
+ https://github.com/kevindew/openapi3_parser/issues/13
11
+
12
+ # 0.8.1
13
+
14
+ - Fix incorrectly spelt method name on `Schema` s/disciminator/discriminator/g
15
+
16
+ # 0.8.0
17
+
18
+ - Resolve deprecation warnings for Ruby 2.7
19
+ - Resolve deprecation warnings for Psych
20
+ - Operation and Path Item objects use servers that have cascaded from parent
21
+ objects if they do not have their own servers defined.
22
+ - Add `#relative_node` and `#parent_node` methods to `Node::Context`.
23
+ - Default to a server of "/" when given an empty or null servers input for
24
+ OpenAPI node.
25
+ - Set referenced data as input and source location for a PathItem with only
26
+ a $ref value.
27
+ - Fix data being lost in PathItem reference merges.
28
+
29
+ # 0.7.0
30
+
31
+ - Add `#values` method to `Node::Object` and `Node#Map` to have a method that
32
+ pairs with `#keys`
33
+ - Add `Node::Schema#requires?` method to simplify checking whether a property
34
+ is required by a particular schema.
35
+ - Add `#==` methods to Node objects. This allows checking whether two nodes
36
+ are from the same source location even if they're referenced in different
37
+ places.
38
+ - Add `Node::Schema#name` method that looks up the name of a Schema based
39
+ on it's contextual position in a document. Allows accessing the `Pet` value
40
+ from `#/components/schemas/Pet`.
41
+
1
42
  # 0.6.1
2
43
 
3
44
  - Fix bug where Node::Object and Node::Map iterated arrays rather than hashes
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OpenAPI 3 Parser
2
2
 
3
- [![Build Status](https://travis-ci.org/kevindew/openapi3_parser.svg?branch=master)](https://travis-ci.org/kevindew/openapi3_parser)
3
+ [![Build Status](https://travis-ci.org/kevindew/openapi3_parser.svg?branch=main)](https://travis-ci.org/kevindew/openapi3_parser)
4
4
 
5
5
  This a Ruby based parser/validator for [OpenAPI 3][openapi-3]. It is used to
6
6
  convert an OpenAPI file (can be a local file, a URL, a string or even a Ruby
@@ -33,10 +33,13 @@ being:
33
33
  - Documentation for the API to navigate the OpenAPI nodes is available on
34
34
  [rubydoc.info][docs].
35
35
 
36
+ I've wrote a blog post reflecting on the decisions involved in building this
37
+ parser in [How to write an OpenAPI 3 parser][blog].
36
38
 
37
39
  [openapi-3]: https://github.com/OAI/OpenAPI-Specification
38
40
  [openapi-3-spec]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification
39
41
  [docs]: http://www.rubydoc.info/github/kevindew/openapi3_parser/Openapi3Parser/Node/Openapi
42
+ [blog]: https://kevindew.me/post/188611423231/how-to-write-an-openapi-3-parser
40
43
 
41
44
  ## Usage
42
45
 
@@ -44,7 +47,7 @@ being:
44
47
 
45
48
  ```ruby
46
49
  # by URL
47
- Openapi3Parser.load_url("https://raw.githubusercontent.com/kevindew/openapi3_parser/master/spec/support/examples/petstore-expanded.yaml")
50
+ Openapi3Parser.load_url("https://raw.githubusercontent.com/kevindew/openapi3_parser/main/spec/support/examples/petstore-expanded.yaml")
48
51
 
49
52
  # by path to file
50
53
  Openapi3Parser.load_file("spec/support/examples/uber.yaml")
@@ -73,7 +76,7 @@ document.errors
73
76
  ### Traversing
74
77
 
75
78
  ```ruby
76
- document = Openapi3Parser.load_url("https://raw.githubusercontent.com/kevindew/openapi3_parser/master/spec/support/examples/petstore-expanded.yaml")
79
+ document = Openapi3Parser.load_url("https://raw.githubusercontent.com/kevindew/openapi3_parser/main/spec/support/examples/petstore-expanded.yaml")
77
80
 
78
81
  # by objects
79
82
 
@@ -118,7 +121,7 @@ You can install this gem into your bundler application by adding this line to
118
121
  your Gemfile:
119
122
 
120
123
  ```
121
- gem "openapi3_parser", "~> 0.6.0"
124
+ gem "openapi3_parser", "~> 0.8.0"
122
125
  ```
123
126
 
124
127
  and then running `$ bundle install`
data/TODO.md CHANGED
@@ -32,7 +32,7 @@ These are the steps defined to reach 1.0. Assistance is very welcome.
32
32
  - [x] Ensure Array and Map nodes return empty ones by default rather than nil
33
33
  - [ ] Make JSON pointer public access to be consistent accepting string, array
34
34
  or (potentially) a pointer class
35
- - [ ] Support creating a default Server object on servers property of OpenAPI
35
+ - [x] Support creating a default Server object on servers property of OpenAPI
36
36
  Node
37
37
  - [ ] Support relative URLs being able to be relative the first server object
38
38
  see: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#relative-references-in-urls
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Dir.glob(File.join(__dir__, "openapi3_parser", "**", "*.rb")).each do |file|
4
- require file
5
- end
3
+ files = Dir.glob(File.join(__dir__, "openapi3_parser", "**", "*.rb")).sort
4
+ files.each { |file| require file }
6
5
 
7
6
  module Openapi3Parser
8
7
  # For a variety of inputs this will construct an OpenAPI document. For a
@@ -5,7 +5,8 @@ module Openapi3Parser
5
5
  refine ::Array do
6
6
  def sentence_join
7
7
  return join if count < 2
8
- self[0..-2].join(", ") + " and " + self[-1]
8
+
9
+ "#{self[0..-2].join(', ')} and #{self[-1]}"
9
10
  end
10
11
  end
11
12
  end
@@ -60,7 +60,7 @@ module Openapi3Parser
60
60
  # @!method external_docs
61
61
  # The value of the external_docs field on the OpenAPI document
62
62
  # @see Node::Openapi#external_docs
63
- # @return [Node::ExternalDocumentation]
63
+ # @return [Node::ExternalDocumentation, nil]
64
64
  # @!method extension
65
65
  # Look up an extension field provided for the root object of the document
66
66
  # @see Node::Object#extension
@@ -138,7 +138,7 @@ module Openapi3Parser
138
138
  look_up_pointer(pointer, relative_to, factory.resolved_input)
139
139
  end
140
140
 
141
- # Look up a node at a particular location in the OpenAPI docuemnt
141
+ # Look up a node at a particular location in the OpenAPI document
142
142
  #
143
143
  # Examples:
144
144
  #
@@ -174,6 +174,7 @@ module Openapi3Parser
174
174
 
175
175
  def build
176
176
  return if build_in_progress || built
177
+
177
178
  @build_in_progress = true
178
179
  context = NodeFactory::Context.root(root_source.data, root_source)
179
180
  @factory = NodeFactory::Openapi.new(context)
@@ -7,32 +7,42 @@ module Openapi3Parser
7
7
  # at runtime when we have tried to access that resource it is not available
8
8
  # for whatever reason.
9
9
  class InaccessibleInput < Error; end
10
+
10
11
  # Raised in cases where we provided data that we expected to be parsable
11
12
  # (such as a string of JSON data) but when we tried to parse it an error
12
13
  # is raised
13
14
  class UnparsableInput < Error; end
15
+
14
16
  # Raised in cases where an object that is in an immutable state is modified
15
17
  #
16
18
  # Typically this would occur when a component that is frozen is modififed.
17
19
  # Some components are mutable during the construction of a document and
18
20
  # then frozen afterwards.
19
21
  class ImmutableObject < Error; end
20
- # Raised when a type that is not a whitelist of valid types is used
22
+
23
+ # Raised when a node is provided data as a type that is outside the allowed
24
+ # list
21
25
  class InvalidType < Error; end
26
+
22
27
  # Raised when we have to abort creating an object due to invalid data
23
28
  class InvalidData < Error; end
29
+
24
30
  # Used when there are fields that are missing from an object which prevents
25
31
  # us from creating a node
26
32
  class MissingFields < Error; end
33
+
27
34
  # Used when there are extra fields that are not expected in the data for
28
35
  # a node
29
36
  class UnexpectedFields < Error; end
37
+
30
38
  # Used when a method we expect to be able to call (through symbol or proc)
31
39
  # is not callable
32
40
  class NotCallable < Error; end
41
+
33
42
  # Raised when we in a recursive data structure and can't perform an
34
43
  # operation
35
44
  class InRecursiveStructure < Error; end
45
+
36
46
  # Used when we're trying to validate that a type is something that is not
37
47
  # validatable, most likely a sign that we're in a bug
38
48
  class UnvalidatableType < Error; end
@@ -14,7 +14,7 @@ module Openapi3Parser
14
14
  extend Forwardable
15
15
  include Enumerable
16
16
 
17
- def_delegators :node_data, :empty?
17
+ def_delegators :node_data, :empty?, :length, :size
18
18
  attr_reader :node_data, :node_context
19
19
 
20
20
  # @param [::Array] data data used to populate this node
@@ -35,6 +35,14 @@ module Openapi3Parser
35
35
  Placeholder.each(node_data, &block)
36
36
  end
37
37
 
38
+ # @param [Any] other
39
+ #
40
+ # @return [Boolean]
41
+ def ==(other)
42
+ other.instance_of?(self.class) &&
43
+ node_context.same_data_and_source?(other.node_context)
44
+ end
45
+
38
46
  # Used to access a node relative to this node
39
47
  # @param [Source::Pointer, ::Array, ::String] pointer_like
40
48
  # @return anything
@@ -65,10 +65,19 @@ module Openapi3Parser
65
65
  @source_location = source_location
66
66
  end
67
67
 
68
+ # @param [Context] other
68
69
  # @return [Boolean]
69
70
  def ==(other)
71
+ document_location == other.document_location &&
72
+ same_data_and_source?(other)
73
+ end
74
+
75
+ # Check that contexts are the same without concern for document location
76
+ #
77
+ # @param [Context] other
78
+ # @return [Boolean]
79
+ def same_data_and_source?(other)
70
80
  input == other.input &&
71
- document_location == other.document_location &&
72
81
  source_location == other.source_location
73
82
  end
74
83
 
@@ -98,9 +107,7 @@ module Openapi3Parser
98
107
  def location_summary
99
108
  summary = document_location.to_s
100
109
 
101
- if document_location != source_location
102
- summary += " (#{source_location})"
103
- end
110
+ summary += " (#{source_location})" if document_location != source_location
104
111
 
105
112
  summary
106
113
  end
@@ -124,6 +131,29 @@ module Openapi3Parser
124
131
  def node
125
132
  document.node_at(document_location.pointer)
126
133
  end
134
+
135
+ # Look up a node at a particular location in the OpenAPI docuemnt based
136
+ # on the relative position in the document of this context
137
+ #
138
+ # Examples:
139
+ #
140
+ # context.relative_node("#schemas")
141
+ # context.relative_node(%w[..])
142
+ #
143
+ # @param [Source::Pointer, String, Array] pointer
144
+ # @return anything
145
+ def relative_node(pointer)
146
+ document.node_at(pointer, document_location.pointer)
147
+ end
148
+
149
+ # Return the node that is the parent node for the node at this context
150
+ #
151
+ # @return [Node::Object, Node::Map, Node::Array, nil]
152
+ def parent_node
153
+ return if document_location.root?
154
+
155
+ relative_node("#..")
156
+ end
127
157
  end
128
158
  end
129
159
  end
@@ -8,7 +8,7 @@ module Openapi3Parser
8
8
  extend Forwardable
9
9
  include Enumerable
10
10
 
11
- def_delegators :node_data, :keys, :empty?
11
+ def_delegators :node_data, :keys, :empty?, :length, :size
12
12
  attr_reader :node_data, :node_context
13
13
 
14
14
  def initialize(data, context)
@@ -48,6 +48,14 @@ module Openapi3Parser
48
48
  self["x-#{value}"]
49
49
  end
50
50
 
51
+ # @param [Any] other
52
+ #
53
+ # @return [Boolean]
54
+ def ==(other)
55
+ other.instance_of?(self.class) &&
56
+ node_context.same_data_and_source?(other.node_context)
57
+ end
58
+
51
59
  # Iterates through the data of this node, used by Enumerable
52
60
  #
53
61
  # @return [Object]
@@ -55,6 +63,14 @@ module Openapi3Parser
55
63
  Placeholder.each(node_data, &block)
56
64
  end
57
65
 
66
+ # Provide an array of values for this object, a partner to the #keys
67
+ # method
68
+ #
69
+ # @return [Array]
70
+ def values
71
+ map(&:last)
72
+ end
73
+
58
74
  # Used to access a node relative to this node
59
75
  # @param [Source::Pointer, ::Array, ::String] pointer_like
60
76
  # @return anything
@@ -48,6 +48,14 @@ module Openapi3Parser
48
48
  self["x-#{value}"]
49
49
  end
50
50
 
51
+ # @param [Any] other
52
+ #
53
+ # @return [Boolean]
54
+ def ==(other)
55
+ other.instance_of?(self.class) &&
56
+ node_context.same_data_and_source?(other.node_context)
57
+ end
58
+
51
59
  # Iterates through the data of this node, used by Enumerable
52
60
  #
53
61
  # @return [Object]
@@ -55,11 +63,20 @@ module Openapi3Parser
55
63
  Placeholder.each(node_data, &block)
56
64
  end
57
65
 
66
+ # Provide an array of values for this object, a partner to the #keys
67
+ # method
68
+ #
69
+ # @return [Array]
70
+ def values
71
+ map(&:last)
72
+ end
73
+
58
74
  # Used to render fields that can be in markdown syntax into HTML
59
75
  # @param [String, nil] value
60
76
  # @return [String, nil]
61
77
  def render_markdown(value)
62
78
  return if value.nil?
79
+
63
80
  Markdown.to_html(value)
64
81
  end
65
82
 
@@ -70,6 +70,14 @@ module Openapi3Parser
70
70
  def servers
71
71
  self["servers"]
72
72
  end
73
+
74
+ # Whether this object uses it's own defined servers instead of falling
75
+ # back to the path items' ones.
76
+ #
77
+ # @return [Boolean]
78
+ def alternative_servers?
79
+ servers != node_context.parent_node.servers
80
+ end
73
81
  end
74
82
  end
75
83
  end