openapi3_parser 0.7.0 → 0.8.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/.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,
|