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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dc74c9c6170cb4f0bd73f5debc10b7a53317c3f4
4
- data.tar.gz: ffd75a8d5d5ff4d1f40493331645db2785810eb6
3
+ metadata.gz: c9c582d931f4f1353ee60c24d7197af410b6a0fe
4
+ data.tar.gz: 99f57e7b4ed9c359a769dbaff78a82bdeac13e55
5
5
  SHA512:
6
- metadata.gz: a9c5641522434bb630c27679dcf7a9a93ab3c538e7cbc45597e84f72ba8cdb3ed7d6477cdeee3623becda92a02fa118073e7dcac61780a5d6bab112a763fd75f
7
- data.tar.gz: 07b6ad8941ab2a02b210a25f278287cd7d29f3025b529d540e9fd5ff973e6d51938e2618b085eb3970e79757f21dca9629d6bd4c472f77f29b24493b82c6cb85
6
+ metadata.gz: 880d452e266f6dae6c14083ce278f21f469843e2952e0df3f3a5a11222fe0c9bb2042a2d5a0d7209eab83249556fb01f708bf2ffa624ac7838270199efaeb8a6
7
+ data.tar.gz: 0c4980179959a775d12d913dec31896307e9712ef2ea3585eeba71f2e9fd6bd1ebbaafa90c0b00b1e5fd61967b83131f7e2773a9a7bd0e481e47b5c4922343c2
@@ -1 +1 @@
1
- 2.3.1
1
+ 2.4.6
@@ -1,9 +1,9 @@
1
1
  language: ruby
2
2
  sudo: false
3
3
  rvm:
4
- - 2.3.7
5
- - 2.4.4
6
- - 2.5.1
4
+ - 2.4.6
5
+ - 2.5.5
6
+ - 2.6.4
7
7
  - ruby-head
8
8
  matrix:
9
9
  allow_failures:
@@ -1,3 +1,10 @@
1
+ # 0.6.0
2
+
3
+ - Drop support for Ruby 2.3
4
+ - Re-use references for significantly faster initialisation and validation
5
+ - Only error when accessing an invalid node rather than at root
6
+ - Handle infinitely recursive references that never resolve
7
+
1
8
  # 0.5.2
2
9
 
3
10
  - Fix outputting warnings for cyclic dependencies and undefined variables -
@@ -34,4 +41,3 @@
34
41
  - Allow defaulting to empty arrays and maps
35
42
  - Configure rubydoc
36
43
  - Types returned documented for the nodes
