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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +3 -3
- data/CHANGELOG.md +7 -1
- data/README.md +102 -15
- data/lib/openapi3_parser/document.rb +14 -14
- data/lib/openapi3_parser/document/reference_registry.rb +72 -0
- data/lib/openapi3_parser/node/array.rb +10 -2
- data/lib/openapi3_parser/node/components.rb +9 -9
- data/lib/openapi3_parser/node/contact.rb +3 -3
- data/lib/openapi3_parser/node/context.rb +129 -0
- data/lib/openapi3_parser/node/discriminator.rb +2 -2
- data/lib/openapi3_parser/node/encoding.rb +5 -5
- data/lib/openapi3_parser/node/example.rb +4 -4
- data/lib/openapi3_parser/node/external_documentation.rb +2 -2
- data/lib/openapi3_parser/node/info.rb +6 -6
- data/lib/openapi3_parser/node/license.rb +2 -2
- data/lib/openapi3_parser/node/link.rb +6 -6
- data/lib/openapi3_parser/node/map.rb +8 -4
- data/lib/openapi3_parser/node/media_type.rb +5 -5
- data/lib/openapi3_parser/node/oauth_flow.rb +4 -4
- data/lib/openapi3_parser/node/oauth_flows.rb +4 -4
- data/lib/openapi3_parser/node/object.rb +8 -4
- data/lib/openapi3_parser/node/openapi.rb +8 -8
- data/lib/openapi3_parser/node/operation.rb +12 -12
- data/lib/openapi3_parser/node/parameter.rb +2 -2
- data/lib/openapi3_parser/node/parameter_like.rb +11 -11
- data/lib/openapi3_parser/node/path_item.rb +12 -12
- data/lib/openapi3_parser/node/placeholder.rb +34 -0
- data/lib/openapi3_parser/node/request_body.rb +3 -3
- data/lib/openapi3_parser/node/response.rb +4 -4
- data/lib/openapi3_parser/node/responses.rb +1 -1
- data/lib/openapi3_parser/node/schema.rb +36 -36
- data/lib/openapi3_parser/node/security_scheme.rb +8 -8
- data/lib/openapi3_parser/node/server.rb +3 -3
- data/lib/openapi3_parser/node/server_variable.rb +3 -3
- data/lib/openapi3_parser/node/tag.rb +3 -3
- data/lib/openapi3_parser/node/xml.rb +5 -5
- data/lib/openapi3_parser/node_factory/array.rb +15 -13
- data/lib/openapi3_parser/node_factory/callback.rb +2 -2
- data/lib/openapi3_parser/node_factory/context.rb +111 -0
- data/lib/openapi3_parser/node_factory/field.rb +5 -7
- data/lib/openapi3_parser/node_factory/fields/reference.rb +43 -24
- data/lib/openapi3_parser/node_factory/link.rb +1 -1
- data/lib/openapi3_parser/node_factory/map.rb +14 -12
- data/lib/openapi3_parser/node_factory/object.rb +9 -5
- data/lib/openapi3_parser/node_factory/object_factory/node_builder.rb +21 -28
- data/lib/openapi3_parser/node_factory/optional_reference.rb +4 -0
- data/lib/openapi3_parser/node_factory/parameter_like.rb +0 -2
- data/lib/openapi3_parser/node_factory/path_item.rb +7 -4
- data/lib/openapi3_parser/node_factory/paths.rb +2 -2
- data/lib/openapi3_parser/node_factory/reference.rb +17 -10
- data/lib/openapi3_parser/node_factory/responses.rb +2 -2
- data/lib/openapi3_parser/node_factory/security_requirement.rb +2 -2
- data/lib/openapi3_parser/source.rb +27 -24
- data/lib/openapi3_parser/{context → source}/location.rb +13 -1
- data/lib/openapi3_parser/{context → source}/pointer.rb +2 -2
- data/lib/openapi3_parser/source/resolved_reference.rb +67 -0
- data/lib/openapi3_parser/validators/duplicate_parameters.rb +8 -4
- data/lib/openapi3_parser/validators/reference.rb +3 -3
- data/lib/openapi3_parser/version.rb +1 -1
- data/openapi3_parser.gemspec +1 -1
- metadata +11 -10
- data/lib/openapi3_parser/context.rb +0 -162
- data/lib/openapi3_parser/document/reference_register.rb +0 -48
- data/lib/openapi3_parser/node_factory/recursive_pointer.rb +0 -17
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9c582d931f4f1353ee60c24d7197af410b6a0fe
|
4
|
+
data.tar.gz: 99f57e7b4ed9c359a769dbaff78a82bdeac13e55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 880d452e266f6dae6c14083ce278f21f469843e2952e0df3f3a5a11222fe0c9bb2042a2d5a0d7209eab83249556fb01f708bf2ffa624ac7838270199efaeb8a6
|
7
|
+
data.tar.gz: 0c4980179959a775d12d913dec31896307e9712ef2ea3585eeba71f2e9fd6bd1ebbaafa90c0b00b1e5fd61967b83131f7e2773a9a7bd0e481e47b5c4922343c2
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.4.6
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -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
|
[](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
|
-
|
10
|
+
Basic example:
|
7
11
|
|
8
|
-
|
9
|
-
|
10
|
-
```
|
12
|
+
```ruby
|
11
13
|
require "openapi3_parser"
|
12
14
|
|
13
|
-
document = Openapi3Parser.
|
15
|
+
document = Openapi3Parser.load_url("https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml")
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
# traverse document
|
19
|
-
document.paths["/"]
|
17
|
+
document.paths["/pets"].get.summary
|
18
|
+
# => "List all pets"
|
20
19
|
```
|
21
20
|
|
22
|
-
|
23
|
-
|
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.
|
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
|
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
|
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
|
-
@
|
84
|
-
@root_source = Source.new(source_input, self,
|
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
|
-
|
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 [
|
135
|
-
# @param [
|
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 [
|
149
|
-
# @param [
|
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 :
|
163
|
+
attr_reader :reference_registry, :built, :build_in_progress
|
164
164
|
|
165
165
|
def look_up_pointer(pointer, relative_pointer, subject)
|
166
|
-
merged_pointer =
|
167
|
-
|
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
|
-
|
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
|
-
|
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, :
|
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 [
|
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
|
-
|
11
|
+
self["schemas"]
|
12
12
|
end
|
13
13
|
|
14
14
|
# @return [Map<String, Response>]
|
15
15
|
def responses
|
16
|
-
|
16
|
+
self["responses"]
|
17
17
|
end
|
18
18
|
|
19
19
|
# @return [Map<String, Parameter>]
|
20
20
|
def parameters
|
21
|
-
|
21
|
+
self["parameters"]
|
22
22
|
end
|
23
23
|
|
24
24
|
# @return [Map<String, Example>]
|
25
25
|
def examples
|
26
|
-
|
26
|
+
self["examples"]
|
27
27
|
end
|
28
28
|
|
29
29
|
# @return [Map<String, RequestBody>]
|
30
30
|
def request_bodies
|
31
|
-
|
31
|
+
self["requestBodies"]
|
32
32
|
end
|
33
33
|
|
34
34
|
# @return [Map<String, Header>]
|
35
35
|
def headers
|
36
|
-
|
36
|
+
self["headers"]
|
37
37
|
end
|
38
38
|
|
39
39
|
# @return [Map<String, SecurityScheme>]
|
40
40
|
def security_schemes
|
41
|
-
|
41
|
+
self["securitySchemes"]
|
42
42
|
end
|
43
43
|
|
44
44
|
# @return [Map<String, Link>]
|
45
45
|
def links
|
46
|
-
|
46
|
+
self["links"]
|
47
47
|
end
|
48
48
|
|
49
49
|
# @return [Map<String, Callback>]
|
50
50
|
def callbacks
|
51
|
-
|
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
|
-
|
11
|
+
self["name"]
|
12
12
|
end
|
13
13
|
|
14
14
|
# @return [String, nil]
|
15
15
|
def url
|
16
|
-
|
16
|
+
self["url"]
|
17
17
|
end
|
18
18
|
|
19
19
|
# @return [String, nil]
|
20
20
|
def email
|
21
|
-
|
21
|
+
self["email"]
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|