openapi_parser 2.5.0 → 2.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/.github/workflows/ci.yaml +1 -3
- data/CHANGELOG.md +34 -0
- data/README.md +44 -1
- data/lib/openapi_parser/concerns/media_type_selectable.rb +18 -0
- data/lib/openapi_parser/concerns/schema_loader/creator.rb +7 -2
- data/lib/openapi_parser/concerns/schema_loader/hash_body_loader.rb +1 -1
- data/lib/openapi_parser/config.rb +5 -0
- data/lib/openapi_parser/errors.rb +24 -0
- data/lib/openapi_parser/schema_validator/any_of_validator.rb +1 -1
- data/lib/openapi_parser/schema_validator/base.rb +35 -9
- data/lib/openapi_parser/schema_validator/float_validator.rb +4 -0
- data/lib/openapi_parser/schema_validator/integer_validator.rb +17 -1
- data/lib/openapi_parser/schema_validator/multiple_of.rb +26 -0
- data/lib/openapi_parser/schema_validator/nil_validator.rb +4 -0
- data/lib/openapi_parser/schema_validator/one_of_validator.rb +1 -1
- data/lib/openapi_parser/schema_validator.rb +32 -4
- data/lib/openapi_parser/schemas/components.rb +4 -0
- data/lib/openapi_parser/schemas/discriminator.rb +4 -0
- data/lib/openapi_parser/schemas/media_type.rb +4 -0
- data/lib/openapi_parser/schemas/openapi.rb +36 -3
- data/lib/openapi_parser/schemas/path_item.rb +32 -6
- data/lib/openapi_parser/schemas/request_body.rb +1 -1
- data/lib/openapi_parser/schemas/response.rb +5 -1
- data/lib/openapi_parser/schemas/schema.rb +8 -1
- data/lib/openapi_parser/spec_validator/rule.rb +24 -3
- data/lib/openapi_parser/spec_validator/rules/additional_operations_before_32.rb +26 -0
- data/lib/openapi_parser/spec_validator/rules/allow_empty_value_deprecation.rb +26 -0
- data/lib/openapi_parser/spec_validator/rules/cookie_style_before_32.rb +26 -0
- data/lib/openapi_parser/spec_validator/rules/default_mapping_before_32.rb +29 -0
- data/lib/openapi_parser/spec_validator/rules/defs_in_30.rb +25 -0
- data/lib/openapi_parser/spec_validator/rules/example_value_fields_before_32.rb +48 -0
- data/lib/openapi_parser/spec_validator/rules/media_types_before_32.rb +24 -0
- data/lib/openapi_parser/spec_validator/rules/query_method_before_32.rb +26 -0
- data/lib/openapi_parser/spec_validator/rules/querystring_before_32.rb +26 -0
- data/lib/openapi_parser/spec_validator/rules/response_summary_before_32.rb +25 -0
- data/lib/openapi_parser/spec_validator/rules/security_scheme_fields_before_32.rb +41 -0
- data/lib/openapi_parser/spec_validator/rules/server_name_before_32.rb +35 -0
- data/lib/openapi_parser/spec_validator/rules/streaming_fields_before_32.rb +56 -0
- data/lib/openapi_parser/spec_validator/rules/tag_fields_before_32.rb +34 -0
- data/lib/openapi_parser/spec_validator/rules/xml_attribute_deprecation.rb +29 -0
- data/lib/openapi_parser/spec_validator/rules/xml_node_type_before_32.rb +29 -0
- data/lib/openapi_parser/spec_validator/rules/xml_wrapped_deprecation.rb +29 -0
- data/lib/openapi_parser/spec_validator.rb +34 -0
- data/lib/openapi_parser/version.rb +1 -1
- data/lib/openapi_parser.rb +5 -4
- data/sig/openapi_parser/config.rbs +1 -0
- data/sig/openapi_parser/schema_validator.rbs +1 -0
- data/sig/openapi_parser/schema_validators/base.rbs +4 -2
- data/sig/openapi_parser/spec_validator.rbs +77 -0
- data/sig/openapi_parser.rbs +5 -3
- data/sig/wip_types.rbs +6 -0
- metadata +19 -1
|
@@ -3,20 +3,46 @@
|
|
|
3
3
|
|
|
4
4
|
module OpenAPIParser::Schemas
|
|
5
5
|
class PathItem < Base
|
|
6
|
+
# `query` is an OpenAPI 3.2 addition
|
|
7
|
+
STANDARD_METHODS = %w[get put post delete options head patch trace query].freeze
|
|
8
|
+
PRE_3_2_METHODS = (STANDARD_METHODS - %w[query]).freeze
|
|
9
|
+
|
|
6
10
|
openapi_attr_values :summary, :description
|
|
7
11
|
|
|
8
|
-
openapi_attr_objects :get, :put, :post, :delete, :options, :head, :patch, :trace, Operation
|
|
12
|
+
openapi_attr_objects :get, :put, :post, :delete, :options, :head, :patch, :trace, :query, Operation
|
|
9
13
|
openapi_attr_list_object :parameters, Parameter, reference: true
|
|
10
14
|
|
|
11
|
-
#
|
|
15
|
+
# @!attribute [r] additional_operations
|
|
16
|
+
# @return [Hash{String => Operation}, nil] operations for non-standard HTTP methods (OpenAPI 3.2+)
|
|
17
|
+
openapi_attr_hash_object :additional_operations, Operation, reference: false, schema_key: :additionalOperations
|
|
18
|
+
|
|
19
|
+
# @return [Operation, nil]
|
|
12
20
|
def operation(method)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
# before 3.2: exact-case standard methods only, as it always was
|
|
22
|
+
unless root.use_3_2_features?
|
|
23
|
+
return PRE_3_2_METHODS.include?(method.to_s) ? public_send(method.to_s) : nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
method_name = method.to_s.downcase
|
|
27
|
+
return public_send(method_name) if STANDARD_METHODS.include?(method_name)
|
|
28
|
+
|
|
29
|
+
additional_operation(method.to_s)
|
|
16
30
|
end
|
|
17
31
|
|
|
18
32
|
def set_path_item_to_operation
|
|
19
|
-
|
|
33
|
+
STANDARD_METHODS.each { |method| operation(method)&.set_parent_path_item(self) }
|
|
34
|
+
additional_operations&.each_value { |op| op.set_parent_path_item(self) }
|
|
20
35
|
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# additionalOperations keys are method names as sent on the wire
|
|
40
|
+
# (conventionally uppercase); prefer an exact match, else ignore case
|
|
41
|
+
def additional_operation(name)
|
|
42
|
+
operations = additional_operations
|
|
43
|
+
return nil unless operations
|
|
44
|
+
|
|
45
|
+
operations.fetch(name) { operations.find { |key, _| key.casecmp?(name) }&.last }
|
|
46
|
+
end
|
|
21
47
|
end
|
|
22
48
|
end
|
|
@@ -12,7 +12,7 @@ module OpenAPIParser::Schemas
|
|
|
12
12
|
|
|
13
13
|
# @!attribute [r] content
|
|
14
14
|
# @return [Hash{String => MediaType}, nil] content type to MediaType object
|
|
15
|
-
openapi_attr_hash_object :content, MediaType, reference:
|
|
15
|
+
openapi_attr_hash_object :content, MediaType, reference: OpenAPIParser::MediaTypeSelectable::CONTENT_REFERENCE
|
|
16
16
|
|
|
17
17
|
# @param [String] content_type
|
|
18
18
|
# @param [Hash] params
|
|
@@ -6,9 +6,13 @@ module OpenAPIParser::Schemas
|
|
|
6
6
|
|
|
7
7
|
openapi_attr_values :description
|
|
8
8
|
|
|
9
|
+
# @!attribute [r] summary
|
|
10
|
+
# @return [String, nil] short summary of the response (OpenAPI 3.2+)
|
|
11
|
+
openapi_attr_value :summary
|
|
12
|
+
|
|
9
13
|
# @!attribute [r] content
|
|
10
14
|
# @return [Hash{String => MediaType}, nil] content_type to MediaType hash
|
|
11
|
-
openapi_attr_hash_object :content, MediaType, reference:
|
|
15
|
+
openapi_attr_hash_object :content, MediaType, reference: OpenAPIParser::MediaTypeSelectable::CONTENT_REFERENCE
|
|
12
16
|
|
|
13
17
|
# @!attribute [r] headers
|
|
14
18
|
# @return [Hash{String => Header}, nil] header string to Header
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# TODO: support 'not' because I need check reference...
|
|
2
1
|
# TODO: support 'xml', 'externalDocs'
|
|
3
2
|
# TODO: support extended property
|
|
4
3
|
|
|
@@ -96,6 +95,10 @@ module OpenAPIParser::Schemas
|
|
|
96
95
|
# @return [Array<Schema, Reference>, nil]
|
|
97
96
|
openapi_attr_list_object :any_of, Schema, reference: true, schema_key: :anyOf
|
|
98
97
|
|
|
98
|
+
# @!attribute [r] not
|
|
99
|
+
# @return [Schema, nil]
|
|
100
|
+
openapi_attr_object :not, Schema, reference: true
|
|
101
|
+
|
|
99
102
|
# @!attribute [r] items
|
|
100
103
|
# @return [Schema, nil]
|
|
101
104
|
openapi_attr_object :items, Schema, reference: true
|
|
@@ -112,6 +115,10 @@ module OpenAPIParser::Schemas
|
|
|
112
115
|
# @return [Hash{String => Schema}, nil]
|
|
113
116
|
openapi_attr_hash_object :properties, Schema, reference: true
|
|
114
117
|
|
|
118
|
+
# @!attribute [r] defs
|
|
119
|
+
# @return [Hash{String => Schema}, nil] reusable schemas for `$ref` (OpenAPI 3.1+)
|
|
120
|
+
openapi_attr_hash_object :defs, Schema, reference: true, schema_key: :$defs
|
|
121
|
+
|
|
115
122
|
# @!attribute [r] discriminator
|
|
116
123
|
# @return [Discriminator, nil]
|
|
117
124
|
openapi_attr_object :discriminator, Discriminator
|
|
@@ -46,12 +46,33 @@ module OpenAPIParser
|
|
|
46
46
|
def each_schema(root, &block)
|
|
47
47
|
return enum_for(:each_schema, root) unless block
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
each_node(root, OpenAPIParser::Schemas::Schema, &block)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# yields every parsed object reachable from root that is one of klasses
|
|
53
|
+
def each_node(root, *klasses)
|
|
54
|
+
walk(root, {}) do |node|
|
|
55
|
+
yield node if klasses.any? { |klass| node.is_a?(klass) }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# security schemes are not modeled by the parse layer, so rules
|
|
60
|
+
# about them read the raw `components.securitySchemes` map
|
|
61
|
+
def each_security_scheme(root)
|
|
62
|
+
components = root.raw_schema.is_a?(Hash) ? root.raw_schema['components'] : nil
|
|
63
|
+
schemes = components.is_a?(Hash) ? components['securitySchemes'] : nil
|
|
64
|
+
return unless schemes.is_a?(Hash)
|
|
65
|
+
|
|
66
|
+
schemes.each do |name, scheme|
|
|
67
|
+
yield(name, scheme) if scheme.is_a?(Hash)
|
|
52
68
|
end
|
|
53
69
|
end
|
|
54
70
|
|
|
71
|
+
# escapes a map key for use in a violation path, matching object_reference
|
|
72
|
+
def escape_reference(key)
|
|
73
|
+
key.to_s.gsub('~', '~0').gsub('/', '~1')
|
|
74
|
+
end
|
|
75
|
+
|
|
55
76
|
def walk(node, visited, &block)
|
|
56
77
|
return unless node.respond_to?(:_openapi_all_child_objects)
|
|
57
78
|
return if visited[node.object_id]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# `additionalOperations` is a 3.2 Path Item addition for non-standard
|
|
5
|
+
# HTTP methods. The parse layer accepts it permissively; this rule
|
|
6
|
+
# reports the version mismatch.
|
|
7
|
+
class AdditionalOperationsBefore32 < Rule
|
|
8
|
+
def check(root)
|
|
9
|
+
return [] unless version_before?('3.2')
|
|
10
|
+
|
|
11
|
+
violations = []
|
|
12
|
+
each_node(root, OpenAPIParser::Schemas::PathItem) do |node|
|
|
13
|
+
raw = node.raw_schema
|
|
14
|
+
next unless raw.is_a?(Hash) && raw.key?('additionalOperations')
|
|
15
|
+
|
|
16
|
+
violations << violation(
|
|
17
|
+
path: "#{node.object_reference}/additionalOperations",
|
|
18
|
+
message: '`additionalOperations` is a 3.2 Path Item addition; earlier documents have no such field',
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
violations
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# 3.2 deprecates `allowEmptyValue` on the Parameter Object (3.1 only
|
|
5
|
+
# called it NOT RECOMMENDED). The field is still allowed but
|
|
6
|
+
# discouraged, so we report it as a violation on 3.2 documents.
|
|
7
|
+
class AllowEmptyValueDeprecation < Rule
|
|
8
|
+
def check(root)
|
|
9
|
+
return [] unless version_at_least?('3.2')
|
|
10
|
+
|
|
11
|
+
violations = []
|
|
12
|
+
each_node(root, OpenAPIParser::Schemas::Parameter) do |node|
|
|
13
|
+
raw = node.raw_schema
|
|
14
|
+
next unless raw.is_a?(Hash) && raw.key?('allowEmptyValue')
|
|
15
|
+
|
|
16
|
+
violations << violation(
|
|
17
|
+
path: "#{node.object_reference}/allowEmptyValue",
|
|
18
|
+
message: '`allowEmptyValue` on a Parameter Object is deprecated in 3.2',
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
violations
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# `style: cookie` is a 3.2 Parameter style for cookie serialization.
|
|
5
|
+
# Runtime validation ignores `style`; this rule reports the version
|
|
6
|
+
# mismatch.
|
|
7
|
+
class CookieStyleBefore32 < Rule
|
|
8
|
+
def check(root)
|
|
9
|
+
return [] unless version_before?('3.2')
|
|
10
|
+
|
|
11
|
+
violations = []
|
|
12
|
+
each_node(root, OpenAPIParser::Schemas::Parameter) do |node|
|
|
13
|
+
raw = node.raw_schema
|
|
14
|
+
next unless raw.is_a?(Hash) && raw['style'] == 'cookie'
|
|
15
|
+
|
|
16
|
+
violations << violation(
|
|
17
|
+
path: "#{node.object_reference}/style",
|
|
18
|
+
message: '`style: cookie` is a 3.2 Parameter style; earlier documents have no such style',
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
violations
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# `defaultMapping` on the Discriminator Object is a 3.2 addition. The
|
|
5
|
+
# parse layer accepts it permissively; this rule reports the version
|
|
6
|
+
# mismatch.
|
|
7
|
+
class DefaultMappingBefore32 < Rule
|
|
8
|
+
def check(root)
|
|
9
|
+
return [] unless version_before?('3.2')
|
|
10
|
+
|
|
11
|
+
violations = []
|
|
12
|
+
each_schema(root) do |schema|
|
|
13
|
+
raw = schema.raw_schema
|
|
14
|
+
next unless raw.is_a?(Hash)
|
|
15
|
+
|
|
16
|
+
discriminator = raw['discriminator']
|
|
17
|
+
next unless discriminator.is_a?(Hash) && discriminator.key?('defaultMapping')
|
|
18
|
+
|
|
19
|
+
violations << violation(
|
|
20
|
+
path: "#{schema.object_reference}/discriminator/defaultMapping",
|
|
21
|
+
message: '`defaultMapping` on a Discriminator Object is a 3.2 addition; earlier documents have no such field',
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
violations
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# `$defs` is a JSON Schema 2020-12 keyword that 3.1 adopted. 3.0
|
|
5
|
+
# Schema Objects do not define it. The parse layer resolves `$ref`s
|
|
6
|
+
# into it regardless of version; this rule reports the version mismatch.
|
|
7
|
+
class DefsIn30 < Rule
|
|
8
|
+
def check(root)
|
|
9
|
+
return [] unless version_before?('3.1')
|
|
10
|
+
|
|
11
|
+
violations = []
|
|
12
|
+
each_schema(root) do |schema|
|
|
13
|
+
next unless schema.raw_schema.is_a?(Hash) && schema.raw_schema.key?('$defs')
|
|
14
|
+
|
|
15
|
+
violations << violation(
|
|
16
|
+
path: schema.object_reference,
|
|
17
|
+
message: '`$defs` is a 3.1 addition (from JSON Schema 2020-12); 3.0 Schema Objects do not define it',
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
violations
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# 3.2 adds `dataValue` and `serializedValue` to the Example Object.
|
|
5
|
+
# Example Objects are not modeled by the parse layer, so this rule
|
|
6
|
+
# inspects the raw `examples` maps on Parameter, Media Type, and
|
|
7
|
+
# Header objects, plus `components.examples`. Media Types under a
|
|
8
|
+
# Parameter or Header `content` and anything under callbacks are not
|
|
9
|
+
# modeled, so examples there are not reached.
|
|
10
|
+
class ExampleValueFieldsBefore32 < Rule
|
|
11
|
+
NEW_FIELDS = %w[dataValue serializedValue].freeze
|
|
12
|
+
|
|
13
|
+
def check(root)
|
|
14
|
+
return [] unless version_before?('3.2')
|
|
15
|
+
|
|
16
|
+
violations = []
|
|
17
|
+
node_classes = [
|
|
18
|
+
OpenAPIParser::Schemas::Parameter,
|
|
19
|
+
OpenAPIParser::Schemas::MediaType,
|
|
20
|
+
OpenAPIParser::Schemas::Header,
|
|
21
|
+
OpenAPIParser::Schemas::Components,
|
|
22
|
+
]
|
|
23
|
+
each_node(root, *node_classes) do |node|
|
|
24
|
+
raw = node.raw_schema
|
|
25
|
+
next unless raw.is_a?(Hash)
|
|
26
|
+
|
|
27
|
+
examples = raw['examples']
|
|
28
|
+
next unless examples.is_a?(Hash)
|
|
29
|
+
|
|
30
|
+
examples.each do |name, example|
|
|
31
|
+
next unless example.is_a?(Hash)
|
|
32
|
+
|
|
33
|
+
NEW_FIELDS.each do |field|
|
|
34
|
+
next unless example.key?(field)
|
|
35
|
+
|
|
36
|
+
violations << violation(
|
|
37
|
+
path: "#{node.object_reference}/examples/#{escape_reference(name)}/#{field}",
|
|
38
|
+
message: "`#{field}` on an Example Object is a 3.2 addition; earlier documents have no such field",
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
violations
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# `components.mediaTypes` is a 3.2 addition. The parse layer accepts
|
|
5
|
+
# it permissively; this rule reports the version mismatch.
|
|
6
|
+
class MediaTypesBefore32 < Rule
|
|
7
|
+
def check(root)
|
|
8
|
+
return [] unless version_before?('3.2')
|
|
9
|
+
|
|
10
|
+
components = root.components
|
|
11
|
+
return [] unless components
|
|
12
|
+
|
|
13
|
+
raw = components.raw_schema
|
|
14
|
+
return [] unless raw.is_a?(Hash) && raw.key?('mediaTypes')
|
|
15
|
+
|
|
16
|
+
[violation(
|
|
17
|
+
path: "#{components.object_reference}/mediaTypes",
|
|
18
|
+
message: '`components.mediaTypes` is a 3.2 addition; earlier documents should not declare it',
|
|
19
|
+
)]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# `query` is a 3.2 Path Item addition for the HTTP QUERY method. The
|
|
5
|
+
# parse layer accepts it permissively; this rule reports the version
|
|
6
|
+
# mismatch.
|
|
7
|
+
class QueryMethodBefore32 < Rule
|
|
8
|
+
def check(root)
|
|
9
|
+
return [] unless version_before?('3.2')
|
|
10
|
+
|
|
11
|
+
violations = []
|
|
12
|
+
each_node(root, OpenAPIParser::Schemas::PathItem) do |node|
|
|
13
|
+
raw = node.raw_schema
|
|
14
|
+
next unless raw.is_a?(Hash) && raw.key?('query')
|
|
15
|
+
|
|
16
|
+
violations << violation(
|
|
17
|
+
path: "#{node.object_reference}/query",
|
|
18
|
+
message: 'the `query` operation is a 3.2 Path Item addition; earlier documents have no such field',
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
violations
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# `in: querystring` is a 3.2 Parameter location that treats the whole
|
|
5
|
+
# query string as one value described by `content`. Runtime validation
|
|
6
|
+
# does not support it yet; this rule reports the version mismatch.
|
|
7
|
+
class QuerystringBefore32 < Rule
|
|
8
|
+
def check(root)
|
|
9
|
+
return [] unless version_before?('3.2')
|
|
10
|
+
|
|
11
|
+
violations = []
|
|
12
|
+
each_node(root, OpenAPIParser::Schemas::Parameter) do |node|
|
|
13
|
+
raw = node.raw_schema
|
|
14
|
+
next unless raw.is_a?(Hash) && raw['in'] == 'querystring'
|
|
15
|
+
|
|
16
|
+
violations << violation(
|
|
17
|
+
path: "#{node.object_reference}/in",
|
|
18
|
+
message: '`in: querystring` is a 3.2 Parameter location; earlier documents have no such location',
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
violations
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# 3.2 adds `summary` to the Response Object. The parse layer accepts it
|
|
5
|
+
# permissively; this rule reports the version mismatch.
|
|
6
|
+
class ResponseSummaryBefore32 < Rule
|
|
7
|
+
def check(root)
|
|
8
|
+
return [] unless version_before?('3.2')
|
|
9
|
+
|
|
10
|
+
violations = []
|
|
11
|
+
each_node(root, OpenAPIParser::Schemas::Response) do |node|
|
|
12
|
+
raw = node.raw_schema
|
|
13
|
+
next unless raw.is_a?(Hash) && raw.key?('summary')
|
|
14
|
+
|
|
15
|
+
violations << violation(
|
|
16
|
+
path: "#{node.object_reference}/summary",
|
|
17
|
+
message: '`summary` on a Response Object is a 3.2 addition; earlier documents have no such field',
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
violations
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# 3.2 adds `deprecated` and `oauth2MetadataUrl` to the Security Scheme
|
|
5
|
+
# Object and the `deviceAuthorization` OAuth flow. Security schemes are
|
|
6
|
+
# not modeled by the parse layer, so this rule inspects the raw
|
|
7
|
+
# `components.securitySchemes` map; schemes given as a `$ref` are not
|
|
8
|
+
# followed.
|
|
9
|
+
class SecuritySchemeFieldsBefore32 < Rule
|
|
10
|
+
NEW_FIELDS = %w[deprecated oauth2MetadataUrl].freeze
|
|
11
|
+
|
|
12
|
+
def check(root)
|
|
13
|
+
return [] unless version_before?('3.2')
|
|
14
|
+
|
|
15
|
+
violations = []
|
|
16
|
+
each_security_scheme(root) do |name, scheme|
|
|
17
|
+
base = "#/components/securitySchemes/#{escape_reference(name)}"
|
|
18
|
+
|
|
19
|
+
NEW_FIELDS.each do |field|
|
|
20
|
+
next unless scheme.key?(field)
|
|
21
|
+
|
|
22
|
+
violations << violation(
|
|
23
|
+
path: "#{base}/#{field}",
|
|
24
|
+
message: "`#{field}` on a Security Scheme Object is a 3.2 addition; earlier documents have no such field",
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
flows = scheme['flows']
|
|
29
|
+
next unless flows.is_a?(Hash) && flows.key?('deviceAuthorization')
|
|
30
|
+
|
|
31
|
+
violations << violation(
|
|
32
|
+
path: "#{base}/flows/deviceAuthorization",
|
|
33
|
+
message: 'the `deviceAuthorization` OAuth flow is a 3.2 addition; earlier documents have no such flow',
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
violations
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# 3.2 adds `name` to the Server Object. Servers are not modeled by
|
|
5
|
+
# the parse layer, so this rule inspects the raw `servers` arrays on
|
|
6
|
+
# the root, Path Item, and Operation objects. Servers under callbacks
|
|
7
|
+
# and Link Objects are not reached.
|
|
8
|
+
class ServerNameBefore32 < Rule
|
|
9
|
+
def check(root)
|
|
10
|
+
return [] unless version_before?('3.2')
|
|
11
|
+
|
|
12
|
+
violations = []
|
|
13
|
+
node_classes = [OpenAPIParser::Schemas::OpenAPI, OpenAPIParser::Schemas::PathItem, OpenAPIParser::Schemas::Operation]
|
|
14
|
+
each_node(root, *node_classes) do |node|
|
|
15
|
+
raw = node.raw_schema
|
|
16
|
+
next unless raw.is_a?(Hash)
|
|
17
|
+
|
|
18
|
+
servers = raw['servers']
|
|
19
|
+
next unless servers.is_a?(Array)
|
|
20
|
+
|
|
21
|
+
servers.each_with_index do |server, index|
|
|
22
|
+
next unless server.is_a?(Hash) && server.key?('name')
|
|
23
|
+
|
|
24
|
+
violations << violation(
|
|
25
|
+
path: "#{node.object_reference}/servers/#{index}/name",
|
|
26
|
+
message: '`name` on a Server Object is a 3.2 addition; earlier documents have no such field',
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
violations
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# 3.2 adds `itemSchema`, `itemEncoding`, and `prefixEncoding` to the
|
|
5
|
+
# Media Type Object for sequential media types (SSE, JSON Lines,
|
|
6
|
+
# JSON Sequences), and lets an Encoding Object nest `encoding`,
|
|
7
|
+
# `itemEncoding`, and `prefixEncoding`. The parse layer accepts
|
|
8
|
+
# `itemSchema` permissively; this rule reports the version mismatch.
|
|
9
|
+
# Media Types under a Parameter or Header `content` are not modeled,
|
|
10
|
+
# so fields there are not reached.
|
|
11
|
+
class StreamingFieldsBefore32 < Rule
|
|
12
|
+
NEW_FIELDS = %w[itemSchema itemEncoding prefixEncoding].freeze
|
|
13
|
+
NEW_ENCODING_FIELDS = %w[encoding itemEncoding prefixEncoding].freeze
|
|
14
|
+
|
|
15
|
+
def check(root)
|
|
16
|
+
return [] unless version_before?('3.2')
|
|
17
|
+
|
|
18
|
+
violations = []
|
|
19
|
+
each_node(root, OpenAPIParser::Schemas::MediaType) do |node|
|
|
20
|
+
raw = node.raw_schema
|
|
21
|
+
next unless raw.is_a?(Hash)
|
|
22
|
+
|
|
23
|
+
NEW_FIELDS.each do |field|
|
|
24
|
+
next unless raw.key?(field)
|
|
25
|
+
|
|
26
|
+
violations << violation(
|
|
27
|
+
path: "#{node.object_reference}/#{field}",
|
|
28
|
+
message: "`#{field}` on a Media Type Object is a 3.2 addition; earlier documents have no such field",
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
violations.concat(encoding_violations(node, raw['encoding']))
|
|
33
|
+
end
|
|
34
|
+
violations
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def encoding_violations(node, encodings)
|
|
40
|
+
return [] unless encodings.is_a?(Hash)
|
|
41
|
+
|
|
42
|
+
encodings.flat_map do |property, encoding|
|
|
43
|
+
next [] unless encoding.is_a?(Hash)
|
|
44
|
+
|
|
45
|
+
NEW_ENCODING_FIELDS.select { |field| encoding.key?(field) }.map do |field|
|
|
46
|
+
violation(
|
|
47
|
+
path: "#{node.object_reference}/encoding/#{escape_reference(property)}/#{field}",
|
|
48
|
+
message: "`#{field}` on an Encoding Object is a 3.2 addition; earlier documents have no such field",
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# 3.2 adds `summary`, `parent`, and `kind` to the Tag Object.
|
|
5
|
+
# Tags are not modeled by the parse layer, so this rule inspects the
|
|
6
|
+
# raw root-level `tags` array.
|
|
7
|
+
class TagFieldsBefore32 < Rule
|
|
8
|
+
NEW_FIELDS = %w[summary parent kind].freeze
|
|
9
|
+
|
|
10
|
+
def check(root)
|
|
11
|
+
return [] unless version_before?('3.2')
|
|
12
|
+
|
|
13
|
+
tags = root.raw_schema.is_a?(Hash) ? root.raw_schema['tags'] : nil
|
|
14
|
+
return [] unless tags.is_a?(Array)
|
|
15
|
+
|
|
16
|
+
violations = []
|
|
17
|
+
tags.each_with_index do |tag, index|
|
|
18
|
+
next unless tag.is_a?(Hash)
|
|
19
|
+
|
|
20
|
+
NEW_FIELDS.each do |field|
|
|
21
|
+
next unless tag.key?(field)
|
|
22
|
+
|
|
23
|
+
violations << violation(
|
|
24
|
+
path: "#/tags/#{index}/#{field}",
|
|
25
|
+
message: "`#{field}` on a Tag Object is a 3.2 addition; earlier documents have no such field",
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
violations
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# 3.2 deprecates `attribute: true` on the XML Object in favor of
|
|
5
|
+
# `nodeType: attribute`. The field is still allowed but discouraged,
|
|
6
|
+
# so we report it as a violation on 3.2 documents.
|
|
7
|
+
class XmlAttributeDeprecation < Rule
|
|
8
|
+
def check(root)
|
|
9
|
+
return [] unless version_at_least?('3.2')
|
|
10
|
+
|
|
11
|
+
violations = []
|
|
12
|
+
each_schema(root) do |schema|
|
|
13
|
+
raw = schema.raw_schema
|
|
14
|
+
next unless raw.is_a?(Hash)
|
|
15
|
+
|
|
16
|
+
xml = raw['xml']
|
|
17
|
+
next unless xml.is_a?(Hash) && xml['attribute'] == true
|
|
18
|
+
|
|
19
|
+
violations << violation(
|
|
20
|
+
path: schema.object_reference,
|
|
21
|
+
message: '`attribute` on an XML Object is deprecated in 3.2; use `nodeType: attribute`',
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
violations
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module OpenAPIParser
|
|
2
|
+
class SpecValidator
|
|
3
|
+
module Rules
|
|
4
|
+
# 3.2 adds `nodeType` to the XML Object (replacing `attribute` and
|
|
5
|
+
# `wrapped`). XML Objects are not modeled by the parse layer, so this
|
|
6
|
+
# rule inspects the raw `xml` field on schemas.
|
|
7
|
+
class XmlNodeTypeBefore32 < Rule
|
|
8
|
+
def check(root)
|
|
9
|
+
return [] unless version_before?('3.2')
|
|
10
|
+
|
|
11
|
+
violations = []
|
|
12
|
+
each_schema(root) do |schema|
|
|
13
|
+
raw = schema.raw_schema
|
|
14
|
+
next unless raw.is_a?(Hash)
|
|
15
|
+
|
|
16
|
+
xml = raw['xml']
|
|
17
|
+
next unless xml.is_a?(Hash) && xml.key?('nodeType')
|
|
18
|
+
|
|
19
|
+
violations << violation(
|
|
20
|
+
path: schema.object_reference,
|
|
21
|
+
message: '`nodeType` on an XML Object is a 3.2 addition; earlier documents have no such field',
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
violations
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|