37
-
data/README.md CHANGED
@@ -2,47 +2,134 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/kevindew/openapi3_parser.svg?branch=master)](https://travis-ci.org/kevindew/openapi3_parser)
4
4
 
5
+ This a Ruby based parser/validator for [OpenAPI 3][openapi-3]. It is used to
6
+ convert an OpenAPI file (can be a local file, a URL, a string or even a Ruby
7
+ hash) into an object graph with a simple API that follows the [OpenAPI
8
+ specification][openapi-3-spec].
5
9
 
6
- This is a parser/validator for [Open API 3][openapi-3] built in Ruby.
10
+ Basic example:
7
11
 
8
- Example usage:
9
-
10
- ```
12
+ ```ruby
11
13
  require "openapi3_parser"
12
14
 
13
- document = Openapi3Parser.load_file("path/to/example.yaml")
15
+ document = Openapi3Parser.load_url("https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml")
14
16
 
15
- # check whether document is valid
16
- document.valid?
17
-
18
- # traverse document
19
- document.paths["/"]
17
+ document.paths["/pets"].get.summary
18
+ # => "List all pets"
20
19
  ```
21
20
 
22
- Documentation for the API to navigate the OpenAPI nodes is available on
23
- [rubydoc.info][docs].
21
+ It aims to support 100% of the OpenAPI 3.0 specification, with key features
22
+ being:
23
+
24
+ - Supports loading a specification by path to a file, URL, Ruby file objects,
25
+ and strings in YAML and JSON formats, it even supports loading via a Ruby hash;
26
+ - Support for loading references from external files including URLs;
27
+ - Handles recursive references;
28
+ - All of OpenAPI specification mapped to Ruby objects, providing a natural
29
+ Ruby interface that maps clearly to the specification;
30
+ - OpenAPI files validated with a simple API to quickly and simply see all
31
+ problems with a file
32
+ - Built-in Markdown to HTML conversion;
33
+ - Documentation for the API to navigate the OpenAPI nodes is available on
34
+ [rubydoc.info][docs].
35
+
24
36
 
25
37
  [openapi-3]: https://github.com/OAI/OpenAPI-Specification
38
+ [openapi-3-spec]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification
26
39
  [docs]: http://www.rubydoc.info/github/kevindew/openapi3_parser/Openapi3Parser/Node/Openapi
27
40
 
41
+ ## Usage
42
+
43
+ ### Loading a specification
44
+
45
+ ```ruby
46
+ # by URL
47
+ Openapi3Parser.load_url("https://raw.githubusercontent.com/kevindew/openapi3_parser/master/spec/support/examples/petstore-expanded.yaml")
48
+
49
+ # by path to file
50
+ Openapi3Parser.load_file("spec/support/examples/uber.yaml")
51
+
52
+ # by File
53
+ Openapi3Parser.load(File.open("spec/support/examples/uber.yaml"))
54
+
55
+ # by String
56
+ Openapi3Parser.load('{ "openapi": "3.0.0", "info": { "title": "API", "version": "1.0.0" }, "paths": {} }')
57
+
58
+ # by Hash
59
+ Openapi3Parser.load(openapi: "3.0.0", info: { title: "API", version: "1.0.0" }, paths: {})
60
+
61
+ ```
62
+
63
+ ### Validating
64
+
65
+ ```ruby
66
+ document = Openapi3Parser.load(openapi: "3.0.0", info: {}, paths: {})
67
+ document.valid?
68
+ # => false
69
+ document.errors
70
+ # => Openapi3Parser::Validation::ErrorCollection(errors: {"#/info"=>["Missing required fields: title and version"]})
71
+ ```
72
+
73
+ ### Traversing
74
+
75
+ ```ruby
76
+ document = Openapi3Parser.load_url("https://raw.githubusercontent.com/kevindew/openapi3_parser/master/spec/support/examples/petstore-expanded.yaml")
77
+
78
+ # by objects
79
+
80
+ document.info.terms_of_service
81
+ # => "http://swagger.io/terms/"
82
+
83
+ document.paths.keys
84
+ # => ["/pets", "/pets/{id}"]
85
+
86
+ document.paths["/pets"].get.parameters.map(&:name)
87
+ # => ["tags", "limit"]
88
+
89
+ # by hash syntax
90
+
91
+ document["info"]["termsOfService"]
92
+ => "http://swagger.io/terms/"
93
+
94
+ document["paths"].keys
95
+ # => ["/pets", "/pets/{id}"]
96
+
97
+ document["paths"]["/pets"]["get"]["parameters"].map(&:name)
98
+ # => ["tags", "limit"]
99
+
100
+ # by a path to a node
101
+ document.node_at("#/paths/%2Fpets/get/operationId")
102
+ => "findPets"
103
+
104
+ document.node_at("#/components/schemas/Pet/allOf/0/required/0")
105
+ => "name"
106
+
107
+ # or combining
108
+
109
+ document.components.schemas["Pet"].node_at("#../NewPet")
110
+ => Openapi3Parser::Node::Schema(#/components/schemas/NewPet)
111
+ ```
112
+
113
+ You can learn more about the API on [rubydoc.info][docs]
114
+
28
115
  ## Installation
29
116
 
30
117
  You can install this gem into your bundler application by adding this line to
31
118
  your Gemfile:
32
119
 
33
120
  ```
34
- gem "openapi3_parser", "~> 0.5.0"
121
+ gem "openapi3_parser", "~> 0.6.0"
35
122
  ```
36
123
 
37
124
  and then running `$ bundle install`
38
125
 
39
- Or install the gem onto your machine via ` $ gem install openapi3_parser`
126
+ Or install the gem onto your machine via `$ gem install openapi3_parser`
40
127
 
41
128
  ## Status
42
129
 
43
130
  This is currently a work in progress and will remain so until it reaches 1.0.
44
131
 
45
- See [TODO](TODO.md) for details of the roadmap there.
132
+ See [TODO](TODO.md) for details of the features still to implement.
46
133
 
47
134
  ## Licence
48
135
 
@@ -80,8 +80,8 @@ module Openapi3Parser
80
80
 
81
81
  # @param [SourceInput] source_input
82
82
  def initialize(source_input)
83
- @reference_register = ReferenceRegister.new
84
- @root_source = Source.new(source_input, self, reference_register)
83
+ @reference_registry = ReferenceRegistry.new
84
+ @root_source = Source.new(source_input, self, reference_registry)
85
85
  @warnings = []
86
86
  @openapi_version = determine_openapi_version(root_source.data["openapi"])
87
87
  @build_in_progress = false
@@ -90,7 +90,7 @@ module Openapi3Parser
90
90
 
91
91
  # @return [Node::Openapi]
92
92
  def root
93
- factory.node
93
+ @root ||= factory.node(Node::Context.root(factory.context))
94
94
  end
95
95
 
96
96
  # All the additional sources that have been referenced as part of loading
@@ -99,7 +99,7 @@ module Openapi3Parser
99
99
  # @return [Array<Source>]
100
100
  def reference_sources
101
101
  build unless built
102
- reference_register.sources
102
+ reference_registry.sources.reject(&:root?)
103
103
  end
104
104
 
105
105
  # All of the sources involved in this OpenAPI document
@@ -131,8 +131,8 @@ module Openapi3Parser
131
131
  # resolved_input refers to the input with references resolevd and all
132
132
  # optional fields existing
133
133
  #
134
- # @param [Context::Pointer, String, Array] pointer
135
- # @param [Context::Pointer, String, Array, nil] relative_to
134
+ # @param [Source::Pointer, String, Array] pointer
135
+ # @param [Source::Pointer, String, Array, nil] relative_to
136
136
  # @return anything
137
137
  def resolved_input_at(pointer, relative_to = nil)
138
138
  look_up_pointer(pointer, relative_to, factory.resolved_input)
@@ -145,8 +145,8 @@ module Openapi3Parser
145
145
  # document.node_at("#/components/schemas")
146
146
  # document.node_at(%w[components schemas])
147
147
  #
148
- # @param [Context::Pointer, String, Array] pointer
149
- # @param [Context::Pointer, String, Array, nil] relative_to
148
+ # @param [Source::Pointer, String, Array] pointer
149
+ # @param [Source::Pointer, String, Array, nil] relative_to
150
150
  # @return anything
151
151
  def node_at(pointer, relative_to = nil)
152
152
  look_up_pointer(pointer, relative_to, root)
@@ -160,11 +160,11 @@ module Openapi3Parser
160
160
 
161
161
  private
162
162
 
163
- attr_reader :reference_register, :built, :build_in_progress
163
+ attr_reader :reference_registry, :built, :build_in_progress
164
164
 
165
165
  def look_up_pointer(pointer, relative_pointer, subject)
166
- merged_pointer = Context::Pointer.merge_pointers(relative_pointer,
167
- pointer)
166
+ merged_pointer = Source::Pointer.merge_pointers(relative_pointer,
167
+ pointer)
168
168
  CautiousDig.call(subject, *merged_pointer.segments)
169
169
  end
170
170
 
@@ -175,9 +175,9 @@ module Openapi3Parser
175
175
  def build
176
176
  return if build_in_progress || built
177
177
  @build_in_progress = true
178
- context = Context.root(root_source.data, root_source)
178
+ context = NodeFactory::Context.root(root_source.data, root_source)
179
179
  @factory = NodeFactory::Openapi.new(context)
180
- reference_register.freeze
180
+ reference_registry.freeze
181
181
  @warnings.freeze
182
182
  @build_in_progress = false
183
183
  @built = true
@@ -210,7 +210,7 @@ module Openapi3Parser
210
210
 
211
211
  def reference_factories
212
212
  build unless built
213
- reference_register.factories
213
+ reference_registry.factories.reject { |f| f.context.source.root? }
214
214
  end
215
215
  end
216
216
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Openapi3Parser
4
+ class Document
5
+ class ReferenceRegistry
6
+ attr_reader :sources
7
+
8
+ def initialize
9
+ @sources = []
10
+ @factories_by_type = {}
11
+ end
12
+
13
+ def freeze
14
+ sources.freeze
15
+ factories_by_type.freeze.each(&:freeze)
16
+ super
17
+ end
18
+
19
+ def factories
20
+ factories_by_type.values.flatten
21
+ end
22
+
23
+ def register(unbuilt_factory, source_location, reference_factory_context)
24
+ register_source(source_location.source)
25
+ object_type = unbuilt_factory.object_type
26
+ existing_factory = factory(object_type, source_location)
27
+
28
+ return existing_factory if existing_factory
29
+
30
+ build_factory(
31
+ unbuilt_factory,
32
+ source_location,
33
+ reference_factory_context
34
+ ).tap { |f| register_factory(object_type, f) }
35
+ end
36
+
37
+ def factory(object_type, source_location)
38
+ factories_by_type[object_type]&.find do |f|
39
+ f.context.source_location == source_location
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :factories_by_type
46
+
47
+ def register_source(source)
48
+ sources << source unless sources.include?(source)
49
+ end
50
+
51
+ def register_factory(object_type, factory)
52
+ factories_by_type[object_type] ||= []
53
+ factories_by_type[object_type] << factory
54
+ end
55
+
56
+ def build_factory(unbuilt_factory,
57
+ source_location,
58
+ reference_factory_context)
59
+ next_context = NodeFactory::Context.resolved_reference(
60
+ reference_factory_context,
61
+ source_location: source_location
62
+ )
63
+
64
+ if unbuilt_factory.is_a?(Class)
65
+ unbuilt_factory.new(next_context)
66
+ else
67
+ unbuilt_factory.call(next_context)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -14,7 +14,7 @@ module Openapi3Parser
14
14
  extend Forwardable
15
15
  include Enumerable
16
16
 
17
- def_delegators :node_data, :each, :[], :empty?
17
+ def_delegators :node_data, :empty?
18
18
  attr_reader :node_data, :node_context
19
19
 
20
20
  # @param [::Array] data data used to populate this node
@@ -24,8 +24,16 @@ module Openapi3Parser
24
24
  @node_context = context
25
25
  end
26
26
 
27
+ def [](index)
28
+ Placeholder.resolve(node_data[index])
29
+ end
30
+
31
+ def each
32
+ node_data.each_index { |index| yield(self[index]) }
33
+ end
34
+
27
35
  # Used to access a node relative to this node
28
- # @param [Context::Pointer, ::Array, ::String] pointer_like
36
+ # @param [Source::Pointer, ::Array, ::String] pointer_like
29
37
  # @return anything
30
38
  def node_at(pointer_like)
31
39
  current_pointer = node_context.document_location.pointer
@@ -8,47 +8,47 @@ module Openapi3Parser
8
8
  class Components < Node::Object
9
9
  # @return [Map<String, Schema>]
10
10
  def schemas
11
- node_data["schemas"]
11
+ self["schemas"]
12
12
  end
13
13
 
14
14
  # @return [Map<String, Response>]
15
15
  def responses
16
- node_data["responses"]
16
+ self["responses"]
17
17
  end
18
18
 
19
19
  # @return [Map<String, Parameter>]
20
20
  def parameters
21
- node_data["parameters"]
21
+ self["parameters"]
22
22
  end
23
23
 
24
24
  # @return [Map<String, Example>]
25
25
  def examples
26
- node_data["examples"]
26
+ self["examples"]
27
27
  end
28
28
 
29
29
  # @return [Map<String, RequestBody>]
30
30
  def request_bodies
31
- node_data["requestBodies"]
31
+ self["requestBodies"]
32
32
  end
33
33
 
34
34
  # @return [Map<String, Header>]
35
35
  def headers
36
- node_data["headers"]
36
+ self["headers"]
37
37
  end
38
38
 
39
39
  # @return [Map<String, SecurityScheme>]
40
40
  def security_schemes
41
- node_data["securitySchemes"]
41
+ self["securitySchemes"]
42
42
  end
43
43
 
44
44
  # @return [Map<String, Link>]
45
45
  def links
46
- node_data["links"]
46
+ self["links"]
47
47
  end
48
48
 
49
49
  # @return [Map<String, Callback>]
50
50
  def callbacks
51
- node_data["callbacks"]
51
+ self["callbacks"]
52
52
  end
53
53
  end
54
54
  end
@@ -8,17 +8,17 @@ module Openapi3Parser
8
8
  class Contact < Node::Object
9
9
  # @return [String, nil]
10
10
  def name
11
- data["name"]
11
+ self["name"]
12
12
  end
13
13
 
14
14
  # @return [String, nil]
15
15
  def url
16
- data["url"]
16
+ self["url"]
17
17
  end
18
18
 
19
19
  # @return [String, nil]
20
20
  def email
21
- data["email"]
21
+ self["email"]
22
22
  end
23
23
  end
24
24
  end