openapi3_parser 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +2 -4
- data/.travis.yml +11 -0
- data/CHANGELOG.md +13 -0
- data/README.md +4 -1
- data/TODO.md +1 -1
- data/lib/openapi3_parser.rb +2 -3
- data/lib/openapi3_parser/array_sentence.rb +1 -0
- data/lib/openapi3_parser/document.rb +2 -1
- data/lib/openapi3_parser/node/array.rb +1 -1
- data/lib/openapi3_parser/node/context.rb +23 -0
- data/lib/openapi3_parser/node/map.rb +1 -1
- data/lib/openapi3_parser/node/object.rb +1 -0
- data/lib/openapi3_parser/node/operation.rb +8 -0
- data/lib/openapi3_parser/node/path_item.rb +8 -0
- data/lib/openapi3_parser/node/placeholder.rb +8 -5
- data/lib/openapi3_parser/node/schema.rb +3 -2
- data/lib/openapi3_parser/node_factory.rb +1 -1
- data/lib/openapi3_parser/node_factory/array.rb +34 -26
- data/lib/openapi3_parser/node_factory/context.rb +9 -2
- data/lib/openapi3_parser/node_factory/discriminator.rb +2 -0
- data/lib/openapi3_parser/node_factory/field.rb +2 -0
- data/lib/openapi3_parser/node_factory/fields/reference.rb +1 -0
- data/lib/openapi3_parser/node_factory/map.rb +6 -4
- data/lib/openapi3_parser/node_factory/media_type.rb +1 -0
- data/lib/openapi3_parser/node_factory/object.rb +4 -1
- data/lib/openapi3_parser/node_factory/object_factory/dsl.rb +1 -1
- data/lib/openapi3_parser/node_factory/object_factory/field_config.rb +1 -0
- data/lib/openapi3_parser/node_factory/object_factory/node_builder.rb +1 -0
- data/lib/openapi3_parser/node_factory/object_factory/validator.rb +6 -4
- data/lib/openapi3_parser/node_factory/openapi.rb +2 -0
- data/lib/openapi3_parser/node_factory/operation.rb +11 -0
- data/lib/openapi3_parser/node_factory/parameter.rb +2 -0
- data/lib/openapi3_parser/node_factory/parameter_like.rb +1 -0
- data/lib/openapi3_parser/node_factory/path_item.rb +27 -5
- data/lib/openapi3_parser/node_factory/paths.rb +1 -1
- data/lib/openapi3_parser/node_factory/response.rb +1 -0
- data/lib/openapi3_parser/node_factory/responses.rb +1 -1
- data/lib/openapi3_parser/node_factory/schema.rb +3 -0
- data/lib/openapi3_parser/node_factory/server_variable.rb +1 -0
- data/lib/openapi3_parser/node_factory/type_checker.rb +6 -0
- data/lib/openapi3_parser/source.rb +2 -0
- data/lib/openapi3_parser/source/location.rb +6 -0
- data/lib/openapi3_parser/source/pointer.rb +5 -0
- data/lib/openapi3_parser/source_input.rb +2 -0
- data/lib/openapi3_parser/source_input/file.rb +2 -0
- data/lib/openapi3_parser/source_input/raw.rb +3 -0
- data/lib/openapi3_parser/source_input/resolve_next.rb +1 -0
- data/lib/openapi3_parser/source_input/string_parser.rb +3 -2
- data/lib/openapi3_parser/source_input/url.rb +2 -0
- data/lib/openapi3_parser/validation/error.rb +2 -0
- data/lib/openapi3_parser/validators/component_keys.rb +1 -1
- data/lib/openapi3_parser/validators/duplicate_parameters.rb +1 -0
- data/lib/openapi3_parser/validators/email.rb +1 -1
- data/lib/openapi3_parser/validators/media_type.rb +1 -1
- data/lib/openapi3_parser/validators/mutually_exclusive_fields.rb +2 -2
- data/lib/openapi3_parser/validators/reference.rb +2 -0
- data/lib/openapi3_parser/validators/required_fields.rb +2 -2
- data/lib/openapi3_parser/validators/unexpected_fields.rb +3 -2
- data/lib/openapi3_parser/version.rb +1 -1
- data/openapi3_parser.gemspec +9 -7
- metadata +44 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 305a84267a984ae06f488b48ed4d9f385fddfccb
|
4
|
+
data.tar.gz: 51e8ed34fcc0b665658a4ff08d3ac51c2755f9bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c77b4149bba7eac8bb043b3c276325a4dc7de5605481e60a63fba01b0476c9f9fcc799ced7eda3bbbdc28cff955a64b578e48633d7f0ba7b0f3cbb659320ce58
|
7
|
+
data.tar.gz: 3dbe3f4d0235e8308cbf28aa4ce79e725bd6071b28f0e087e222d8f6a91cb618e8ea91c46bde703f393baf5c09e5bb3f7ed23741b3ae07275b6aed7f9a60b655
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -4,12 +4,10 @@ Metrics/MethodLength:
|
|
4
4
|
Max: 30
|
5
5
|
Metrics/AbcSize:
|
6
6
|
Max: 30
|
7
|
-
Documentation:
|
7
|
+
Style/Documentation:
|
8
8
|
Enabled: false
|
9
9
|
Metrics/BlockLength:
|
10
10
|
Exclude:
|
11
11
|
- 'spec/**/*.rb'
|
12
|
-
|
13
|
-
Enabled: false
|
14
|
-
Naming/UncommunicativeBlockParamName:
|
12
|
+
Style/BracesAroundHashParameters:
|
15
13
|
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,11 +1,22 @@
|
|
1
1
|
language: ruby
|
2
2
|
sudo: false
|
3
|
+
env:
|
4
|
+
global:
|
5
|
+
CC_TEST_REPORTER_ID=8bc2d8e54331569aeb442094c21cb64a58d6efa0670f65ff00d9ae887f63c0b4
|
6
|
+
CC_RUBY_VERSION=ruby-2.4.6
|
3
7
|
rvm:
|
4
8
|
- 2.4.6
|
5
9
|
- 2.5.5
|
6
10
|
- 2.6.4
|
11
|
+
- 2.7.0
|
7
12
|
- ruby-head
|
8
13
|
matrix:
|
9
14
|
allow_failures:
|
10
15
|
- rvm: ruby-head
|
11
16
|
fast_finish: true
|
17
|
+
before_script:
|
18
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
19
|
+
- chmod +x ./cc-test-reporter
|
20
|
+
- if [ $(rvm current) = $CC_RUBY_VERSION ]; then ./cc-test-reporter before-build; fi
|
21
|
+
after_script:
|
22
|
+
- if [ $(rvm current) = $CC_RUBY_VERSION ]; then ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT; fi
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# 0.8.0
|
2
|
+
|
3
|
+
- Resolve deprecation warnings for Ruby 2.7
|
4
|
+
- Resolve deprecation warnings for Psych
|
5
|
+
- Operation and Path Item objects use servers that have cascaded from parent
|
6
|
+
objects if they do not have their own servers defined.
|
7
|
+
- Add `#relative_node` and `#parent_node` methods to `Node::Context`.
|
8
|
+
- Default to a server of "/" when given an empty or null servers input for
|
9
|
+
OpenAPI node.
|
10
|
+
- Set referenced data as input and source location for a PathItem with only
|
11
|
+
a $ref value.
|
12
|
+
- Fix data being lost in PathItem reference merges.
|
13
|
+
|
1
14
|
# 0.7.0
|
2
15
|
|
3
16
|
- Add `#values` method to `Node::Object` and `Node#Map` to have a method that
|
data/README.md
CHANGED
@@ -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
|
|
@@ -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.
|
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
|
-
- [
|
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
|
data/lib/openapi3_parser.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
Dir.glob(File.join(__dir__, "openapi3_parser", "**", "*.rb")).
|
4
|
-
|
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
|
@@ -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
|
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)
|
@@ -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
|
@@ -133,6 +133,29 @@ module Openapi3Parser
|
|
133
133
|
def node
|
134
134
|
document.node_at(document_location.pointer)
|
135
135
|
end
|
136
|
+
|
137
|
+
# Look up a node at a particular location in the OpenAPI docuemnt based
|
138
|
+
# on the relative position in the document of this context
|
139
|
+
#
|
140
|
+
# Examples:
|
141
|
+
#
|
142
|
+
# context.relative_node("#schemas")
|
143
|
+
# context.relative_node(%w[..])
|
144
|
+
#
|
145
|
+
# @param [Source::Pointer, String, Array] pointer
|
146
|
+
# @return anything
|
147
|
+
def relative_node(pointer)
|
148
|
+
document.node_at(pointer, document_location.pointer)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Return the node that is the parent node for the node at this context
|
152
|
+
#
|
153
|
+
# @return [Node::Object, Node::Map, Node::Array, nil]
|
154
|
+
def parent_node
|
155
|
+
return if document_location.root?
|
156
|
+
|
157
|
+
relative_node("#..")
|
158
|
+
end
|
136
159
|
end
|
137
160
|
end
|
138
161
|
end
|
@@ -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
|
@@ -66,6 +66,14 @@ module Openapi3Parser
|
|
66
66
|
self["servers"]
|
67
67
|
end
|
68
68
|
|
69
|
+
# Whether this object uses it's own defined servers instead of falling
|
70
|
+
# back to the root ones.
|
71
|
+
#
|
72
|
+
# @return [Boolean]
|
73
|
+
def alternative_servers?
|
74
|
+
servers != node_context.document.root.servers
|
75
|
+
end
|
76
|
+
|
69
77
|
# @return [Node::Array<Parameter>]
|
70
78
|
def parameters
|
71
79
|
self["parameters"]
|
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "forwardable"
|
4
|
+
|
3
5
|
module Openapi3Parser
|
4
6
|
module Node
|
5
7
|
class Placeholder
|
8
|
+
extend Forwardable
|
9
|
+
|
6
10
|
def self.resolve(potential_placeholder)
|
7
11
|
if potential_placeholder.is_a?(Placeholder)
|
8
12
|
potential_placeholder.node
|
@@ -13,7 +17,7 @@ module Openapi3Parser
|
|
13
17
|
|
14
18
|
# Used to iterate through hashes or arrays that may contain
|
15
19
|
# Placeholder objects where these are resolved to being nodes
|
16
|
-
#
|
20
|
+
# before iteration
|
17
21
|
def self.each(node_data, &block)
|
18
22
|
resolved =
|
19
23
|
if node_data.respond_to?(:keys)
|
@@ -27,6 +31,9 @@ module Openapi3Parser
|
|
27
31
|
resolved.each(&block)
|
28
32
|
end
|
29
33
|
|
34
|
+
attr_reader :node_factory, :field, :parent_context
|
35
|
+
def_delegators :node_factory, :nil_input?
|
36
|
+
|
30
37
|
def initialize(node_factory, field, parent_context)
|
31
38
|
@node_factory = node_factory
|
32
39
|
@field = field
|
@@ -41,10 +48,6 @@ module Openapi3Parser
|
|
41
48
|
node_factory.node(node_context)
|
42
49
|
end
|
43
50
|
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
attr_reader :node_factory, :field, :parent_context
|
48
51
|
end
|
49
52
|
end
|
50
53
|
end
|
@@ -5,7 +5,7 @@ require "openapi3_parser/node/object"
|
|
5
5
|
module Openapi3Parser
|
6
6
|
module Node
|
7
7
|
# @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaObject
|
8
|
-
# rubocop:disable ClassLength
|
8
|
+
# rubocop:disable Metrics/ClassLength
|
9
9
|
class Schema < Node::Object
|
10
10
|
# This is used to provide a name for the schema based on it's position in
|
11
11
|
# an OpenAPI document.
|
@@ -177,6 +177,7 @@ module Openapi3Parser
|
|
177
177
|
def additional_properties_schema
|
178
178
|
properties = self["additionalProperties"]
|
179
179
|
return if [true, false].include?(properties)
|
180
|
+
|
180
181
|
properties
|
181
182
|
end
|
182
183
|
|
@@ -240,6 +241,6 @@ module Openapi3Parser
|
|
240
241
|
self["deprecated"]
|
241
242
|
end
|
242
243
|
end
|
243
|
-
# rubocop:enable ClassLength
|
244
|
+
# rubocop:enable Metrics/ClassLength
|
244
245
|
end
|
245
246
|
end
|
@@ -3,23 +3,27 @@
|
|
3
3
|
module Openapi3Parser
|
4
4
|
module NodeFactory
|
5
5
|
class Array
|
6
|
-
attr_reader :context, :data, :default, :
|
7
|
-
:value_factory, :validation
|
6
|
+
attr_reader :context, :data, :default, :use_default_on_empty,
|
7
|
+
:value_input_type, :value_factory, :validation
|
8
8
|
|
9
|
+
# rubocop:disable Metrics/ParameterLists
|
9
10
|
def initialize(
|
10
11
|
context,
|
11
12
|
default: [],
|
13
|
+
use_default_on_empty: false,
|
12
14
|
value_input_type: nil,
|
13
15
|
value_factory: nil,
|
14
16
|
validate: nil
|
15
17
|
)
|
16
18
|
@context = context
|
17
19
|
@default = default
|
20
|
+
@use_default_on_empty = use_default_on_empty
|
18
21
|
@value_input_type = value_input_type
|
19
22
|
@value_factory = value_factory
|
20
23
|
@validation = validate
|
21
24
|
@data = build_data(context.input)
|
22
25
|
end
|
26
|
+
# rubocop:enable Metrics/ParameterLists
|
23
27
|
|
24
28
|
def raw_input
|
25
29
|
context.input
|
@@ -50,18 +54,25 @@ module Openapi3Parser
|
|
50
54
|
%{#{self.class.name}(#{context.source_location.inspect})}
|
51
55
|
end
|
52
56
|
|
57
|
+
def use_default?
|
58
|
+
return true if nil_input? || !raw_input.is_a?(::Array)
|
59
|
+
return false unless use_default_on_empty
|
60
|
+
|
61
|
+
raw_input.empty?
|
62
|
+
end
|
63
|
+
|
53
64
|
private
|
54
65
|
|
55
66
|
def build_data(raw_input)
|
56
|
-
|
57
|
-
|
58
|
-
process_data(use_default ? default : raw_input)
|
67
|
+
return if use_default? && default.nil?
|
68
|
+
|
69
|
+
process_data(use_default? ? default : raw_input)
|
59
70
|
end
|
60
71
|
|
61
72
|
def process_data(data)
|
62
73
|
data.each_with_index.map do |value, i|
|
63
74
|
if value_factory
|
64
|
-
initialize_value_factory(Context.next_field(context, i))
|
75
|
+
initialize_value_factory(Context.next_field(context, i, value))
|
65
76
|
else
|
66
77
|
value
|
67
78
|
end
|
@@ -104,26 +115,24 @@ module Openapi3Parser
|
|
104
115
|
|
105
116
|
def errors
|
106
117
|
return validatable.collection if factory.nil_input?
|
118
|
+
|
107
119
|
TypeChecker.validate_type(validatable, type: ::Array)
|
108
120
|
return validatable.collection if validatable.errors.any?
|
121
|
+
|
109
122
|
collate_errors
|
110
123
|
validatable.collection
|
111
124
|
end
|
112
125
|
|
113
126
|
def data(parent_context)
|
114
|
-
|
127
|
+
if factory.use_default?
|
128
|
+
return factory.default.nil? ? nil : build_node_data(parent_context)
|
129
|
+
end
|
115
130
|
|
116
131
|
TypeChecker.raise_on_invalid_type(factory.context, type: ::Array)
|
117
132
|
check_values(raise_on_invalid: true)
|
118
133
|
validate(raise_on_invalid: true)
|
119
134
|
|
120
|
-
|
121
|
-
if value.respond_to?(:node)
|
122
|
-
Node::Placeholder.new(value, i, parent_context)
|
123
|
-
else
|
124
|
-
value
|
125
|
-
end
|
126
|
-
end
|
135
|
+
build_node_data(parent_context)
|
127
136
|
end
|
128
137
|
|
129
138
|
private_class_method :new
|
@@ -132,6 +141,14 @@ module Openapi3Parser
|
|
132
141
|
|
133
142
|
attr_reader :factory, :validatable
|
134
143
|
|
144
|
+
def build_node_data(parent_context)
|
145
|
+
factory.data.each_with_index.map do |value, i|
|
146
|
+
next value unless value.respond_to?(:node)
|
147
|
+
|
148
|
+
Node::Placeholder.new(value, i, parent_context)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
135
152
|
def collate_errors
|
136
153
|
check_values(raise_on_invalid: false)
|
137
154
|
validate(raise_on_invalid: false)
|
@@ -141,21 +158,12 @@ module Openapi3Parser
|
|
141
158
|
end
|
142
159
|
end
|
143
160
|
|
144
|
-
def default_value
|
145
|
-
if factory.nil_input? && factory.default.nil?
|
146
|
-
nil
|
147
|
-
else
|
148
|
-
factory.data
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
161
|
def check_values(raise_on_invalid: false)
|
153
162
|
return unless factory.value_input_type
|
154
163
|
|
155
|
-
factory.context.input.
|
156
|
-
check_field_type(
|
157
|
-
|
158
|
-
)
|
164
|
+
factory.context.input.each_with_index do |value, index|
|
165
|
+
check_field_type(Context.next_field(factory.context, index, value),
|
166
|
+
raise_on_invalid)
|
159
167
|
end
|
160
168
|
end
|
161
169
|
|
@@ -12,6 +12,8 @@ module Openapi3Parser
|
|
12
12
|
# @attr_reader [Array<Source::Location>] refernce_locations
|
13
13
|
#
|
14
14
|
class Context
|
15
|
+
UNDEFINED = Class.new
|
16
|
+
|
15
17
|
# Create a context for the root of a document
|
16
18
|
#
|
17
19
|
# @param [Any] input
|
@@ -29,10 +31,15 @@ module Openapi3Parser
|
|
29
31
|
#
|
30
32
|
# @param [Context] parent_context
|
31
33
|
# @param [String] field
|
34
|
+
# @param [Any] input
|
32
35
|
# @return [Context]
|
33
|
-
def self.next_field(parent_context, field)
|
36
|
+
def self.next_field(parent_context, field, given_input = UNDEFINED)
|
34
37
|
pc = parent_context
|
35
|
-
input =
|
38
|
+
input = if given_input == UNDEFINED
|
39
|
+
pc.input.respond_to?(:[]) ? pc.input[field] : nil
|
40
|
+
else
|
41
|
+
given_input
|
42
|
+
end
|
36
43
|
source_location = Source::Location.next_field(pc.source_location, field)
|
37
44
|
new(input,
|
38
45
|
source_location: source_location,
|