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.
- 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
|
[![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
|
-
|
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
|